diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 605b14091c4..00000000000 --- a/.babelrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "presets": ["es2015", "flow-vue"], - "plugins": ["transform-vue-jsx", "syntax-dynamic-import"], - "ignore": [ - "dist/*.js", - "packages/**/*.js" - ] -} diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 0c5f49e158d..00000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,81 +0,0 @@ -version: 2 - -defaults: &defaults - working_directory: ~/project/vue - docker: - - image: circleci/node:6-browsers - -jobs: - install: - <<: *defaults - steps: - - checkout - - restore_cache: - keys: - - v1-vue-{{ .Branch }}-{{ checksum "package-lock.json" }} - - v1-vue-{{ .Branch }}- - - v1-vue- - - run: npm install - - save_cache: - key: v1-vue-{{ .Branch }}-{{ checksum "package-lock.json" }} - paths: - - node_modules/ - - persist_to_workspace: - root: ~/project - paths: - - vue - - lint-flow-types: - <<: *defaults - steps: - - attach_workspace: - at: ~/project - - run: npm run lint - - run: npm run flow - - run: npm run test:types - - test-cover: - <<: *defaults - steps: - - attach_workspace: - at: ~/project - - run: npm run test:cover - - run: - name: report coverage stats for non-PRs - command: | - if [[ -z $CI_PULL_REQUEST ]]; then - cat ./coverage/lcov.info | ./node_modules/.bin/codecov - fi - - test-e2e: - <<: *defaults - steps: - - attach_workspace: - at: ~/project - - run: npm run test:e2e -- --env phantomjs - - test-ssr-weex: - <<: *defaults - steps: - - attach_workspace: - at: ~/project - - run: npm run test:ssr - - run: npm run test:weex - -workflows: - version: 2 - install-and-parallel-test: - jobs: - - install - - test-cover: - requires: - - install - - lint-flow-types: - requires: - - install - - test-e2e: - requires: - - install - - test-ssr-weex: - requires: - - install diff --git a/.editorconfig b/.editorconfig index f1cc3ad329c..01a20f16fe3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# http://editorconfig.org +# https://editorconfig.org root = true diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 7bf90e20366..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -flow -dist -packages diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 523d283e8a4..00000000000 --- a/.eslintrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "root": true, - "plugins": [ - "flowtype" - ], - "extends": [ - "plugin:vue-libs/recommended", - "plugin:flowtype/recommended" - ], - "globals": { - "__WEEX__": true - } -} diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index b070e08fddb..00000000000 --- a/.flowconfig +++ /dev/null @@ -1,23 +0,0 @@ -[ignore] -.*/node_modules/.* -.*/test/.* -.*/build/.* -.*/examples/.* -.*/benchmarks/.* - -[include] - -[libs] -flow - -[options] -unsafe.enable_getters_and_setters=true -module.name_mapper='^compiler/\(.*\)$' -> '<PROJECT_ROOT>/src/compiler/\1' -module.name_mapper='^core/\(.*\)$' -> '<PROJECT_ROOT>/src/core/\1' -module.name_mapper='^shared/\(.*\)$' -> '<PROJECT_ROOT>/src/shared/\1' -module.name_mapper='^web/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/web/\1' -module.name_mapper='^weex/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/weex/\1' -module.name_mapper='^server/\(.*\)$' -> '<PROJECT_ROOT>/src/server/\1' -module.name_mapper='^entries/\(.*\)$' -> '<PROJECT_ROOT>/src/entries/\1' -module.name_mapper='^sfc/\(.*\)$' -> '<PROJECT_ROOT>/src/sfc/\1' -suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000000..53dbf616c0a --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,4 @@ +# chore: move to typescript +af9fc2bcff31d5baa413039818a9b3e011deccaf +# workflow: remove eslint, apply prettier +72aed6a149b94b5b929fb47370a7a6d4cb7491c5 diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 4ead0e1daf2..82effcdc869 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -2,7 +2,7 @@ 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 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 the 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 +10,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](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/) +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/) diff --git a/.github/COMMIT_CONVENTION.md b/.github/COMMIT_CONVENTION.md index 59e91abe7a8..381bf17baab 100644 --- a/.github/COMMIT_CONVENTION.md +++ b/.github/COMMIT_CONVENTION.md @@ -1,6 +1,14 @@ ## Git Commit Message Convention -> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-angular/convention.md). +> 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 @@ -50,24 +58,24 @@ 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. +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. +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 place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc... +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 succinct description of the change: +The subject contains a succinct description of the change: * use the imperative, present tense: "change" not "changed" nor "changes" -* don't capitalize first letter +* don't capitalize the first letter * no dot (.) at the end ### Body diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4f140719553..5a00cc5a319 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ # 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. +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) @@ -14,92 +14,96 @@ Hi! I’m really excited that you are interested in contributing to Vue.js. Befo ## Pull Request Guidelines -- The `master` branch is basically just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.** +- 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 - we will let GitHub automatically squash it before merging. +- 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 new feature: +- If adding a 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. + - 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 a bug: - - If you are resolving a special issue, add `(fix #xxxx[,#xxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`. - - Provide detailed description of the bug in the PR. Live demo preferred. +- 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](http://nodejs.org) **version 6+** and [Java Runtime Environment](http://www.oracle.com/technetwork/java/javase/downloads/index.html) (needed for running Selenium server during e2e tests). +You will need [Node.js](https://nodejs.org) **version 18+** and [pnpm](https://pnpm.io/) **version 8+**. After cloning the repo, run: -``` bash -$ npm install -& npm run setup +```bash +$ pnpm i # install the dependencies of the project ``` -The `setup` script links two git hooks: - -- `pre-commit`: runs ESLint on staged files. -- `commit-msg`: validates commit message format (see below). - ### Committing Changes -Commit messages should follow the [commit message convention](./COMMIT_CONVENTION.md) so that changelogs can be automatically generated. If git hooks have been properly linked, commit messages will be automatically validated upon commit. It is recommended to use `npm run commit` instead of `git commit`, which provides an interactive CLI for generating proper commit messages. +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 +```bash # watch and auto re-build dist/vue.js $ npm run dev -# watch and auto re-run unit tests in Chrome -$ npm run dev:test +# 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, include linting / type checking +# 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 beforehand. +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 -- **`build`**: contains build-related configuration files. In most cases you don't need to touch them. However, it would be helpful to familiarize yourself with the following files: +- **`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: - - `build/alias.js`: module import aliases used across all source code and tests. + - `scripts/alias.js`: module import aliases used across all source code and tests. - - `build/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. + - `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. -- **`flow`**: contains type declarations for [Flow](https://flowtype.org/). These declarations are loaded **globally** and you will see them used in type annotations in normal source code. +- **`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`**: -- **`packages`**: contains `vue-server-renderer` and `vue-template-compiler`, which 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. + - `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, obviously. The codebase is written in ES2015 with [Flow](https://flowtype.org/) type annotations. +- **`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 the 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. + 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 - which means code inside `core` should be able to run in any JavaScript environment, be it the browser, Node.js, or an embedded JavaScript runtime in native applications. + 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. @@ -107,9 +111,9 @@ The default test script will do the following: lint with ESLint -> type check wi - **`instance`**: contains Vue instance constructor and prototype methods. - - **`global-api`**: as the name suggests. + - **`global-api`**: contains Vue global api. - - **`components`**: universal abstract components. Currently `keep-alive` is the only one. + - **`components`**: contains universal abstract components. - **`server`**: contains code related to server-side rendering. @@ -117,30 +121,20 @@ The default test script will do the following: lint with ESLint -> type check wi 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 then 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 `entries/web-runtime.js` and used to create the browser-specific vdom patching function. + 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 TypeScript type definitions - - - **`test`**: type definitions tests - + - **`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 Patreon or OpenCollective. - -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou) -- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs) - -### What's the difference between Patreon and OpenCollective? - -Funds donated via Patreon goes directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses by core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. +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 vuejs! +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 new file mode 100644 index 00000000000..652c1192b7b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,8 @@ +# 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.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index cb7f961992c..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,16 +0,0 @@ -<!-- -IMPORTANT: Please use the following link to create a new issue: - - https://new-issue.vuejs.org/ - -If your issue was not created using the app above, it will be closed immediately. - -中文用户请注意: -请使用上面的链接来创建新的 issue。如果不是用上述工具创建的 issue 会被自动关闭。 ---> - -<!-- -Love vuejs? Please consider supporting us via Patreon or OpenCollective: -👉 https://www.patreon.com/evanyou -👉 https://opencollective.com/vuejs/donate ---> diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..2d3d0ba085e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +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 index 7bda0561856..21f32de041a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -24,7 +24,7 @@ If yes, please describe the impact and migration path for existing applications: **The PR fulfills these requirements:** -- [ ] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch +- [ ] 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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..9a40dbcb701 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,93 @@ +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 new file mode 100644 index 00000000000..b698513080d --- /dev/null +++ b/.github/workflows/release-tag.yml @@ -0,0 +1,23 @@ +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 370a6eddbc0..18c343efc53 100644 --- a/.gitignore +++ b/.gitignore @@ -3,17 +3,15 @@ node_modules *.log explorations TODOs.md -dist/*.gz -dist/*.map -dist/vue.common.min.js -test/e2e/reports -test/e2e/screenshots -coverage RELEASE_NOTE*.md -dist/*.js -packages/vue-server-renderer/basic.js -packages/vue-server-renderer/build.js -packages/vue-server-renderer/server-plugin.js -packages/vue-server-renderer/client-plugin.js -packages/vue-template-compiler/build.js +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 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..ef93d94821a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +semi: false +singleQuote: true +printWidth: 80 +trailingComma: 'none' +arrowParens: 'avoid' diff --git a/BACKERS.md b/BACKERS.md index f11656a8e31..fa66d206698 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -1,489 +1,9 @@ <h1 align="center">Sponsors & Backers</h1> -Vue.js is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider: - -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou). -- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs). - -#### What's the difference between Patreon and OpenCollective? - -Funds donated via Patreon goes directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses by core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. - -<br><br> - -<h2 align="center">Platinum via Patreon</h2> +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 href="https://stdlib.com"> - <img width="300px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/stdlib.png"> - </a> - <br><br> - <a href="https://xiaozhuanlan.com"> - <img width="200px" src="https://raw.githubusercontent.com/vuejs/cn.vuejs.org/master/themes/vue/source/images/xiaozhuanlan.png"> - </a> - <br><br> - <a href="http://tooltwist.com" target="_blank"> - <img width="280px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tooltwist.png"> + <a target="_blank" href="https://sponsors.vuejs.org/backers.svg"> + <img alt="sponsors" src="https://sponsors.vuejs.org/backers.svg"> </a> </p> - -<h2 align="center">Platinum via OpenCollective</h2> - -<!-- <a href="https://opencollective.com/vuejs/platinumsponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/platinumsponsor/0/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/platinumsponsor/1/website" target="_blank"><img src="https://opencollective.com/vuejs/platinumsponsor/1/avatar.svg"></a> --> - -<h2 align="center">Gold via Patreon</h2> - -<table> - <tbody> - <tr> - <td align="center" valign="middle"> - <a href="https://deepstreamhub.com" target="_blank"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/deepstream.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://jsfiddle.net/"> - <img width="140px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/jsfiddle.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://laravel.com/"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/laravel.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://chaitin.cn/"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/chaitin.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://htmlburger.com/"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/htmlburger.png"> - </a> - </td> - </tr> - <tr></tr> - <tr> - <td align="center" valign="middle"> - <a href="https://starter.someline.com/"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/someline.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="http://monterail.com/" target="_blank"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/monterail.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://www.2mhost.com/" target="_blank"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/2mhost.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://vuejsjob.com/?ref=vuejs" target="_blank"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuejobs.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://leanpub.com/vuejs2" target="_blank"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tmvuejs2.png"> - </a> - </td> - </tr> - <tr></tr> - <tr> - <td align="center" valign="middle"> - <a href="https://component.io/" target="_blank"> - <img width="170px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/component_io.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://www.xfive.co/" target="_blank"> - <img width="100px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/xfive.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="http://www.frontenddevelopermeetups.com/" target="_blank"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/frontend-meetups.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://onsen.io/vue/" target="_blank"> - <img width="170px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/onsen-ui.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://themeforest.net/item/clear-bootstrap-vuejs-admin-template/19339739?ref=jyostna&utm_source=vuejs&utm_campaign=vuejs_patreon&clickthrough_id=1113830525&redirect_back=true" target="_blank"> - <img width="150px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuejsadmin.png"> - </a> - </td> - </tr> - </tbody> -</table> - -<h2 align="center">Gold via OpenCollective</h2> - -<a href="https://opencollective.com/vuejs/goldsponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/0/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/1/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/1/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/2/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/2/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/3/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/3/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/4/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/4/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/5/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/5/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/6/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/6/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/7/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/7/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/8/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/8/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/9/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/9/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/10/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/10/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/11/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/11/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/12/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/12/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/13/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/13/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/14/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/14/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/15/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/15/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/16/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/16/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/17/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/17/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/18/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/18/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/19/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/19/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/20/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/20/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/21/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/21/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/22/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/22/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/23/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/23/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/24/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/24/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/25/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/25/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/26/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/26/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/27/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/27/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/28/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/28/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/goldsponsor/29/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/29/avatar.svg"></a> - -<h2 align="center">Silver via Patreon</h2> - -- Matt Mullenweg - -<h2 align="center">Silver via OpenCollective</h2> - -<a href="https://opencollective.com/vuejs/silversponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/0/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/1/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/1/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/2/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/2/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/3/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/3/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/4/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/4/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/5/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/5/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/6/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/6/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/7/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/7/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/8/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/8/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/9/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/9/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/10/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/10/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/11/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/11/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/12/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/12/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/13/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/13/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/14/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/14/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/15/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/15/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/16/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/16/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/17/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/17/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/18/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/18/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/19/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/19/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/20/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/20/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/21/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/21/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/22/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/22/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/23/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/23/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/24/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/24/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/25/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/25/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/26/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/26/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/27/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/27/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/28/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/28/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/silversponsor/29/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/29/avatar.svg"></a> - -<h2 align="center">Bronze via Patreon</h2> - -<table> - <tbody> - <tr> - <td align="center" valign="middle"> - <a href="http://tighten.co/"> - <img width="140px" src="http://i.imgur.com/T7fQYLT.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="http://invoicemachine.com/"> - <img width="140px" src="http://assets.invoicemachine.com/images/flat_logo.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://alligator.io"> - <img width="140px" src="https://alligator.io/images/alligator-logo.svg"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://monei.net/"> - <img width="90px" src="http://i.imgur.com/JUSjHAi.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://www.accelebrate.com/"> - <img width="120pxpx" src="https://www.accelebrate.com/assets/images/accelebrate_logo@2x.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://www.waterfall.com"> - <img width="100px" src="https://waterfall.com/waterfall_logo_large.png"> - </a> - </td> - </tr> - <tr></tr> - <tr> - <td align="center" valign="middle"> - <a href="https://vuetifyjs.com"> - <img width="40px" src="https://vuetifyjs.com/static/doc-images/logo.svg"> - </a> - </td> - </tr> - </tbody> -</table> - -<h2 align="center">Bronze via OpenCollective</h2> - -<a href="https://opencollective.com/vuejs/bronzesponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/0/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/1/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/1/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/2/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/2/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/3/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/3/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/4/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/4/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/5/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/5/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/6/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/6/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/7/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/7/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/8/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/8/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/9/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/9/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/10/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/10/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/11/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/11/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/12/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/12/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/13/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/13/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/14/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/14/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/15/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/15/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/16/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/16/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/17/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/17/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/18/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/18/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/19/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/19/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/20/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/20/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/21/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/21/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/22/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/22/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/23/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/23/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/24/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/24/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/25/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/25/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/26/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/26/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/27/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/27/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/28/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/28/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/bronzesponsor/29/website" target="_blank"><img src="https://opencollective.com/vuejs/bronzesponsor/29/avatar.svg"></a> - -<h2 align="center">Generous Backers via Patreon ($50+)</h2> - -- No Divide Studio -- James Kyle -- Blake Newman -- Lee Smith -- Adam Dorsey -- Greg McCarvell -- Yoshiya Hinosawa -- Wasim Khamlichi -- errorrik -- Alex Balashov -- Konstantin Levinski -- Samuel Smith -- Harri J Salomaa - -<h2 align="center">Backers via Patreon</h2> - -- 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 -- 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 Brooks -- Kirk Lewis -- 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 -- Barbara Liau -- Jens Lind -- Yegor Sytnyk -- Benson Wong -- Anthony Tsui -- Karol Fabjanczuk -- Isaac Sant -- Milos Stojanovic -- Matsumoto Takamasa -- Douglas Lowder -- Bess Brooks -- Christian Griffith -- Matt Rockwell -- Jarek Tkaczyk -- Michael Laccetti -- Jonothan Allen -- Andrew Davis -- Jason Rys -- Sean -- Xiaojie Li -- Joakim Bugge -- Lacey Pevey -- David Hess -- Niannian Modisette -- Kim Cuartero -- Luke Sampson -- Dariusz Jastrzębski -- Ivan Sieder -- Jivan Roquet -- Shane -- Stew Heckenberg -- Matt Jones -- Dave Chenell -- Frank Baele -- Jack McDade -- Patrick O'Dacre -- Wietse Wind -- Donny Donny -- Duncan Kenzie -- Mike Margerum -- Michael Richards -- Eduardo Reveles -- Jan Kremlacek -- Guy Gavergun -- Keith Bailey -- Joel Birch -- Bernhard E. Reiter -- Radu Cretu -- Luiz Tanure -- Poamrong Rith -- Chengzhi Yin -- Dan Barrett -- Zoran Knezevic -- Charles Beaumont -- Jonathan Kent -- James Simpson -- Pascal Germain -- Pierre Vanhulst -- Vincent Gabriel -- Kyovo Digaw -- devneko -- Cheng-Wei Chien -- Michael Mazurczak -- Daniel -- Chris Anderson -- Jon Hobbs-Smith -- Chez Tschetter -- Akiho Nagao -- Alexander Karelas -- Asaf Yishai -- Diana Espino -- Alexandre Madurell -- alxs -- Anthony Estebe -- Haim Yulzari -- Blake Campbell -- David McGuigan -- Niklas Wallentin -- Jeremy Tan -- Jim Raden -- Luka Savic -- IMGNRY -- Pascal Germain -- Raphaël Saunier -- Kirk Lewis -- Nicholas Reid -- Tyler -- Yong Jun Thong -- Jonatan Machado -- Tai Shi Ling -- Bryan Gruneberg -- Matthew McMillion -- Keith Mancuso -- Alexander Karelas -- Matias Verdier -- Jamie McElwain -- Travis Gertz -- Ale Mohamad -- Jonas Galvez -- William Correa -- Raphael Belvederese -- Mickaël Andrieu -- Guilherme S L de Souza -- Rob Yedlin -- Daniel Waghorn -- Eric Githinji -- Vivekanandhan Natarajan -- Chih-Hsuan Fan -- Jordan Oroshiba -- Brian Jorden -- Cliff Hess -- Joe Gregory -- Johnny Eshan -- Alexander Karelas -- Kamil Ocean -- Orney Enrique Martinez Said -- Tom Striker - -<h2 align="center">Backers via OpenCollective</h2> - -<a href="https://opencollective.com/vuejs#backers" target="_blank"><img src="https://opencollective.com/vuejs/backers.svg?width=890"></a> diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000000..d040c2c85b8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1618 @@ +## [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/README.md b/README.md index 392467b06ee..274642ee3ae 100644 --- a/README.md +++ b/README.md @@ -1,149 +1,41 @@ -<p align="center"><a href="https://vuejs.org" target="_blank"><img width="100"src="https://vuejs.org/images/logo.png"></a></p> +## Vue 2 has reached End of Life -<p align="center"> - <a href="https://circleci.com/gh/vuejs/vue/tree/dev"><img src="https://img.shields.io/circleci/project/vuejs/vue/dev.svg" 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" alt="Coverage Status"></a> - <a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/dm/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> - <a href="https://chat.vuejs.org/"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg" alt="Chat"> - <br> - <a href="https://saucelabs.com/u/vuejs"><img src="https://saucelabs.com/browser-matrix/vuejs.svg" alt="Sauce Test Status"></a> -</p> +**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).** -<h2 align="center">Supporting Vue.js</h2> +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). -Vue.js is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider: +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). -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou). -- [Become a backer or sponsor on Open Collective](https://opencollective.com/vuejs). +<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> -#### What's the difference between Patreon and OpenCollective? +<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> +</p> -Funds donated via Patreon goes directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses by core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. +## Sponsors -<h3 align="center">Sponsors via Patreon</h3> +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/). -<h4 align="center">Platinum</h4> +<p align="center"> + <h3 align="center">Special Sponsor</h3> +</p> <p align="center"> - <a href="https://stdlib.com"> - <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/stdlib.png"> - </a> - <br><br> - <a href="https://xiaozhuanlan.com"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/cn.vuejs.org/master/themes/vue/source/images/xiaozhuanlan.png"> - </a> - <br><br> - <a href="http://tooltwist.com" target="_blank"> - <img width="220px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tooltwist.png"> + <a target="_blank" href="https://github.com/appwrite/appwrite"> + <img alt="special sponsor appwrite" src="https://sponsors.vuejs.org/images/appwrite.svg" width="300"> </a> </p> -<h4 align="center">Gold</h4> - -<table> - <tbody> - <tr> - <td align="center" valign="middle"> - <a href="https://deepstreamhub.com" target="_blank"> - <img width="140px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/deepstream.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://jsfiddle.net/"> - <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/jsfiddle.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://laravel.com/"> - <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/laravel.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://chaitin.cn/"> - <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/chaitin.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://htmlburger.com/"> - <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/htmlburger.png"> - </a> - </td> - <td align="center" valign="middle"> - <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> - </td> - </tr> - <tr></tr> - <tr> - <td align="center" valign="middle"> - <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> - </td> - <td align="center" valign="middle"> - <a href="https://www.2mhost.com/" target="_blank"> - <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/2mhost.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://vuejsjob.com/?ref=vuejs" target="_blank"> - <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuejobs.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://leanpub.com/vuejs2" target="_blank"> - <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tmvuejs2.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://component.io/" target="_blank"> - <img width="130px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/component_io.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://www.xfive.co/" target="_blank"> - <img width="80px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/xfive.png"> - </a> - </td> - </tr> - <tr></tr> - <tr> - <td align="center" valign="middle"> - <a href="http://www.frontenddevelopermeetups.com/" target="_blank"> - <img width="160px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/frontend-meetups.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://onsen.io/vue/" target="_blank"> - <img width="130px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/onsen-ui.png"> - </a> - </td> - <td align="center" valign="middle"> - <a href="https://themeforest.net/item/clear-bootstrap-vuejs-admin-template/19339739?ref=jyostna&utm_source=vuejs.org&utm_campaign=vuejs_patreon" target="_blank"> - <img width="150px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuejsadmin.png"> - </a> - </td> - </tr> - </tbody> -</table> - -<h3 align="center">Sponsors via <a href="https://opencollective.com/vuejs">Open Collective</a></h3> - -<h4 align="center">Platinum</h4> - -<a href="https://opencollective.com/vuejs/tiers/platinumsponsors/0/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/platinumsponsors/0/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/tiers/platinumsponsors/1/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/platinumsponsors/1/avatar.svg"></a> - -<h4 align="center">Gold</h4> - -<a href="https://opencollective.com/vuejs/tiers/goldsponsors/0/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/goldsponsors/0/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/tiers/goldsponsors/1/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/goldsponsors/1/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/tiers/goldsponsors/2/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/goldsponsors/2/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/tiers/goldsponsors/3/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/goldsponsors/3/avatar.svg"></a> -<a href="https://opencollective.com/vuejs/tiers/goldsponsors/4/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/goldsponsors/4/avatar.svg"></a> +<p align="center"> + <a target="_blank" href="https://vuejs.org/sponsor/"> + <img alt="sponsors" src="https://sponsors.vuejs.org/sponsors.svg?v3"> + </a> +</p> --- @@ -153,20 +45,20 @@ Vue (pronounced `/vjuː/`, like view) is a **progressive framework** for buildin #### Browser Compatibility -Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). +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 | +| 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 @@ -175,20 +67,18 @@ Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/co [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-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-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-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 @@ -197,11 +87,11 @@ Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/co ## Documentation -To check out live examples and docs, visit [vuejs.org](https://vuejs.org). +To check out [live examples](https://v2.vuejs.org/v2/examples/) and docs, visit [vuejs.org](https://v2.vuejs.org). ## Questions -For questions and support please use the [the official forum](http://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests. +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. ## Issues @@ -219,15 +109,14 @@ Detailed changes for each release are documented in the [release notes](https:// ## 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/.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)! Thank you to all the people who already contributed to Vue! <a href="https://github.com/vuejs/vue/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a> - ## License -[MIT](http://opensource.org/licenses/MIT) +[MIT](https://opensource.org/licenses/MIT) Copyright (c) 2013-present, Yuxi (Evan) You diff --git a/api-extractor.json b/api-extractor.json new file mode 100644 index 00000000000..b92115b8c62 --- /dev/null +++ b/api-extractor.json @@ -0,0 +1,64 @@ +{ + "$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 new file mode 100644 index 00000000000..ba778b35435 --- /dev/null +++ b/api-extractor.tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "baseUrl": "./temp", + "types": [] + } +} diff --git a/benchmarks/reorder-list/index.html b/benchmarks/reorder-list/index.html index 81b5a26d0d8..668160b0719 100644 --- a/benchmarks/reorder-list/index.html +++ b/benchmarks/reorder-list/index.html @@ -15,7 +15,7 @@ <script type="text/x-template" id="t"> <div> - <h1>{{ total }} Components</h1> + <h1>{{ items.length }} Components</h1> <p>{{ action }} took {{time}}ms.</p> <button @click="shuffle">shuffle</button> <button @click="add">add</button> diff --git a/benchmarks/ssr/README.md b/benchmarks/ssr/README.md index 2f0529c1fd8..c61d6daa57d 100644 --- a/benchmarks/ssr/README.md +++ b/benchmarks/ssr/README.md @@ -2,7 +2,7 @@ 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 are 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. +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. diff --git a/benchmarks/ssr/common.js b/benchmarks/ssr/common.js index 688832f6dda..0411ac896dc 100644 --- a/benchmarks/ssr/common.js +++ b/benchmarks/ssr/common.js @@ -1,6 +1,6 @@ 'use strict' -const self = (global || root) +const self = (global || root) // eslint-disable-line self.performance = { now: function () { @@ -35,11 +35,11 @@ module.exports = { } }, // 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" :row="row"></row></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"></column></tr>', + template: '<tr><th>{{ Math.random() }}</th><column v-for="item in row.items" :key="item.id"></column></tr>', components: { column: { template: '<td class="item">' + diff --git a/benchmarks/ssr/renderToStream.js b/benchmarks/ssr/renderToStream.js index 74ca103f3f8..40dacef7811 100644 --- a/benchmarks/ssr/renderToStream.js +++ b/benchmarks/ssr/renderToStream.js @@ -5,7 +5,7 @@ process.env.NODE_ENV = 'production' const Vue = require('../../dist/vue.runtime.common.js') -const createRenderer = require('../../packages/vue-server-renderer').createRenderer +const createRenderer = require('../../packages/server-renderer').createRenderer const renderToStream = createRenderer().renderToStream const gridComponent = require('./common.js') diff --git a/benchmarks/ssr/renderToString.js b/benchmarks/ssr/renderToString.js index a96373e0504..1988f730e73 100644 --- a/benchmarks/ssr/renderToString.js +++ b/benchmarks/ssr/renderToString.js @@ -3,7 +3,7 @@ process.env.NODE_ENV = 'production' const Vue = require('../../dist/vue.runtime.common.js') -const createRenderer = require('../../packages/vue-server-renderer').createRenderer +const createRenderer = require('../../packages/server-renderer').createRenderer const renderToString = createRenderer().renderToString const gridComponent = require('./common.js') diff --git a/benchmarks/uptime/index.html b/benchmarks/uptime/index.html index 4a375152cf2..d43c93010a6 100644 --- a/benchmarks/uptime/index.html +++ b/benchmarks/uptime/index.html @@ -38,7 +38,7 @@ .days { display: flex; flex-direction: row; - flex-flow: wrap; + flex-wrap: wrap; } .uptime-day { @@ -82,7 +82,7 @@ </div> <script src="../../dist/vue.min.js"></script> <script> - // functional components are prefect for small, presentational components + // functional components are perfect for small, presentational components // and they are much more efficient than stateful ones. Vue.component('uptime-day', { props: ['day'], diff --git a/build/alias.js b/build/alias.js deleted file mode 100644 index 30a27b59c19..00000000000 --- a/build/alias.js +++ /dev/null @@ -1,15 +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'), - weex: resolve('src/platforms/weex'), - server: resolve('src/server'), - entries: resolve('src/entries'), - sfc: resolve('src/sfc') -} diff --git a/build/build.js b/build/build.js deleted file mode 100644 index 82d4c52e631..00000000000 --- a/build/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 uglify = require('uglify-js') - -if (!fs.existsSync('dist')) { - fs.mkdirSync('dist') -} - -let builds = require('./config').getAllBuilds() - -// filter builds via command line arg -if (process.argv[2]) { - const filters = process.argv[2].split(',') - builds = builds.filter(b => { - return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1) - }) -} else { - // filter out weex builds by default - builds = builds.filter(b => { - return b.output.file.indexOf('weex') === -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\.js$/.test(file) - return rollup.rollup(config) - .then(bundle => bundle.generate(output)) - .then(({ code }) => { - if (isProd) { - var minified = (banner ? banner + '\n' : '') + uglify.minify(code, { - output: { - ascii_only: true - }, - compress: { - pure_funcs: ['makeMap'] - } - }).code - return write(file, minified, true) - } else { - return write(file, code) - } - }) -} - -function write (dest, code, zip) { - return new Promise((resolve, reject) => { - function report (extra) { - console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || '')) - resolve() - } - - fs.writeFile(dest, code, err => { - if (err) return reject(err) - if (zip) { - zlib.gzip(code, (err, zipped) => { - if (err) return reject(err) - report(' (gzipped: ' + getSize(zipped) + ')') - }) - } else { - report() - } - }) - }) -} - -function getSize (code) { - return (code.length / 1024).toFixed(2) + 'kb' -} - -function logError (e) { - console.log(e) -} - -function blue (str) { - return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' -} diff --git a/build/config.js b/build/config.js deleted file mode 100644 index b0c705e4d1a..00000000000 --- a/build/config.js +++ /dev/null @@ -1,213 +0,0 @@ -const path = require('path') -const buble = require('rollup-plugin-buble') -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') -const flow = require('rollup-plugin-flow-no-whitespace') -const version = process.env.VERSION || require('../package.json').version -const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version - -const banner = - '/*!\n' + - ' * Vue.js v' + version + '\n' + - ' * (c) 2014-' + new Date().getFullYear() + ' Evan You\n' + - ' * Released under the MIT License.\n' + - ' */' - -const weexFactoryPlugin = { - intro () { - return 'module.exports = function weexFactory (exports, document) {' - }, - outro () { - return '}' - } -} - -const aliases = require('./alias') -const resolve = p => { - const base = p.split('/')[0] - if (aliases[base]) { - return path.resolve(aliases[base], p.slice(base.length + 1)) - } else { - return path.resolve(__dirname, '../', p) - } -} - -const builds = { - // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify - 'web-runtime-cjs': { - entry: resolve('web/entry-runtime.js'), - dest: resolve('dist/vue.runtime.common.js'), - format: 'cjs', - banner - }, - // Runtime+compiler CommonJS build (CommonJS) - 'web-full-cjs': { - entry: resolve('web/entry-runtime-with-compiler.js'), - dest: resolve('dist/vue.common.js'), - format: 'cjs', - alias: { he: './entity-decoder' }, - banner - }, - // Runtime only (ES Modules). Used by bundlers that support ES Modules, - // e.g. Rollup & Webpack 2 - 'web-runtime-esm': { - entry: resolve('web/entry-runtime.js'), - dest: resolve('dist/vue.runtime.esm.js'), - format: 'es', - banner - }, - // Runtime+compiler CommonJS build (ES Modules) - 'web-full-esm': { - entry: resolve('web/entry-runtime-with-compiler.js'), - dest: resolve('dist/vue.esm.js'), - format: 'es', - alias: { he: './entity-decoder' }, - banner - }, - // runtime-only build (Browser) - 'web-runtime-dev': { - entry: resolve('web/entry-runtime.js'), - dest: resolve('dist/vue.runtime.js'), - format: 'umd', - env: 'development', - banner - }, - // runtime-only production build (Browser) - 'web-runtime-prod': { - entry: resolve('web/entry-runtime.js'), - dest: resolve('dist/vue.runtime.min.js'), - format: 'umd', - env: 'production', - banner - }, - // Runtime+compiler development build (Browser) - 'web-full-dev': { - entry: resolve('web/entry-runtime-with-compiler.js'), - dest: resolve('dist/vue.js'), - format: 'umd', - env: 'development', - alias: { he: './entity-decoder' }, - banner - }, - // Runtime+compiler production build (Browser) - 'web-full-prod': { - entry: resolve('web/entry-runtime-with-compiler.js'), - dest: resolve('dist/vue.min.js'), - format: 'umd', - env: 'production', - alias: { he: './entity-decoder' }, - banner - }, - // Web compiler (CommonJS). - 'web-compiler': { - entry: resolve('web/entry-compiler.js'), - dest: resolve('packages/vue-template-compiler/build.js'), - format: 'cjs', - external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies) - }, - // Web compiler (UMD for in-browser use). - 'web-compiler-browser': { - entry: resolve('web/entry-compiler.js'), - dest: resolve('packages/vue-template-compiler/browser.js'), - format: 'umd', - env: 'development', - moduleName: 'VueTemplateCompiler', - plugins: [node(), cjs()] - }, - // Web server renderer (CommonJS). - 'web-server-renderer': { - entry: resolve('web/entry-server-renderer.js'), - dest: resolve('packages/vue-server-renderer/build.js'), - format: 'cjs', - external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies) - }, - 'web-server-basic-renderer': { - entry: resolve('web/entry-server-basic-renderer.js'), - dest: resolve('packages/vue-server-renderer/basic.js'), - format: 'umd', - env: 'development', - moduleName: 'renderVueComponentToString', - plugins: [node(), cjs()] - }, - 'web-server-renderer-webpack-server-plugin': { - entry: resolve('server/webpack-plugin/server.js'), - dest: resolve('packages/vue-server-renderer/server-plugin.js'), - format: 'cjs', - external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies) - }, - 'web-server-renderer-webpack-client-plugin': { - entry: resolve('server/webpack-plugin/client.js'), - dest: resolve('packages/vue-server-renderer/client-plugin.js'), - format: 'cjs', - external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies) - }, - // Weex runtime factory - 'weex-factory': { - weex: true, - entry: resolve('weex/entry-runtime-factory.js'), - dest: resolve('packages/weex-vue-framework/factory.js'), - format: 'cjs', - plugins: [weexFactoryPlugin] - }, - // Weex runtime framework (CommonJS). - 'weex-framework': { - weex: true, - entry: resolve('weex/entry-framework.js'), - dest: resolve('packages/weex-vue-framework/index.js'), - format: 'cjs' - }, - // Weex compiler (CommonJS). Used by Weex's Webpack loader. - 'weex-compiler': { - weex: true, - entry: resolve('weex/entry-compiler.js'), - dest: resolve('packages/weex-template-compiler/build.js'), - format: 'cjs', - external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies) - } -} - -function genConfig (name) { - const opts = builds[name] - const config = { - input: opts.entry, - external: opts.external, - plugins: [ - replace({ - __WEEX__: !!opts.weex, - __WEEX_VERSION__: weexVersion, - __VERSION__: version - }), - flow(), - buble(), - alias(Object.assign({}, aliases, opts.alias)) - ].concat(opts.plugins || []), - output: { - file: opts.dest, - format: opts.format, - banner: opts.banner, - name: opts.moduleName || 'Vue' - } - } - - if (opts.env) { - config.plugins.push(replace({ - 'process.env.NODE_ENV': JSON.stringify(opts.env) - })) - } - - 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/build/get-weex-version.js b/build/get-weex-version.js deleted file mode 100644 index d701c4320bc..00000000000 --- a/build/get-weex-version.js +++ /dev/null @@ -1,24 +0,0 @@ -var coreVersion = require('../package.json').version -var weexVersion = require('../packages/weex-vue-framework/package.json').version -var weexBaseVersion = weexVersion.match(/^[\d.]+/)[0] -var weexSubVersion = Number(weexVersion.match(/-weex\.(\d+)$/)[1]) - -if (weexBaseVersion === coreVersion) { - // same core version, increment sub version - weexSubVersion++ -} else { - // new core version, reset sub version - weexBaseVersion = coreVersion - weexSubVersion = 1 -} - -if (process.argv[2] === '-c') { - console.log(weexVersion) -} else { - console.log(weexBaseVersion + '-weex.' + weexSubVersion) -} - -module.exports = { - base: weexBaseVersion, - sub: weexSubVersion -} diff --git a/build/release-weex.sh b/build/release-weex.sh deleted file mode 100644 index 3bb5412dd4f..00000000000 --- a/build/release-weex.sh +++ /dev/null @@ -1,36 +0,0 @@ -set -e -CUR_VERSION=`node build/get-weex-version.js -c` -NEXT_VERSION=`node build/get-weex-version.js` - -echo "Current: $CUR_VERSION" -read -p "Enter new version ($NEXT_VERSION): " -n 1 -r -if ! [[ -z $REPLY ]]; then - NEXT_VERSION=$REPLY -fi - -read -p "Releasing weex-vue-framework@$NEXT_VERSION - are you sure? (y/n) " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "Releasing weex-vue-framework@$NEXT_VERSION ..." - npm run lint - npm run flow - npm run test:weex - - # build - WEEX_VERSION=$NEXT_VERSION npm run build:weex - - # update package - cd packages/weex-vue-framework - npm version $NEXT_VERSION - npm publish - cd - - - cd packages/weex-template-compiler - npm version $NEXT_VERSION - npm publish - cd - - - # commit - git add packages/weex* - git commit -m "[release] weex-vue-framework@$NEXT_VERSION" -fi diff --git a/build/release.sh b/build/release.sh deleted file mode 100644 index 8452944ef8b..00000000000 --- a/build/release.sh +++ /dev/null @@ -1,73 +0,0 @@ -set -e - -if [[ -z $1 ]]; then - echo "Enter new version: " - read VERSION -else - VERSION=$1 -fi - -read -p "Releasing $VERSION - are you sure? (y/n) " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "Releasing $VERSION ..." - - if [[ -z $SKIP_TESTS ]]; then - npm run lint - npm run flow - npm run test:cover - npm run test:e2e - npm run test:ssr - fi - - # Sauce Labs tests has a decent change of failing - # so we usually manually run them before running the release script. - - # if [[ -z $SKIP_SAUCE ]]; then - # export SAUCE_BUILD_ID=$VERSION:`date +"%s"` - # npm run test:sauce - # fi - - # build - VERSION=$VERSION npm run build - - # update packages - cd packages/vue-template-compiler - npm version $VERSION - if [[ -z $RELEASE_TAG ]]; then - npm publish - else - npm publish --tag $RELEASE_TAG - fi - cd - - - cd packages/vue-server-renderer - npm version $VERSION - if [[ -z $RELEASE_TAG ]]; then - npm publish - else - npm publish --tag $RELEASE_TAG - fi - cd - - - # commit - git add -A - git add -f \ - dist/*.js \ - packages/vue-server-renderer/basic.js \ - packages/vue-server-renderer/build.js \ - packages/vue-server-renderer/server-plugin.js \ - packages/vue-server-renderer/client-plugin.js \ - packages/vue-template-compiler/build.js - git commit -m "build: build $VERSION" - npm version $VERSION --message "build: release $VERSION" - - # publish - git push origin refs/tags/v$VERSION - git push - if [[ -z $RELEASE_TAG ]]; then - npm publish - else - npm publish --tag $RELEASE_TAG - fi -fi diff --git a/build/setup.js b/build/setup.js deleted file mode 100644 index 3427c5aa966..00000000000 --- a/build/setup.js +++ /dev/null @@ -1,12 +0,0 @@ -const { test, ln, chmod } = require('shelljs') - -function installHooks () { - if (test('-e', '.git/hooks')) { - ln('-sf', '../../build/git-hooks/pre-commit', '.git/hooks/pre-commit') - chmod('+x', '.git/hooks/pre-commit') - ln('-sf', '../../build/git-hooks/commit-msg', '.git/hooks/commit-msg') - chmod('+x', '.git/hooks/commit-msg') - } -} - -installHooks() diff --git a/compiler-sfc/index.d.ts b/compiler-sfc/index.d.ts new file mode 100644 index 00000000000..3c30abc8ccf --- /dev/null +++ b/compiler-sfc/index.d.ts @@ -0,0 +1 @@ +export * from '@vue/compiler-sfc' diff --git a/compiler-sfc/index.js b/compiler-sfc/index.js new file mode 100644 index 00000000000..774f9da2742 --- /dev/null +++ b/compiler-sfc/index.js @@ -0,0 +1 @@ +module.exports = require('@vue/compiler-sfc') diff --git a/compiler-sfc/index.mjs b/compiler-sfc/index.mjs new file mode 100644 index 00000000000..3c30abc8ccf --- /dev/null +++ b/compiler-sfc/index.mjs @@ -0,0 +1 @@ +export * from '@vue/compiler-sfc' diff --git a/compiler-sfc/package.json b/compiler-sfc/package.json new file mode 100644 index 00000000000..778c7ebf51c --- /dev/null +++ b/compiler-sfc/package.json @@ -0,0 +1,5 @@ +{ + "main": "index.js", + "module": "index.mjs", + "types": "index.d.ts" +} diff --git a/dist/README.md b/dist/README.md deleted file mode 100644 index 6b2068395e7..00000000000 --- a/dist/README.md +++ /dev/null @@ -1,124 +0,0 @@ -## Explanation of Build Files - -| | UMD | CommonJS | ES Module | -| --- | --- | --- | --- | -| **Full** | vue.js | vue.common.js | vue.esm.js | -| **Runtime-only** | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js | -| **Full (production)** | vue.min.js | | | -| **Runtime-only (production)** | vue.runtime.min.js | | | - -### Terms - -- **Full**: builds that contains both the compiler and the runtime. - -- **Compiler**: code that is responsible for compiling template strings into JavaScript render functions. - -- **Runtime**: code that is responsible for creating Vue instances, rendering and patching virtual DOM, etc. Basically everything minus the compiler. - -- **[UMD](https://github.com/umdjs/umd)**: UMD builds can be used directly in the browser via a `<script>` tag. The default file from Unpkg CDN at [https://unpkg.com/vue](https://unpkg.com/vue) is the Runtime + Compiler UMD build (`vue.js`). - -- **[CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1)**: CommonJS builds are intended for use with older bundlers like [browserify](http://browserify.org/) or [webpack 1](https://webpack.github.io). The default file for these bundlers (`pkg.main`) is the Runtime only CommonJS build (`vue.runtime.common.js`). - -- **[ES Module](http://exploringjs.com/es6/ch_modules.html)**: ES module builds are intended for use with modern bundlers like [webpack 2](https://webpack.js.org) or [rollup](http://rollupjs.org/). The default file for these bundlers (`pkg.module`) is the Runtime only ES Module build (`vue.runtime.esm.js`). - -### Runtime + Compiler vs. Runtime-only - -If you need to compile templates on the fly (e.g. passing a string to the `template` option, or mounting to an element using its in-DOM HTML as the template), you will need the compiler and thus the full build. - -When using `vue-loader` or `vueify`, templates inside `*.vue` files are compiled into JavaScript at build time. You don't really need the compiler in the final bundle, and can therefore use the runtime-only build. - -Since the runtime-only builds are roughly 30% lighter-weight than their full-build counterparts, you should use it whenever you can. If you wish to use the full build instead, you need to configure an alias in your bundler. - -#### Webpack - -``` js -module.exports = { - // ... - resolve: { - alias: { - 'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1 - } - } -} -```` - -#### Rollup - -``` js -const alias = require('rollup-plugin-alias') - -rollup({ - // ... - plugins: [ - alias({ - 'vue': 'vue/dist/vue.esm.js' - }) - ] -}) -``` - -#### Browserify - -Add to your project's `package.json`: - -``` js -{ - // ... - "browser": { - "vue": "vue/dist/vue.common.js" - } -} -``` - -### Development vs. Production Mode - -Development/production modes are hard-coded for the UMD builds: the un-minified files are for development, and the minified files are for production. - -CommonJS and ES Module builds are intended for bundlers, therefore we don't provide minified versions for them. You will be responsible for minifying the final bundle yourself. - -CommonJS and ES Module builds also preserve raw checks for `process.env.NODE_ENV` to determine the mode they should run in. You should use appropriate bundler configurations to replace these environment variables in order to control which mode Vue will run in. Replacing `process.env.NODE_ENV` with string literals also allows minifiers like UglifyJS to completely drop the development-only code blocks, reducing final file size. - -#### Webpack - -Use Webpack's [DefinePlugin](https://webpack.js.org/plugins/define-plugin/): - -``` js -var webpack = require('webpack') - -module.exports = { - // ... - plugins: [ - // ... - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify('production') - } - }) - ] -} -``` - -#### Rollup - -Use [rollup-plugin-replace](https://github.com/rollup/rollup-plugin-replace): - -``` js -const replace = require('rollup-plugin-replace') - -rollup({ - // ... - plugins: [ - replace({ - 'process.env.NODE_ENV': JSON.stringify('production') - }) - ] -}).then(...) -``` - -#### Browserify - -Apply a global [envify](https://github.com/hughsk/envify) transform to your bundle. - -``` bash -NODE_ENV=production browserify -g envify -e main.js | uglifyjs -c -m > build.js -``` diff --git a/dist/vue.common.js b/dist/vue.common.js index 5527db3e248..3bbc37c95fc 100644 --- a/dist/vue.common.js +++ b/dist/vue.common.js @@ -1,10615 +1,5 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 Evan You - * Released under the MIT License. - */ -'use strict'; - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real 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. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - 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) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else if (process.env.NODE_ENV !== 'production') { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else if (process.env.NODE_ENV !== 'production') { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if (process.env.NODE_ENV !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (process.env.NODE_ENV !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; +if (process.env.NODE_ENV === 'production') { + module.exports = require('./vue.common.prod.js') } else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -var mark; -var measure; - -if (process.env.NODE_ENV !== 'production') { - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -if (process.env.NODE_ENV !== 'production') { - var 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,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var 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; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - if (process.env.NODE_ENV !== 'production') { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - process.env.NODE_ENV !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - process.env.NODE_ENV !== 'production' - ? ("timeout (" + (res.timeout) + "ms)") - : null - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - -function getFirstComponentChild (children) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var c = children[i]; - if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { - return c - } - } - } -} - -/* */ - -/* */ - -function initEvents (vm) { - vm._events = Object.create(null); - vm._hasHookEvent = false; - // init parent attached events - var listeners = vm.$options._parentListeners; - if (listeners) { - updateComponentListeners(vm, listeners); - } -} - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -function eventsMixin (Vue) { - var hookRE = /^hook:/; - Vue.prototype.$on = function (event, fn) { - var this$1 = this; - - var vm = this; - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$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, fn) { - var vm = 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, fn) { - var this$1 = this; - - var vm = this; - // all - if (!arguments.length) { - vm._events = Object.create(null); - return vm - } - // array of events - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$off(event[i], fn); - } - return vm - } - // specific event - var cbs = vm._events[event]; - if (!cbs) { - return vm - } - if (!fn) { - vm._events[event] = null; - return vm - } - if (fn) { - // specific handler - var cb; - var i$1 = cbs.length; - while (i$1--) { - cb = cbs[i$1]; - if (cb === fn || cb.fn === fn) { - cbs.splice(i$1, 1); - break - } - } - } - return vm - }; - - Vue.prototype.$emit = function (event) { - var vm = this; - if (process.env.NODE_ENV !== 'production') { - var 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 + "\"." - ); - } - } - var cbs = vm._events[event]; - if (cbs) { - cbs = cbs.length > 1 ? toArray(cbs) : cbs; - var args = toArray(arguments, 1); - for (var i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args); - } catch (e) { - handleError(e, vm, ("event handler for \"" + event + "\"")); - } - } - } - return vm - }; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; -var isUpdatingChildComponent = false; - -function initLifecycle (vm) { - var options = vm.$options; - - // locate first non-abstract parent - var 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._watcher = null; - vm._inactive = null; - vm._directInactive = false; - vm._isMounted = false; - vm._isDestroyed = false; - vm._isBeingDestroyed = false; -} - -function lifecycleMixin (Vue) { - Vue.prototype._update = function (vnode, hydrating) { - var vm = this; - if (vm._isMounted) { - callHook(vm, 'beforeUpdate'); - } - var prevEl = vm.$el; - var prevVnode = vm._vnode; - var prevActiveInstance = activeInstance; - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ); - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null; - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode); - } - activeInstance = prevActiveInstance; - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el; - } - // updated hook is called by the scheduler to ensure that children are - // updated in a parent's updated hook. - }; - - Vue.prototype.$forceUpdate = function () { - var vm = this; - if (vm._watcher) { - vm._watcher.update(); - } - }; - - Vue.prototype.$destroy = function () { - var vm = this; - if (vm._isBeingDestroyed) { - return - } - callHook(vm, 'beforeDestroy'); - vm._isBeingDestroyed = true; - // remove self from parent - var parent = vm.$parent; - if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { - remove(parent.$children, vm); - } - // teardown watchers - if (vm._watcher) { - vm._watcher.teardown(); - } - var i = vm._watchers.length; - while (i--) { - vm._watchers[i].teardown(); - } - // 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; - } - }; -} - -function mountComponent ( - vm, - el, - hydrating -) { - vm.$el = el; - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode; - if (process.env.NODE_ENV !== 'production') { - /* 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'); - - var updateComponent; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - updateComponent = function () { - var name = vm._name; - var id = vm._uid; - var startTag = "vue-perf-start:" + id; - var endTag = "vue-perf-end:" + id; - - mark(startTag); - var 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 = function () { - vm._update(vm._render(), hydrating); - }; - } - - vm._watcher = new Watcher(vm, updateComponent, noop); - hydrating = false; - - // 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 -} - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = true; - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = false; - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index = queue.length = activatedChildren.length = 0; - has = {}; - if (process.env.NODE_ENV !== 'production') { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // 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]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var 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; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = process.env.NODE_ENV !== 'production' - ? expOrFn.toString() - : ''; - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - process.env.NODE_ENV !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var sharedPropertyDefinition = { - enumerable: true, - configurable: true, - get: noop, - set: noop -}; - -function proxy (target, sourceKey, key) { - sharedPropertyDefinition.get = function proxyGetter () { - return this[sourceKey][key] - }; - sharedPropertyDefinition.set = function proxySetter (val) { - this[sourceKey][key] = val; - }; - Object.defineProperty(target, key, sharedPropertyDefinition); -} - -function initState (vm) { - vm._watchers = []; - var opts = vm.$options; - if (opts.props) { initProps(vm, opts.props); } - if (opts.methods) { initMethods(vm, opts.methods); } - if (opts.data) { - initData(vm); - } else { - observe(vm._data = {}, true /* asRootData */); - } - if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch && opts.watch !== nativeWatch) { - initWatch(vm, opts.watch); - } -} - -function initProps (vm, propsOptions) { - var propsData = vm.$options.propsData || {}; - var props = vm._props = {}; - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - var keys = vm.$options._propKeys = []; - var isRoot = !vm.$parent; - // root instance props should be converted - observerState.shouldConvert = isRoot; - var loop = function ( key ) { - keys.push(key); - var value = validateProp(key, propsOptions, propsData, vm); - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - var 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, function () { - if (vm.$parent && !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 - ); - } - }); - } else { - defineReactive(props, key, value); - } - // 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); - } - }; - - for (var key in propsOptions) loop( key ); - observerState.shouldConvert = true; -} - -function initData (vm) { - var data = vm.$options.data; - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {}; - if (!isPlainObject(data)) { - data = {}; - process.env.NODE_ENV !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', - vm - ); - } - // proxy data on instance - var keys = Object.keys(data); - var props = vm.$options.props; - var methods = vm.$options.methods; - var i = keys.length; - while (i--) { - var key = keys[i]; - if (process.env.NODE_ENV !== 'production') { - if (methods && hasOwn(methods, key)) { - warn( - ("Method \"" + key + "\" has already been defined as a data property."), - vm - ); - } - } - if (props && hasOwn(props, key)) { - process.env.NODE_ENV !== 'production' && 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 - observe(data, true /* asRootData */); -} - -function getData (data, vm) { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, "data()"); - return {} - } -} - -var computedWatcherOptions = { lazy: true }; - -function initComputed (vm, computed) { - var watchers = vm._computedWatchers = Object.create(null); - // computed properties are just getters during SSR - var isSSR = isServerRendering(); - - for (var key in computed) { - var userDef = computed[key]; - var getter = typeof userDef === 'function' ? userDef : userDef.get; - if (process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production') { - 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); - } - } - } -} - -function defineComputed ( - target, - key, - userDef -) { - var shouldCache = !isServerRendering(); - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef; - sharedPropertyDefinition.set = noop; - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop; - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop; - } - if (process.env.NODE_ENV !== 'production' && - 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 () { - var watcher = this._computedWatchers && this._computedWatchers[key]; - if (watcher) { - if (watcher.dirty) { - watcher.evaluate(); - } - if (Dep.target) { - watcher.depend(); - } - return watcher.value - } - } -} - -function initMethods (vm, methods) { - var props = vm.$options.props; - for (var key in methods) { - if (process.env.NODE_ENV !== 'production') { - if (methods[key] == null) { - warn( - "Method \"" + key + "\" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm); - } -} - -function initWatch (vm, watch) { - for (var key in watch) { - var handler = watch[key]; - if (Array.isArray(handler)) { - for (var i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]); - } - } else { - createWatcher(vm, key, handler); - } - } -} - -function createWatcher ( - vm, - keyOrFn, - handler, - options -) { - if (isPlainObject(handler)) { - options = handler; - handler = handler.handler; - } - if (typeof handler === 'string') { - handler = vm[handler]; - } - return vm.$watch(keyOrFn, handler, options) -} - -function stateMixin (Vue) { - // flow somehow has problems with directly declared definition object - // when using Object.defineProperty, so we have to procedurally build up - // the object here. - var dataDef = {}; - dataDef.get = function () { return this._data }; - var propsDef = {}; - propsDef.get = function () { return this._props }; - if (process.env.NODE_ENV !== 'production') { - dataDef.set = function (newData) { - 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, - cb, - options - ) { - var vm = this; - if (isPlainObject(cb)) { - return createWatcher(vm, expOrFn, cb, options) - } - options = options || {}; - options.user = true; - var watcher = new Watcher(vm, expOrFn, cb, options); - if (options.immediate) { - cb.call(vm, watcher.value); - } - return function unwatchFn () { - watcher.teardown(); - } - }; -} - -/* */ - -function initProvide (vm) { - var provide = vm.$options.provide; - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide; - } -} - -function initInjections (vm) { - var result = resolveInject(vm.$options.inject, vm); - if (result) { - observerState.shouldConvert = false; - Object.keys(result).forEach(function (key) { - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, key, result[key], function () { - 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]); - } - }); - observerState.shouldConvert = true; - } -} - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else if (process.env.NODE_ENV !== 'production') { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - if (process.env.NODE_ENV !== 'production') { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -function initRender (vm) { - vm._vnode = null; // the root of the child tree - var options = vm.$options; - var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree - var renderContext = parentVnode && parentVnode.context; - vm.$slots = resolveSlots(options._renderChildren, renderContext); - vm.$scopedSlots = 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 - vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; - // normalization is always applied for the public version, used in - // user-written render functions. - vm.$createElement = function (a, b, c, d) { return 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 - var parentData = parentVnode && parentVnode.data; - - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () { - !isUpdatingChildComponent && warn("$attrs is readonly.", vm); - }, true); - defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () { - !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); - } -} - -function renderMixin (Vue) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype); - - Vue.prototype.$nextTick = function (fn) { - return nextTick(fn, this) - }; - - Vue.prototype._render = function () { - var vm = this; - var ref = vm.$options; - var render = ref.render; - var _parentVnode = ref._parentVnode; - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (var key in vm.$slots) { - var slot = vm.$slots[key]; - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */); - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode; - // render self - var vnode; - try { - vnode = render.call(vm._renderProxy, vm.$createElement); - } catch (e) { - handleError(e, vm, "render"); - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); - } catch (e) { - handleError(e, vm, "renderError"); - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if (process.env.NODE_ENV !== 'production' && Array.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 - }; -} - -/* */ - -var uid$1 = 0; - -function initMixin (Vue) { - Vue.prototype._init = function (options) { - var vm = this; - // a uid - vm._uid = uid$1++; - - var startTag, endTag; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - startTag = "vue-perf-start:" + (vm._uid); - endTag = "vue-perf-end:" + (vm._uid); - mark(startTag); - } - - // a flag to avoid this being observed - vm._isVue = 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); - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ); - } - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - initProxy(vm); - } else { - vm._renderProxy = vm; - } - // expose real self - vm._self = vm; - initLifecycle(vm); - initEvents(vm); - initRender(vm); - callHook(vm, 'beforeCreate'); - initInjections(vm); // resolve injections before data/props - initState(vm); - initProvide(vm); // resolve provide after data/props - callHook(vm, 'created'); - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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); - } - }; -} - -function initInternalComponent (vm, options) { - var opts = vm.$options = Object.create(vm.constructor.options); - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent; - opts.propsData = options.propsData; - opts._parentVnode = options._parentVnode; - opts._parentListeners = options._parentListeners; - opts._renderChildren = options._renderChildren; - opts._componentTag = options._componentTag; - opts._parentElm = options._parentElm; - opts._refElm = options._refElm; - if (options.render) { - opts.render = options.render; - opts.staticRenderFns = options.staticRenderFns; - } -} - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -function Vue$3 (options) { - if (process.env.NODE_ENV !== 'production' && - !(this instanceof Vue$3) - ) { - warn('Vue is a constructor and should be called with the `new` keyword'); - } - this._init(options); -} - -initMixin(Vue$3); -stateMixin(Vue$3); -eventsMixin(Vue$3); -lifecycleMixin(Vue$3); -renderMixin(Vue$3); - -/* */ - -function initUse (Vue) { - Vue.use = function (plugin) { - var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - var args = toArray(arguments, 1); - args.unshift(this); - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args); - } else if (typeof plugin === 'function') { - plugin.apply(null, args); - } - installedPlugins.push(plugin); - return this - }; -} - -/* */ - -function initMixin$1 (Vue) { - Vue.mixin = function (mixin) { - this.options = mergeOptions(this.options, mixin); - return this - }; -} - -/* */ - -function initExtend (Vue) { - /** - * 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 - */ - Vue.extend = function (extendOptions) { - extendOptions = extendOptions || {}; - var Super = this; - var SuperId = Super.cid; - var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - 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 characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - } - - var Sub = function VueComponent (options) { - this._init(options); - }; - 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$1(Sub); - } - if (Sub.options.computed) { - initComputed$1(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$1 (Comp) { - var props = Comp.options.props; - for (var key in props) { - proxy(Comp.prototype, "_props", key); - } -} - -function initComputed$1 (Comp) { - var computed = Comp.options.computed; - for (var key in computed) { - defineComputed(Comp.prototype, key, computed[key]); - } -} - -/* */ - -function initAssetRegisters (Vue) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.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' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ); - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id; - definition = this.options._base.extend(definition); - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition }; - } - this.options[type + 's'][id] = definition; - return definition - } - }; - }); -} - -/* */ - -function getComponentName (opts) { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern, name) { - if (Array.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, filter) { - var cache = keepAliveInstance.cache; - var keys = keepAliveInstance.keys; - var _vnode = keepAliveInstance._vnode; - for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode); - } - } - } -} - -function pruneCacheEntry ( - cache, - key, - keys, - current -) { - var cached$$1 = cache[key]; - if (cached$$1 && cached$$1 !== current) { - cached$$1.componentInstance.$destroy(); - } - cache[key] = null; - remove(keys, key); -} - -var patternTypes = [String, RegExp, Array]; - -var KeepAlive = { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created: function created () { - this.cache = Object.create(null); - this.keys = []; - }, - - destroyed: function destroyed () { - var this$1 = this; - - for (var key in this$1.cache) { - pruneCacheEntry(this$1.cache, key, this$1.keys); - } - }, - - watch: { - include: function include (val) { - pruneCache(this, function (name) { return matches(val, name); }); - }, - exclude: function exclude (val) { - pruneCache(this, function (name) { return !matches(val, name); }); - } - }, - - render: function render () { - var vnode = getFirstComponentChild(this.$slots.default); - var componentOptions = vnode && vnode.componentOptions; - if (componentOptions) { - // check pattern - var name = getComponentName(componentOptions); - if (name && ( - (this.exclude && matches(this.exclude, name)) || - (this.include && !matches(this.include, name)) - )) { - return vnode - } - - var ref = this; - var cache = ref.cache; - var keys = ref.keys; - var 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 { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } - } - - vnode.data.keepAlive = true; - } - return vnode - } -}; - -var builtInComponents = { - KeepAlive: KeepAlive -}; - -/* */ - -function initGlobalAPI (Vue) { - // config - var configDef = {}; - configDef.get = function () { return config; }; - if (process.env.NODE_ENV !== 'production') { - configDef.set = function () { - 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: warn, - extend: extend, - mergeOptions: mergeOptions, - defineReactive: defineReactive - }; - - Vue.set = set; - Vue.delete = del; - Vue.nextTick = nextTick; - - Vue.options = Object.create(null); - ASSET_TYPES.forEach(function (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$1(Vue); - initExtend(Vue); - initAssetRegisters(Vue); -} - -initGlobalAPI(Vue$3); - -Object.defineProperty(Vue$3.prototype, '$isServer', { - get: isServerRendering -}); - -Object.defineProperty(Vue$3.prototype, '$ssrContext', { - get: function get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}); - -Vue$3.version = '2.5.3'; - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -var xlinkNS = 'http://www.w3.org/1999/xlink'; - -var isXlink = function (name) { - return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' -}; - -var getXlinkProp = function (name) { - return isXlink(name) ? name.slice(6, name.length) : '' -}; - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - -var namespaceMap = { - svg: 'http://www.w3.org/2000/svg', - math: 'http://www.w3.org/1998/Math/MathML' -}; - -var 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. -var 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 -); - -var isPreTag = function (tag) { return tag === 'pre'; }; - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - -var unknownElementCache = Object.create(null); -function isUnknownElement (tag) { - /* 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] - } - var el = document.createElement(tag); - if (tag.indexOf('-') > -1) { - // http://stackoverflow.com/a/28210364/1070244 - return (unknownElementCache[tag] = ( - el.constructor === window.HTMLUnknownElement || - el.constructor === window.HTMLElement - )) - } else { - return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString())) - } -} - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ -function query (el) { - if (typeof el === 'string') { - var selected = document.querySelector(el); - if (!selected) { - process.env.NODE_ENV !== 'production' && warn( - 'Cannot find element: ' + el - ); - return document.createElement('div') - } - return selected - } else { - return el - } -} - -/* */ - -function createElement$1 (tagName, vnode) { - var 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 -} - -function createElementNS (namespace, tagName) { - return document.createElementNS(namespaceMap[namespace], tagName) -} - -function createTextNode (text) { - return document.createTextNode(text) -} - -function createComment (text) { - return document.createComment(text) -} - -function insertBefore (parentNode, newNode, referenceNode) { - parentNode.insertBefore(newNode, referenceNode); -} - -function removeChild (node, child) { - node.removeChild(child); -} - -function appendChild (node, child) { - node.appendChild(child); -} - -function parentNode (node) { - return node.parentNode -} - -function nextSibling (node) { - return node.nextSibling -} - -function tagName (node) { - return node.tagName -} - -function setTextContent (node, text) { - node.textContent = text; -} - -function setAttribute (node, key, val) { - node.setAttribute(key, val); -} - - -var nodeOps = Object.freeze({ - createElement: createElement$1, - createElementNS: createElementNS, - createTextNode: createTextNode, - createComment: createComment, - insertBefore: insertBefore, - removeChild: removeChild, - appendChild: appendChild, - parentNode: parentNode, - nextSibling: nextSibling, - tagName: tagName, - setTextContent: setTextContent, - setAttribute: setAttribute -}); - -/* */ - -var ref = { - create: function create (_, vnode) { - registerRef(vnode); - }, - update: function update (oldVnode, vnode) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true); - registerRef(vnode); - } - }, - destroy: function destroy (vnode) { - registerRef(vnode, true); - } -}; - -function registerRef (vnode, isRemoval) { - var key = vnode.data.ref; - if (!key) { return } - - var vm = vnode.context; - var ref = vnode.componentInstance || vnode.elm; - var refs = vm.$refs; - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref); - } else if (refs[key] === ref) { - refs[key] = undefined; - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref]; - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref); - } - } else { - refs[key] = ref; - } - } -} - -/** - * 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. - */ - -var emptyNode = new VNode('', {}, []); - -var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; - -function sameVnode (a, b) { - return ( - a.key === b.key && ( - ( - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - isUndef(b.asyncFactory.error) - ) - ) - ) -} - -function sameInputType (a, b) { - if (a.tag !== 'input') { return true } - var i; - var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type; - var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type; - return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) -} - -function createKeyToOldIdx (children, beginIdx, endIdx) { - var i, key; - var map = {}; - for (i = beginIdx; i <= endIdx; ++i) { - key = children[i].key; - if (isDef(key)) { map[key] = i; } - } - return map -} - -function createPatchFunction (backend) { - var i, j; - var cbs = {}; - - var modules = backend.modules; - var nodeOps = backend.nodeOps; - - 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) { - var parent = nodeOps.parentNode(el); - // element may have already been removed due to v-html / v-text - if (isDef(parent)) { - nodeOps.removeChild(parent, el); - } - } - - var inPre = 0; - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - vnode.isRootInsert = !nested; // for transition enter check - if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { - return - } - - var data = vnode.data; - var children = vnode.children; - var tag = vnode.tag; - if (isDef(tag)) { - if (process.env.NODE_ENV !== 'production') { - if (data && data.pre) { - inPre++; - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(function (ignore) { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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); - - /* istanbul ignore if */ - { - createChildren(vnode, children, insertedVnodeQueue); - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - - if (process.env.NODE_ENV !== 'production' && data && data.pre) { - inPre--; - } - } 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) { - var i = vnode.data; - if (isDef(i)) { - var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; - if (isDef(i = i.hook) && isDef(i = i.init)) { - i(vnode, false /* hydrating */, parentElm, refElm); - } - // 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); - 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) { - var 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. - var 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$$1) { - if (isDef(parent)) { - if (isDef(ref$$1)) { - if (ref$$1.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref$$1); - } - } else { - nodeOps.appendChild(parent, elm); - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); - } - } - - function isPatchable (vnode) { - while (vnode.componentInstance) { - vnode = vnode.componentInstance._vnode; - } - return isDef(vnode.tag) - } - - function invokeCreateHooks (vnode, insertedVnodeQueue) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](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) { - var i; - if (isDef(i = vnode.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, ''); - } else { - var ancestor = vnode; - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, ''); - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm); - } - } - - function invokeDestroyHook (vnode) { - var i, j; - var 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 (parentElm, vnodes, startIdx, endIdx) { - for (; startIdx <= endIdx; ++startIdx) { - var ch = vnodes[startIdx]; - if (isDef(ch)) { - if (isDef(ch.tag)) { - removeAndInvokeRemoveHook(ch); - invokeDestroyHook(ch); - } else { // Text node - removeNode(ch.elm); - } - } - } - } - - function removeAndInvokeRemoveHook (vnode, rm) { - if (isDef(rm) || isDef(vnode.data)) { - var i; - var 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) { - var oldStartIdx = 0; - var newStartIdx = 0; - var oldEndIdx = oldCh.length - 1; - var oldStartVnode = oldCh[0]; - var oldEndVnode = oldCh[oldEndIdx]; - var newEndIdx = newCh.length - 1; - var newStartVnode = newCh[0]; - var newEndVnode = newCh[newEndIdx]; - var 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 - var canMove = !removeOnly; - - 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); - oldStartVnode = oldCh[++oldStartIdx]; - newStartVnode = newCh[++newStartIdx]; - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); - oldEndVnode = oldCh[--oldEndIdx]; - newEndVnode = newCh[--newEndIdx]; - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); - 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); - 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); - } else { - vnodeToMove = oldCh[idxInOld]; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ); - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue); - 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); - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx); - } - } - - function findIdxInOld (node, oldCh, start, end) { - for (var i = start; i < end; i++) { - var c = oldCh[i]; - if (isDef(c) && sameVnode(node, c)) { return i } - } - } - - function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { - if (oldVnode === vnode) { - return - } - - var 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 - } - - var i; - var data = vnode.data; - if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { - i(oldVnode, vnode); - } - - var oldCh = oldVnode.children; - var 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 (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 (var i = 0; i < queue.length; ++i) { - queue[i].data.hook.insert(queue[i]); - } - } - } - - var bailed = 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 - var isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key'); - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm; - vnode.isAsyncPlaceholder = true; - return true - } - if (process.env.NODE_ENV !== 'production') { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm; - var tag = vnode.tag; - var data = vnode.data; - var children = vnode.children; - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('server innerHTML: ', i); - console.warn('client innerHTML: ', elm.innerHTML); - } - return false - } - } else { - // iterate and compare children lists - var childrenMatch = true; - var childNode = elm.firstChild; - for (var i$1 = 0; i$1 < children.length; i$1++) { - if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue)) { - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); - } - return false - } - } - } - } - if (isDef(data)) { - for (var key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text; - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } - return - } - - var isInitialPatch = false; - var insertedVnodeQueue = []; - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true; - createElm(vnode, insertedVnodeQueue, parentElm, refElm); - } else { - var isRealElement = isDef(oldVnode.nodeType); - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 (process.env.NODE_ENV !== 'production') { - 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 - var oldElm = oldVnode.elm; - var parentElm$1 = 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$1, - nodeOps.nextSibling(oldElm) - ); - - // update parent placeholder node element, recursively - if (isDef(vnode.parent)) { - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - // destroy old node - if (isDef(parentElm$1)) { - removeVnodes(parentElm$1, [oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } -} - -/* */ - -var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } -}; - -function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } -} - -function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode, 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode, 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } -} - -var emptyModifiers = Object.create(null); - -function normalizeDirectives$1 ( - dirs, - vm -) { - var res = Object.create(null); - if (!dirs) { - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - return res -} - -function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) -} - -function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } -} - -var baseModules = [ - ref, - directives -]; - -/* */ - -function updateAttrs (oldVnode, vnode) { - var opts = vnode.componentOptions; - if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { - return - } - if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(attrs.__ob__)) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - setAttr(elm, key, cur); - } - } - // #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 ((isIE9 || 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, key, value) { - 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, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'); - } else if (isXlink(key)) { - if (isFalsyAttrValue(value)) { - el.removeAttributeNS(xlinkNS, getXlinkProp(key)); - } else { - el.setAttributeNS(xlinkNS, key, value); - } - } else { - if (isFalsyAttrValue(value)) { - el.removeAttribute(key); - } else { - el.setAttribute(key, value); - } - } -} - -var attrs = { - create: updateAttrs, - update: updateAttrs -}; - -/* */ - -function updateClass (oldVnode, vnode) { - var el = vnode.elm; - var data = vnode.data; - var oldData = oldVnode.data; - if ( - isUndef(data.staticClass) && - isUndef(data.class) && ( - isUndef(oldData) || ( - isUndef(oldData.staticClass) && - isUndef(oldData.class) - ) - ) - ) { - return - } - - var cls = genClassForVnode(vnode); - - // handle transition classes - var 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; - } -} - -var klass = { - create: updateClass, - update: updateClass -}; - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - process.env.NODE_ENV !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index$1; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - len = val.length; - - if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) { - index$1 = val.lastIndexOf('.'); - if (index$1 > -1) { - return { - exp: val.slice(0, index$1), - key: '"' + val.slice(index$1 + 1) + '"' - } - } else { - return { - exp: val, - key: null - } - } - } - - str = val; - index$1 = 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 () { - return str.charCodeAt(++index$1) -} - -function eof () { - return index$1 >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var inBracket = 1; - expressionPos = index$1; - while (!eof()) { - chr = next(); - if (isStringStart(chr)) { - parseString(chr); - continue - } - if (chr === 0x5B) { inBracket++; } - if (chr === 0x5D) { inBracket--; } - if (inBracket === 0) { - expressionEndPos = index$1; - break - } - } -} - -function parseString (chr) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -/* */ - -var warn$1; - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; -var CHECKBOX_RADIO_TOKEN = '__c'; - -function model ( - el, - dir, - _warn -) { - warn$1 = _warn; - var value = dir.value; - var modifiers = dir.modifiers; - var tag = el.tag; - var type = el.attrsMap.type; - - if (process.env.NODE_ENV !== 'production') { - // inputs with type="file" are read only and setting the input's - // value will throw an error. - if (tag === 'input' && type === 'file') { - warn$1( - "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" + - "File inputs are read only. Use a v-on:change listener instead." - ); - } - } - - 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 (process.env.NODE_ENV !== 'production') { - warn$1( - "<" + (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.' - ); - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var valueBinding = getBindingAttr(el, 'value') || 'null'; - var trueValueBinding = getBindingAttr(el, 'true-value') || 'true'; - var 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&&(" + value + "=$$a.concat([$$v]))}" + - "else{$$i>-1&&(" + value + "=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}" + - "}else{" + (genAssignmentCode(value, '$$c')) + "}", - null, true - ); -} - -function genRadioModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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') + "})"; - - var assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'; - var code = "var $$selectedVal = " + selectedVal + ";"; - code = code + " " + (genAssignmentCode(value, assignment)); - addHandler(el, 'change', code, null, true); -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var type = el.attrsMap.type; - var ref = modifiers || {}; - var lazy = ref.lazy; - var number = ref.number; - var trim = ref.trim; - var needCompositionGuard = !lazy && type !== 'range'; - var event = lazy - ? 'change' - : type === 'range' - ? RANGE_TOKEN - : 'input'; - - var valueExpression = '$event.target.value'; - if (trim) { - valueExpression = "$event.target.value.trim()"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var 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()'); - } -} - -/* */ - -// 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 - var 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]; - } -} - -var target$1; - -function createOnceHandler (handler, event, capture) { - var _target = target$1; // save current target element in closure - return function onceHandler () { - var res = handler.apply(null, arguments); - if (res !== null) { - remove$2(event, onceHandler, capture, _target); - } - } -} - -function add$1 ( - event, - handler, - once$$1, - capture, - passive -) { - handler = withMacroTask(handler); - if (once$$1) { handler = createOnceHandler(handler, event, capture); } - target$1.addEventListener( - event, - handler, - supportsPassive - ? { capture: capture, passive: passive } - : capture - ); -} - -function remove$2 ( - event, - handler, - capture, - _target -) { - (_target || target$1).removeEventListener( - event, - handler._withTask || handler, - capture - ); -} - -function updateDOMListeners (oldVnode, vnode) { - if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { - return - } - var on = vnode.data.on || {}; - var oldOn = oldVnode.data.on || {}; - target$1 = vnode.elm; - normalizeEvents(on); - updateListeners(on, oldOn, add$1, remove$2, vnode.context); - target$1 = undefined; -} - -var events = { - create: updateDOMListeners, - update: updateDOMListeners -}; - -/* */ - -function updateDOMProps (oldVnode, vnode) { - if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { - return - } - var key, cur; - var elm = vnode.elm; - var oldProps = oldVnode.data.domProps || {}; - var props = vnode.data.domProps || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(props.__ob__)) { - props = vnode.data.domProps = extend({}, props); - } - - for (key in oldProps) { - if (isUndef(props[key])) { - 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') { - // 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 - var strCur = isUndef(cur) ? '' : String(cur); - if (shouldUpdateValue(elm, strCur)) { - elm.value = strCur; - } - } else { - elm[key] = cur; - } - } -} - -// check platforms/web/util/attrs.js acceptValue - - -function shouldUpdateValue (elm, checkVal) { - return (!elm.composing && ( - elm.tagName === 'OPTION' || - isDirty(elm, checkVal) || - isInputChanged(elm, checkVal) - )) -} - -function isDirty (elm, checkVal) { - // return true when textbox (.number and .trim) loses focus and its value is - // not equal to the updated value - var notInFocus = true; - // #6157 - // work around IE bug when accessing document.activeElement in an iframe - try { notInFocus = document.activeElement !== elm; } catch (e) {} - return notInFocus && elm.value !== checkVal -} - -function isInputChanged (elm, newVal) { - var value = elm.value; - var modifiers = elm._vModifiers; // injected by v-model runtime - if (isDef(modifiers) && modifiers.number) { - return toNumber(value) !== toNumber(newVal) - } - if (isDef(modifiers) && modifiers.trim) { - return value.trim() !== newVal.trim() - } - return value !== newVal -} - -var domProps = { - create: updateDOMProps, - update: updateDOMProps -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -var cssVarRE = /^--/; -var importantRE = /\s*!important$/; -var setProp = function (el, name, val) { - /* istanbul ignore if */ - if (cssVarRE.test(name)) { - el.style.setProperty(name, val); - } else if (importantRE.test(val)) { - el.style.setProperty(name, val.replace(importantRE, ''), 'important'); - } else { - var 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 (var i = 0, len = val.length; i < len; i++) { - el.style[normalizedName] = val[i]; - } - } else { - el.style[normalizedName] = val; - } - } -}; - -var vendorNames = ['Webkit', 'Moz', 'ms']; - -var emptyStyle; -var normalize = cached(function (prop) { - emptyStyle = emptyStyle || document.createElement('div').style; - prop = camelize(prop); - if (prop !== 'filter' && (prop in emptyStyle)) { - return prop - } - var capName = prop.charAt(0).toUpperCase() + prop.slice(1); - for (var i = 0; i < vendorNames.length; i++) { - var name = vendorNames[i] + capName; - if (name in emptyStyle) { - return name - } - } -}); - -function updateStyle (oldVnode, vnode) { - var data = vnode.data; - var oldData = oldVnode.data; - - if (isUndef(data.staticStyle) && isUndef(data.style) && - isUndef(oldData.staticStyle) && isUndef(oldData.style) - ) { - return - } - - var cur, name; - var el = vnode.elm; - var oldStaticStyle = oldData.staticStyle; - var oldStyleBinding = oldData.normalizedStyle || oldData.style || {}; - - // if static style exists, stylebinding already merged into it when doing normalizeStyleData - var oldStyle = oldStaticStyle || oldStyleBinding; - - var 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; - - var newStyle = getStyle(vnode, true); - - for (name in oldStyle) { - if (isUndef(newStyle[name])) { - setProp(el, name, ''); - } - } - for (name in newStyle) { - cur = newStyle[name]; - if (cur !== oldStyle[name]) { - // ie9 setting to null has no effect, must use empty string - setProp(el, name, cur == null ? '' : cur); - } - } -} - -var style = { - create: updateStyle, - update: updateStyle -}; - -/* */ - -/** - * Add class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ -function addClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.add(c); }); - } else { - el.classList.add(cls); - } - } else { - var 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 - */ -function removeClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.remove(c); }); - } else { - el.classList.remove(cls); - } - if (!el.classList.length) { - el.removeAttribute('class'); - } - } else { - var cur = " " + (el.getAttribute('class') || '') + " "; - var tar = ' ' + cls + ' '; - while (cur.indexOf(tar) >= 0) { - cur = cur.replace(tar, ' '); - } - cur = cur.trim(); - if (cur) { - el.setAttribute('class', cur); - } else { - el.removeAttribute('class'); - } - } -} - -/* */ - -function resolveTransition (def) { - if (!def) { - return - } - /* istanbul ignore else */ - if (typeof def === 'object') { - var res = {}; - if (def.css !== false) { - extend(res, autoCssTransition(def.name || 'v')); - } - extend(res, def); - return res - } else if (typeof def === 'string') { - return autoCssTransition(def) - } -} - -var autoCssTransition = cached(function (name) { - return { - enterClass: (name + "-enter"), - enterToClass: (name + "-enter-to"), - enterActiveClass: (name + "-enter-active"), - leaveClass: (name + "-leave"), - leaveToClass: (name + "-leave-to"), - leaveActiveClass: (name + "-leave-active") - } -}); - -var hasTransition = inBrowser && !isIE9; -var TRANSITION = 'transition'; -var ANIMATION = 'animation'; - -// Transition property/event sniffing -var transitionProp = 'transition'; -var transitionEndEvent = 'transitionend'; -var animationProp = 'animation'; -var 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 -var raf = inBrowser - ? window.requestAnimationFrame - ? window.requestAnimationFrame.bind(window) - : setTimeout - : /* istanbul ignore next */ function (fn) { return fn(); }; - -function nextFrame (fn) { - raf(function () { - raf(fn); - }); -} - -function addTransitionClass (el, cls) { - var transitionClasses = el._transitionClasses || (el._transitionClasses = []); - if (transitionClasses.indexOf(cls) < 0) { - transitionClasses.push(cls); - addClass(el, cls); - } -} - -function removeTransitionClass (el, cls) { - if (el._transitionClasses) { - remove(el._transitionClasses, cls); - } - removeClass(el, cls); -} - -function whenTransitionEnds ( - el, - expectedType, - cb -) { - var ref = getTransitionInfo(el, expectedType); - var type = ref.type; - var timeout = ref.timeout; - var propCount = ref.propCount; - if (!type) { return cb() } - var event = type === TRANSITION ? transitionEndEvent : animationEndEvent; - var ended = 0; - var end = function () { - el.removeEventListener(event, onEnd); - cb(); - }; - var onEnd = function (e) { - if (e.target === el) { - if (++ended >= propCount) { - end(); - } - } - }; - setTimeout(function () { - if (ended < propCount) { - end(); - } - }, timeout + 1); - el.addEventListener(event, onEnd); -} - -var transformRE = /\b(transform|all)(,|$)/; - -function getTransitionInfo (el, expectedType) { - var styles = window.getComputedStyle(el); - var transitionDelays = styles[transitionProp + 'Delay'].split(', '); - var transitionDurations = styles[transitionProp + 'Duration'].split(', '); - var transitionTimeout = getTimeout(transitionDelays, transitionDurations); - var animationDelays = styles[animationProp + 'Delay'].split(', '); - var animationDurations = styles[animationProp + 'Duration'].split(', '); - var animationTimeout = getTimeout(animationDelays, animationDurations); - - var type; - var timeout = 0; - var 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; - } - var hasTransform = - type === TRANSITION && - transformRE.test(styles[transitionProp + 'Property']); - return { - type: type, - timeout: timeout, - propCount: propCount, - hasTransform: hasTransform - } -} - -function getTimeout (delays, durations) { - /* istanbul ignore next */ - while (delays.length < durations.length) { - delays = delays.concat(delays); - } - - return Math.max.apply(null, durations.map(function (d, i) { - return toMs(d) + toMs(delays[i]) - })) -} - -function toMs (s) { - return Number(s.slice(0, -1)) * 1000 -} - -/* */ - -function enter (vnode, toggleDisplay) { - var el = vnode.elm; - - // call leave callback now - if (isDef(el._leaveCb)) { - el._leaveCb.cancelled = true; - el._leaveCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return - } - - /* istanbul ignore if */ - if (isDef(el._enterCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var enterClass = data.enterClass; - var enterToClass = data.enterToClass; - var enterActiveClass = data.enterActiveClass; - var appearClass = data.appearClass; - var appearToClass = data.appearToClass; - var appearActiveClass = data.appearActiveClass; - var beforeEnter = data.beforeEnter; - var enter = data.enter; - var afterEnter = data.afterEnter; - var enterCancelled = data.enterCancelled; - var beforeAppear = data.beforeAppear; - var appear = data.appear; - var afterAppear = data.afterAppear; - var appearCancelled = data.appearCancelled; - var duration = data.duration; - - // 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. - var context = activeInstance; - var transitionNode = activeInstance.$vnode; - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent; - context = transitionNode.context; - } - - var isAppear = !context._isMounted || !vnode.isRootInsert; - - if (isAppear && !appear && appear !== '') { - return - } - - var startClass = isAppear && appearClass - ? appearClass - : enterClass; - var activeClass = isAppear && appearActiveClass - ? appearActiveClass - : enterActiveClass; - var toClass = isAppear && appearToClass - ? appearToClass - : enterToClass; - - var beforeEnterHook = isAppear - ? (beforeAppear || beforeEnter) - : beforeEnter; - var enterHook = isAppear - ? (typeof appear === 'function' ? appear : enter) - : enter; - var afterEnterHook = isAppear - ? (afterAppear || afterEnter) - : afterEnter; - var enterCancelledHook = isAppear - ? (appearCancelled || enterCancelled) - : enterCancelled; - - var explicitEnterDuration = toNumber( - isObject(duration) - ? duration.enter - : duration - ); - - if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) { - checkDuration(explicitEnterDuration, 'enter', vnode); - } - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(enterHook); - - var cb = el._enterCb = once(function () { - if (expectsCSS) { - removeTransitionClass(el, toClass); - removeTransitionClass(el, activeClass); - } - 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', function () { - var parent = el.parentNode; - var 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(function () { - addTransitionClass(el, toClass); - removeTransitionClass(el, startClass); - if (!cb.cancelled && !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(); - } -} - -function leave (vnode, rm) { - var el = vnode.elm; - - // call enter callback now - if (isDef(el._enterCb)) { - el._enterCb.cancelled = true; - el._enterCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return rm() - } - - /* istanbul ignore if */ - if (isDef(el._leaveCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var leaveClass = data.leaveClass; - var leaveToClass = data.leaveToClass; - var leaveActiveClass = data.leaveActiveClass; - var beforeLeave = data.beforeLeave; - var leave = data.leave; - var afterLeave = data.afterLeave; - var leaveCancelled = data.leaveCancelled; - var delayLeave = data.delayLeave; - var duration = data.duration; - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(leave); - - var explicitLeaveDuration = toNumber( - isObject(duration) - ? duration.leave - : duration - ); - - if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) { - checkDuration(explicitLeaveDuration, 'leave', vnode); - } - - var cb = el._leaveCb = once(function () { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null; - } - if (expectsCSS) { - removeTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveActiveClass); - } - 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 - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode; - } - beforeLeave && beforeLeave(el); - if (expectsCSS) { - addTransitionClass(el, leaveClass); - addTransitionClass(el, leaveActiveClass); - nextFrame(function () { - addTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveClass); - if (!cb.cancelled && !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) { - if (isUndef(fn)) { - return false - } - var invokerFns = fn.fns; - if (isDef(invokerFns)) { - // invoker - return getHookArgumentsLength( - Array.isArray(invokerFns) - ? invokerFns[0] - : invokerFns - ) - } else { - return (fn._length || fn.length) > 1 - } -} - -function _enter (_, vnode) { - if (vnode.data.show !== true) { - enter(vnode); - } -} - -var transition = inBrowser ? { - create: _enter, - activate: _enter, - remove: function remove$$1 (vnode, rm) { - /* istanbul ignore else */ - if (vnode.data.show !== true) { - leave(vnode, rm); - } else { - rm(); - } - } -} : {}; - -var platformModules = [ - attrs, - klass, - events, - domProps, - style, - transition -]; - -/* */ - -// the directive module should be applied last, after all -// built-in modules have been applied. -var modules = platformModules.concat(baseModules); - -var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules }); - -/** - * Not type checking this file because flow doesn't like attaching - * properties to Elements. - */ - -/* istanbul ignore if */ -if (isIE9) { - // http://www.matts411.com/post/internet-explorer-9-oninput/ - document.addEventListener('selectionchange', function () { - var el = document.activeElement; - if (el && el.vmodel) { - trigger(el, 'input'); - } - }); -} - -var directive = { - inserted: function inserted (el, binding, vnode, oldVnode) { - if (vnode.tag === 'select') { - // #6903 - if (oldVnode.elm && !oldVnode.elm._vOptions) { - mergeVNodeHook(vnode, 'postpatch', function () { - 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) { - // 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); - if (!isAndroid) { - el.addEventListener('compositionstart', onCompositionStart); - el.addEventListener('compositionend', onCompositionEnd); - } - /* istanbul ignore if */ - if (isIE9) { - el.vmodel = true; - } - } - } - }, - - componentUpdated: function 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. - var prevOptions = el._vOptions; - var curOptions = el._vOptions = [].map.call(el.options, getValue); - if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) { - // trigger change event if - // no matching option found for at least one value - var needReset = el.multiple - ? binding.value.some(function (v) { return 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(function () { - actuallySetSelected(el, binding, vm); - }, 0); - } -} - -function actuallySetSelected (el, binding, vm) { - var value = binding.value; - var isMultiple = el.multiple; - if (isMultiple && !Array.isArray(value)) { - process.env.NODE_ENV !== 'production' && 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 - } - var selected, option; - for (var 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(function (o) { return !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) { - var e = document.createEvent('HTMLEvents'); - e.initEvent(type, true, true); - el.dispatchEvent(e); -} - -/* */ - -// recursively search for possible transition defined inside the component root -function locateNode (vnode) { - return vnode.componentInstance && (!vnode.data || !vnode.data.transition) - ? locateNode(vnode.componentInstance._vnode) - : vnode -} - -var show = { - bind: function bind (el, ref, vnode) { - var value = ref.value; - - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - var originalDisplay = el.__vOriginalDisplay = - el.style.display === 'none' ? '' : el.style.display; - if (value && transition$$1) { - vnode.data.show = true; - enter(vnode, function () { - el.style.display = originalDisplay; - }); - } else { - el.style.display = value ? originalDisplay : 'none'; - } - }, - - update: function update (el, ref, vnode) { - var value = ref.value; - var oldValue = ref.oldValue; - - /* istanbul ignore if */ - if (value === oldValue) { return } - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - if (transition$$1) { - vnode.data.show = true; - if (value) { - enter(vnode, function () { - el.style.display = el.__vOriginalDisplay; - }); - } else { - leave(vnode, function () { - el.style.display = 'none'; - }); - } - } else { - el.style.display = value ? el.__vOriginalDisplay : 'none'; - } - }, - - unbind: function unbind ( - el, - binding, - vnode, - oldVnode, - isDestroy - ) { - if (!isDestroy) { - el.style.display = el.__vOriginalDisplay; - } - } -}; - -var platformDirectives = { - model: directive, - show: show -}; - -/* */ - -// Provides transition support for a single element/component. -// supports transition mode (out-in / in-out) - -var 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) { - var compOptions = vnode && vnode.componentOptions; - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -function extractTransitionData (comp) { - var data = {}; - var options = comp.$options; - // props - for (var key in options.propsData) { - data[key] = comp[key]; - } - // events. - // extract listeners and pass them directly to the transition methods - var listeners = options._parentListeners; - for (var key$1 in listeners) { - data[camelize(key$1)] = listeners[key$1]; - } - return data -} - -function placeholder (h, rawChild) { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode) { - while ((vnode = vnode.parent)) { - if (vnode.data.transition) { - return true - } - } -} - -function isSameChild (child, oldChild) { - return oldChild.key === child.key && oldChild.tag === child.tag -} - -var Transition = { - name: 'transition', - props: transitionProps, - abstract: true, - - render: function render (h) { - var this$1 = this; - - var children = this.$options._renderChildren; - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if (process.env.NODE_ENV !== 'production' && children.length > 1) { - warn( - '<transition> can only be used on a single element. Use ' + - '<transition-group> for lists.', - this.$parent - ); - } - - var mode = this.mode; - - // warn invalid mode - if (process.env.NODE_ENV !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in' - ) { - warn( - 'invalid <transition> mode: ' + mode, - this.$parent - ); - } - - var rawChild = 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 - var 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. - var id = "__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; - - var data = (child.data || (child.data = {})).transition = extractTransitionData(this); - var oldRawChild = this._vnode; - var 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(function (d) { return d.name === 'show'; })) { - child.data.show = true; - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // replace old child transition data with fresh one - // important for dynamic transitions! - var oldData = 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', function () { - this$1._leaving = false; - this$1.$forceUpdate(); - }); - return placeholder(h, rawChild) - } else if (mode === 'in-out') { - if (isAsyncPlaceholder(child)) { - return oldRawChild - } - var delayedLeave; - var performLeave = function () { delayedLeave(); }; - mergeVNodeHook(data, 'afterEnter', performLeave); - mergeVNodeHook(data, 'enterCancelled', performLeave); - mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; }); - } - } - - return rawChild - } -}; - -/* */ - -// 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. - -var props = extend({ - tag: String, - moveClass: String -}, transitionProps); - -delete props.mode; - -var TransitionGroup = { - props: props, - - render: function render (h) { - var tag = this.tag || this.$vnode.data.tag || 'span'; - var map = Object.create(null); - var prevChildren = this.prevChildren = this.children; - var rawChildren = this.$slots.default || []; - var children = this.children = []; - var transitionData = extractTransitionData(this); - - for (var i = 0; i < rawChildren.length; i++) { - var c = 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 (process.env.NODE_ENV !== 'production') { - var opts = c.componentOptions; - var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag; - warn(("<transition-group> children must be keyed: <" + name + ">")); - } - } - } - - if (prevChildren) { - var kept = []; - var removed = []; - for (var i$1 = 0; i$1 < prevChildren.length; i$1++) { - var c$1 = prevChildren[i$1]; - c$1.data.transition = transitionData; - c$1.data.pos = c$1.elm.getBoundingClientRect(); - if (map[c$1.key]) { - kept.push(c$1); - } else { - removed.push(c$1); - } - } - this.kept = h(tag, null, kept); - this.removed = removed; - } - - return h(tag, null, children) - }, - - beforeUpdate: function beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ); - this._vnode = this.kept; - }, - - updated: function updated () { - var children = this.prevChildren; - var moveClass = 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(function (c) { - if (c.data.moved) { - var el = c.elm; - var s = el.style; - addTransitionClass(el, moveClass); - s.transform = s.WebkitTransform = s.transitionDuration = ''; - el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { - if (!e || /transform$/.test(e.propertyName)) { - el.removeEventListener(transitionEndEvent, cb); - el._moveCb = null; - removeTransitionClass(el, moveClass); - } - }); - } - }); - }, - - methods: { - hasMove: function hasMove (el, moveClass) { - /* 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. - var clone = el.cloneNode(); - if (el._transitionClasses) { - el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); }); - } - addClass(clone, moveClass); - clone.style.display = 'none'; - this.$el.appendChild(clone); - var info = getTransitionInfo(clone); - this.$el.removeChild(clone); - return (this._hasMove = info.hasTransform) - } - } -}; - -function callPendingCbs (c) { - /* istanbul ignore if */ - if (c.elm._moveCb) { - c.elm._moveCb(); - } - /* istanbul ignore if */ - if (c.elm._enterCb) { - c.elm._enterCb(); - } -} - -function recordPosition (c) { - c.data.newPos = c.elm.getBoundingClientRect(); -} - -function applyTranslation (c) { - var oldPos = c.data.pos; - var newPos = c.data.newPos; - var dx = oldPos.left - newPos.left; - var dy = oldPos.top - newPos.top; - if (dx || dy) { - c.data.moved = true; - var s = c.elm.style; - s.transform = s.WebkitTransform = "translate(" + dx + "px," + dy + "px)"; - s.transitionDuration = '0s'; - } -} - -var platformComponents = { - Transition: Transition, - TransitionGroup: TransitionGroup -}; - -/* */ - -// install platform specific utils -Vue$3.config.mustUseProp = mustUseProp; -Vue$3.config.isReservedTag = isReservedTag; -Vue$3.config.isReservedAttr = isReservedAttr; -Vue$3.config.getTagNamespace = getTagNamespace; -Vue$3.config.isUnknownElement = isUnknownElement; - -// install platform runtime directives & components -extend(Vue$3.options.directives, platformDirectives); -extend(Vue$3.options.components, platformComponents); - -// install platform patch function -Vue$3.prototype.__patch__ = inBrowser ? patch : noop; - -// public mount method -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && inBrowser ? query(el) : undefined; - return mountComponent(this, el, hydrating) -}; - -// devtools global hook -/* istanbul ignore next */ -Vue$3.nextTick(function () { - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue$3); - } else if (process.env.NODE_ENV !== 'production' && isChrome) { - console[console.info ? 'info' : 'log']( - 'Download the Vue Devtools extension for a better development experience:\n' + - 'https://github.com/vuejs/vue-devtools' - ); - } - } - if (process.env.NODE_ENV !== 'production' && - config.productionTip !== false && - inBrowser && typeof console !== 'undefined' - ) { - 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); - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - if (process.env.NODE_ENV !== 'production' && staticClass) { - var expression = parseText(staticClass, options.delimiters); - if (expression) { - 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">.' - ); - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass); - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } -} - -function genData (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -var klass$1 = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData -}; - -/* */ - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - if (staticStyle) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(staticStyle, options.delimiters); - if (expression) { - 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.staticStyle = JSON.stringify(parseStyleText(staticStyle)); - } - - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:(" + (el.styleBinding) + "),"; - } - return data -} - -var style$1 = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$1 -}; - -/* */ - -var decoder; - -var he = { - decode: function decode (html) { - decoder = decoder || document.createElement('div'); - decoder.innerHTML = html; - return decoder.textContent - } -}; - -/* */ - -var 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) -var 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 -var 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' -); - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if (process.env.NODE_ENV !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href' - ? options.shouldDecodeNewlinesForHref - : options.shouldDecodeNewlines; - attrs[i] = { - name: args[1], - value: decodeAttr(value, shouldDecodeNewlines) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if (process.env.NODE_ENV !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn$2; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn$2 = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn$2(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn$2, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - process.env.NODE_ENV !== 'production' && warn$2( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - function checkRootConstraints (el) { - if (process.env.NODE_ENV !== 'production') { - if (el.tag === 'slot' || el.tag === 'template') { - warnOnce( - "Cannot use <" + (el.tag) + "> as component root element because it may " + - 'contain multiple nodes.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else if (process.env.NODE_ENV !== 'production') { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - if (process.env.NODE_ENV !== 'production') { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if (process.env.NODE_ENV !== 'production' && el.tag === 'template') { - warn$2("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - process.env.NODE_ENV !== 'production' && warn$2( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else if (process.env.NODE_ENV !== 'production') { - warn$2( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if (process.env.NODE_ENV !== 'production' && children[i].text !== ' ') { - warn$2( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if (process.env.NODE_ENV !== 'production' && el.key) { - warn$2( - "`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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && slotScope) { - warn$2( - "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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // 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); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn$2); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if (process.env.NODE_ENV !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(value, delimiters); - if (expression) { - warn$2( - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - // #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'); - } - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - process.env.NODE_ENV !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn$2('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _el = el; - while (_el) { - if (_el.for && _el.alias === value) { - warn$2( - "<" + (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 = _el.parent; - } -} - -/* */ - -/** - * Expand input[v-model] with dyanmic 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]"> - */ - -function preTransformNode (el, options) { - if (el.tag === 'input') { - var map = el.attrsMap; - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - var typeBinding = getBindingAttr(el, 'type'); - var ifCondition = getAndRemoveAttr(el, 'v-if', true); - var ifConditionExtra = ifCondition ? ("&&(" + ifCondition + ")") : ""; - var hasElse = getAndRemoveAttr(el, 'v-else', true) != null; - var elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true); - // 1. checkbox - var 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 - var 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 - var 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value; - el.attrsList.push({ name: name, value: value }); -} - -var model$2 = { - preTransformNode: preTransformNode -}; - -var modules$1 = [ - klass$1, - style$1, - model$2 -]; - -/* */ - -function text (el, dir) { - if (dir.value) { - addProp(el, 'textContent', ("_s(" + (dir.value) + ")")); - } -} - -/* */ - -function html (el, dir) { - if (dir.value) { - addProp(el, 'innerHTML', ("_s(" + (dir.value) + ")")); - } -} - -var directives$1 = { - model: model, - text: text, - html: html -}; - -/* */ - -var baseOptions = { - expectHTML: true, - modules: modules$1, - directives: directives$1, - isPreTag: isPreTag, - isUnaryTag: isUnaryTag, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - staticKeys: genStaticKeys(modules$1) -}; - -/* */ - -var isStaticKey; -var isPlatformReservedTag; - -var genStaticKeysCached = cached(genStaticKeys$1); - -/** - * 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. - */ -function optimize (root, options) { - if (!root) { return } - isStaticKey = genStaticKeysCached(options.staticKeys || ''); - isPlatformReservedTag = options.isReservedTag || no; - // first pass: mark all non-static nodes. - markStatic$1(root); - // second pass: mark static roots. - markStaticRoots(root, false); -} - -function genStaticKeys$1 (keys) { - return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + - (keys ? ',' + keys : '') - ) -} - -function markStatic$1 (node) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - markStatic$1(child); - if (!child.static) { - node.static = false; - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].block; - markStatic$1(block); - if (!block.static) { - node.static = false; - } - } - } - } -} - -function markStaticRoots (node, isInFor) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - markStaticRoots(node.children[i], isInFor || !!node.for); - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - markStaticRoots(node.ifConditions[i$1].block, isInFor); - } - } - } -} - -function isStatic (node) { - 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) { - while (node.parent) { - node = node.parent; - if (node.tag !== 'template') { - return false - } - if (node.for) { - return true - } - } - return false -} - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if (process.env.NODE_ENV !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -function on (el, dir) { - if (process.env.NODE_ENV !== 'production' && dir.modifiers) { - warn("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData$2(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - process.env.NODE_ENV !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if (process.env.NODE_ENV !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData$2 (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if (process.env.NODE_ENV !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn; - delete options.warn; - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - if (process.env.NODE_ENV !== 'production') { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 (process.env.NODE_ENV !== 'production') { - if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -// `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. -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref$1 = createCompiler(baseOptions); -var compileToFunctions = ref$1.compileToFunctions; - -/* */ - -// check whether current browser encodes a char inside attribute values -var div; -function getShouldDecode (href) { - div = div || document.createElement('div'); - div.innerHTML = href ? "<a href=\"\n\"/>" : "<div a=\"\n\"/>"; - return div.innerHTML.indexOf(' ') > 0 -} - -// #3663: IE encodes newlines inside attribute values while other browsers don't -var shouldDecodeNewlines = inBrowser ? getShouldDecode(false) : false; -// #6828: chrome encodes content in a[href] -var shouldDecodeNewlinesForHref = inBrowser ? getShouldDecode(true) : false; - -/* */ - -var idToTemplate = cached(function (id) { - var el = query(id); - return el && el.innerHTML -}); - -var mount = Vue$3.prototype.$mount; -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && query(el); - - /* istanbul ignore if */ - if (el === document.body || el === document.documentElement) { - process.env.NODE_ENV !== 'production' && warn( - "Do not mount Vue to <html> or <body> - mount to normal elements instead." - ); - return this - } - - var options = this.$options; - // resolve template/el and convert to render function - if (!options.render) { - var template = options.template; - if (template) { - if (typeof template === 'string') { - if (template.charAt(0) === '#') { - template = idToTemplate(template); - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !template) { - warn( - ("Template element not found or is empty: " + (options.template)), - this - ); - } - } - } else if (template.nodeType) { - template = template.innerHTML; - } else { - if (process.env.NODE_ENV !== 'production') { - warn('invalid template option:' + template, this); - } - return this - } - } else if (el) { - template = getOuterHTML(el); - } - if (template) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - mark('compile'); - } - - var ref = compileToFunctions(template, { - shouldDecodeNewlines: shouldDecodeNewlines, - shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref, - delimiters: options.delimiters, - comments: options.comments - }, this); - var render = ref.render; - var staticRenderFns = ref.staticRenderFns; - options.render = render; - options.staticRenderFns = staticRenderFns; - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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) { - if (el.outerHTML) { - return el.outerHTML - } else { - var container = document.createElement('div'); - container.appendChild(el.cloneNode(true)); - return container.innerHTML - } + module.exports = require('./vue.common.dev.js') } - -Vue$3.compile = compileToFunctions; - -module.exports = Vue$3; diff --git a/dist/vue.esm.js b/dist/vue.esm.js deleted file mode 100644 index b623498876a..00000000000 --- a/dist/vue.esm.js +++ /dev/null @@ -1,10613 +0,0 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 Evan You - * Released under the MIT License. - */ -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real 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. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - 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) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else if (process.env.NODE_ENV !== 'production') { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else if (process.env.NODE_ENV !== 'production') { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if (process.env.NODE_ENV !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (process.env.NODE_ENV !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -var mark; -var measure; - -if (process.env.NODE_ENV !== 'production') { - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -if (process.env.NODE_ENV !== 'production') { - var 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,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var 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; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - if (process.env.NODE_ENV !== 'production') { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - process.env.NODE_ENV !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - process.env.NODE_ENV !== 'production' - ? ("timeout (" + (res.timeout) + "ms)") - : null - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - -function getFirstComponentChild (children) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var c = children[i]; - if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { - return c - } - } - } -} - -/* */ - -/* */ - -function initEvents (vm) { - vm._events = Object.create(null); - vm._hasHookEvent = false; - // init parent attached events - var listeners = vm.$options._parentListeners; - if (listeners) { - updateComponentListeners(vm, listeners); - } -} - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -function eventsMixin (Vue) { - var hookRE = /^hook:/; - Vue.prototype.$on = function (event, fn) { - var this$1 = this; - - var vm = this; - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$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, fn) { - var vm = 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, fn) { - var this$1 = this; - - var vm = this; - // all - if (!arguments.length) { - vm._events = Object.create(null); - return vm - } - // array of events - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$off(event[i], fn); - } - return vm - } - // specific event - var cbs = vm._events[event]; - if (!cbs) { - return vm - } - if (!fn) { - vm._events[event] = null; - return vm - } - if (fn) { - // specific handler - var cb; - var i$1 = cbs.length; - while (i$1--) { - cb = cbs[i$1]; - if (cb === fn || cb.fn === fn) { - cbs.splice(i$1, 1); - break - } - } - } - return vm - }; - - Vue.prototype.$emit = function (event) { - var vm = this; - if (process.env.NODE_ENV !== 'production') { - var 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 + "\"." - ); - } - } - var cbs = vm._events[event]; - if (cbs) { - cbs = cbs.length > 1 ? toArray(cbs) : cbs; - var args = toArray(arguments, 1); - for (var i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args); - } catch (e) { - handleError(e, vm, ("event handler for \"" + event + "\"")); - } - } - } - return vm - }; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; -var isUpdatingChildComponent = false; - -function initLifecycle (vm) { - var options = vm.$options; - - // locate first non-abstract parent - var 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._watcher = null; - vm._inactive = null; - vm._directInactive = false; - vm._isMounted = false; - vm._isDestroyed = false; - vm._isBeingDestroyed = false; -} - -function lifecycleMixin (Vue) { - Vue.prototype._update = function (vnode, hydrating) { - var vm = this; - if (vm._isMounted) { - callHook(vm, 'beforeUpdate'); - } - var prevEl = vm.$el; - var prevVnode = vm._vnode; - var prevActiveInstance = activeInstance; - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ); - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null; - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode); - } - activeInstance = prevActiveInstance; - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el; - } - // updated hook is called by the scheduler to ensure that children are - // updated in a parent's updated hook. - }; - - Vue.prototype.$forceUpdate = function () { - var vm = this; - if (vm._watcher) { - vm._watcher.update(); - } - }; - - Vue.prototype.$destroy = function () { - var vm = this; - if (vm._isBeingDestroyed) { - return - } - callHook(vm, 'beforeDestroy'); - vm._isBeingDestroyed = true; - // remove self from parent - var parent = vm.$parent; - if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { - remove(parent.$children, vm); - } - // teardown watchers - if (vm._watcher) { - vm._watcher.teardown(); - } - var i = vm._watchers.length; - while (i--) { - vm._watchers[i].teardown(); - } - // 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; - } - }; -} - -function mountComponent ( - vm, - el, - hydrating -) { - vm.$el = el; - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode; - if (process.env.NODE_ENV !== 'production') { - /* 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'); - - var updateComponent; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - updateComponent = function () { - var name = vm._name; - var id = vm._uid; - var startTag = "vue-perf-start:" + id; - var endTag = "vue-perf-end:" + id; - - mark(startTag); - var 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 = function () { - vm._update(vm._render(), hydrating); - }; - } - - vm._watcher = new Watcher(vm, updateComponent, noop); - hydrating = false; - - // 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 -} - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = true; - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = false; - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index = queue.length = activatedChildren.length = 0; - has = {}; - if (process.env.NODE_ENV !== 'production') { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // 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]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var 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; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = process.env.NODE_ENV !== 'production' - ? expOrFn.toString() - : ''; - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - process.env.NODE_ENV !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var sharedPropertyDefinition = { - enumerable: true, - configurable: true, - get: noop, - set: noop -}; - -function proxy (target, sourceKey, key) { - sharedPropertyDefinition.get = function proxyGetter () { - return this[sourceKey][key] - }; - sharedPropertyDefinition.set = function proxySetter (val) { - this[sourceKey][key] = val; - }; - Object.defineProperty(target, key, sharedPropertyDefinition); -} - -function initState (vm) { - vm._watchers = []; - var opts = vm.$options; - if (opts.props) { initProps(vm, opts.props); } - if (opts.methods) { initMethods(vm, opts.methods); } - if (opts.data) { - initData(vm); - } else { - observe(vm._data = {}, true /* asRootData */); - } - if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch && opts.watch !== nativeWatch) { - initWatch(vm, opts.watch); - } -} - -function initProps (vm, propsOptions) { - var propsData = vm.$options.propsData || {}; - var props = vm._props = {}; - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - var keys = vm.$options._propKeys = []; - var isRoot = !vm.$parent; - // root instance props should be converted - observerState.shouldConvert = isRoot; - var loop = function ( key ) { - keys.push(key); - var value = validateProp(key, propsOptions, propsData, vm); - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - var 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, function () { - if (vm.$parent && !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 - ); - } - }); - } else { - defineReactive(props, key, value); - } - // 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); - } - }; - - for (var key in propsOptions) loop( key ); - observerState.shouldConvert = true; -} - -function initData (vm) { - var data = vm.$options.data; - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {}; - if (!isPlainObject(data)) { - data = {}; - process.env.NODE_ENV !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', - vm - ); - } - // proxy data on instance - var keys = Object.keys(data); - var props = vm.$options.props; - var methods = vm.$options.methods; - var i = keys.length; - while (i--) { - var key = keys[i]; - if (process.env.NODE_ENV !== 'production') { - if (methods && hasOwn(methods, key)) { - warn( - ("Method \"" + key + "\" has already been defined as a data property."), - vm - ); - } - } - if (props && hasOwn(props, key)) { - process.env.NODE_ENV !== 'production' && 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 - observe(data, true /* asRootData */); -} - -function getData (data, vm) { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, "data()"); - return {} - } -} - -var computedWatcherOptions = { lazy: true }; - -function initComputed (vm, computed) { - var watchers = vm._computedWatchers = Object.create(null); - // computed properties are just getters during SSR - var isSSR = isServerRendering(); - - for (var key in computed) { - var userDef = computed[key]; - var getter = typeof userDef === 'function' ? userDef : userDef.get; - if (process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production') { - 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); - } - } - } -} - -function defineComputed ( - target, - key, - userDef -) { - var shouldCache = !isServerRendering(); - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef; - sharedPropertyDefinition.set = noop; - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop; - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop; - } - if (process.env.NODE_ENV !== 'production' && - 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 () { - var watcher = this._computedWatchers && this._computedWatchers[key]; - if (watcher) { - if (watcher.dirty) { - watcher.evaluate(); - } - if (Dep.target) { - watcher.depend(); - } - return watcher.value - } - } -} - -function initMethods (vm, methods) { - var props = vm.$options.props; - for (var key in methods) { - if (process.env.NODE_ENV !== 'production') { - if (methods[key] == null) { - warn( - "Method \"" + key + "\" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm); - } -} - -function initWatch (vm, watch) { - for (var key in watch) { - var handler = watch[key]; - if (Array.isArray(handler)) { - for (var i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]); - } - } else { - createWatcher(vm, key, handler); - } - } -} - -function createWatcher ( - vm, - keyOrFn, - handler, - options -) { - if (isPlainObject(handler)) { - options = handler; - handler = handler.handler; - } - if (typeof handler === 'string') { - handler = vm[handler]; - } - return vm.$watch(keyOrFn, handler, options) -} - -function stateMixin (Vue) { - // flow somehow has problems with directly declared definition object - // when using Object.defineProperty, so we have to procedurally build up - // the object here. - var dataDef = {}; - dataDef.get = function () { return this._data }; - var propsDef = {}; - propsDef.get = function () { return this._props }; - if (process.env.NODE_ENV !== 'production') { - dataDef.set = function (newData) { - 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, - cb, - options - ) { - var vm = this; - if (isPlainObject(cb)) { - return createWatcher(vm, expOrFn, cb, options) - } - options = options || {}; - options.user = true; - var watcher = new Watcher(vm, expOrFn, cb, options); - if (options.immediate) { - cb.call(vm, watcher.value); - } - return function unwatchFn () { - watcher.teardown(); - } - }; -} - -/* */ - -function initProvide (vm) { - var provide = vm.$options.provide; - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide; - } -} - -function initInjections (vm) { - var result = resolveInject(vm.$options.inject, vm); - if (result) { - observerState.shouldConvert = false; - Object.keys(result).forEach(function (key) { - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, key, result[key], function () { - 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]); - } - }); - observerState.shouldConvert = true; - } -} - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else if (process.env.NODE_ENV !== 'production') { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - if (process.env.NODE_ENV !== 'production') { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -function initRender (vm) { - vm._vnode = null; // the root of the child tree - var options = vm.$options; - var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree - var renderContext = parentVnode && parentVnode.context; - vm.$slots = resolveSlots(options._renderChildren, renderContext); - vm.$scopedSlots = 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 - vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; - // normalization is always applied for the public version, used in - // user-written render functions. - vm.$createElement = function (a, b, c, d) { return 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 - var parentData = parentVnode && parentVnode.data; - - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () { - !isUpdatingChildComponent && warn("$attrs is readonly.", vm); - }, true); - defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () { - !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); - } -} - -function renderMixin (Vue) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype); - - Vue.prototype.$nextTick = function (fn) { - return nextTick(fn, this) - }; - - Vue.prototype._render = function () { - var vm = this; - var ref = vm.$options; - var render = ref.render; - var _parentVnode = ref._parentVnode; - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (var key in vm.$slots) { - var slot = vm.$slots[key]; - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */); - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode; - // render self - var vnode; - try { - vnode = render.call(vm._renderProxy, vm.$createElement); - } catch (e) { - handleError(e, vm, "render"); - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); - } catch (e) { - handleError(e, vm, "renderError"); - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if (process.env.NODE_ENV !== 'production' && Array.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 - }; -} - -/* */ - -var uid$1 = 0; - -function initMixin (Vue) { - Vue.prototype._init = function (options) { - var vm = this; - // a uid - vm._uid = uid$1++; - - var startTag, endTag; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - startTag = "vue-perf-start:" + (vm._uid); - endTag = "vue-perf-end:" + (vm._uid); - mark(startTag); - } - - // a flag to avoid this being observed - vm._isVue = 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); - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ); - } - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - initProxy(vm); - } else { - vm._renderProxy = vm; - } - // expose real self - vm._self = vm; - initLifecycle(vm); - initEvents(vm); - initRender(vm); - callHook(vm, 'beforeCreate'); - initInjections(vm); // resolve injections before data/props - initState(vm); - initProvide(vm); // resolve provide after data/props - callHook(vm, 'created'); - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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); - } - }; -} - -function initInternalComponent (vm, options) { - var opts = vm.$options = Object.create(vm.constructor.options); - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent; - opts.propsData = options.propsData; - opts._parentVnode = options._parentVnode; - opts._parentListeners = options._parentListeners; - opts._renderChildren = options._renderChildren; - opts._componentTag = options._componentTag; - opts._parentElm = options._parentElm; - opts._refElm = options._refElm; - if (options.render) { - opts.render = options.render; - opts.staticRenderFns = options.staticRenderFns; - } -} - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -function Vue$3 (options) { - if (process.env.NODE_ENV !== 'production' && - !(this instanceof Vue$3) - ) { - warn('Vue is a constructor and should be called with the `new` keyword'); - } - this._init(options); -} - -initMixin(Vue$3); -stateMixin(Vue$3); -eventsMixin(Vue$3); -lifecycleMixin(Vue$3); -renderMixin(Vue$3); - -/* */ - -function initUse (Vue) { - Vue.use = function (plugin) { - var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - var args = toArray(arguments, 1); - args.unshift(this); - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args); - } else if (typeof plugin === 'function') { - plugin.apply(null, args); - } - installedPlugins.push(plugin); - return this - }; -} - -/* */ - -function initMixin$1 (Vue) { - Vue.mixin = function (mixin) { - this.options = mergeOptions(this.options, mixin); - return this - }; -} - -/* */ - -function initExtend (Vue) { - /** - * 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 - */ - Vue.extend = function (extendOptions) { - extendOptions = extendOptions || {}; - var Super = this; - var SuperId = Super.cid; - var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - 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 characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - } - - var Sub = function VueComponent (options) { - this._init(options); - }; - 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$1(Sub); - } - if (Sub.options.computed) { - initComputed$1(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$1 (Comp) { - var props = Comp.options.props; - for (var key in props) { - proxy(Comp.prototype, "_props", key); - } -} - -function initComputed$1 (Comp) { - var computed = Comp.options.computed; - for (var key in computed) { - defineComputed(Comp.prototype, key, computed[key]); - } -} - -/* */ - -function initAssetRegisters (Vue) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.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' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ); - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id; - definition = this.options._base.extend(definition); - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition }; - } - this.options[type + 's'][id] = definition; - return definition - } - }; - }); -} - -/* */ - -function getComponentName (opts) { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern, name) { - if (Array.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, filter) { - var cache = keepAliveInstance.cache; - var keys = keepAliveInstance.keys; - var _vnode = keepAliveInstance._vnode; - for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode); - } - } - } -} - -function pruneCacheEntry ( - cache, - key, - keys, - current -) { - var cached$$1 = cache[key]; - if (cached$$1 && cached$$1 !== current) { - cached$$1.componentInstance.$destroy(); - } - cache[key] = null; - remove(keys, key); -} - -var patternTypes = [String, RegExp, Array]; - -var KeepAlive = { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created: function created () { - this.cache = Object.create(null); - this.keys = []; - }, - - destroyed: function destroyed () { - var this$1 = this; - - for (var key in this$1.cache) { - pruneCacheEntry(this$1.cache, key, this$1.keys); - } - }, - - watch: { - include: function include (val) { - pruneCache(this, function (name) { return matches(val, name); }); - }, - exclude: function exclude (val) { - pruneCache(this, function (name) { return !matches(val, name); }); - } - }, - - render: function render () { - var vnode = getFirstComponentChild(this.$slots.default); - var componentOptions = vnode && vnode.componentOptions; - if (componentOptions) { - // check pattern - var name = getComponentName(componentOptions); - if (name && ( - (this.exclude && matches(this.exclude, name)) || - (this.include && !matches(this.include, name)) - )) { - return vnode - } - - var ref = this; - var cache = ref.cache; - var keys = ref.keys; - var 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 { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } - } - - vnode.data.keepAlive = true; - } - return vnode - } -}; - -var builtInComponents = { - KeepAlive: KeepAlive -}; - -/* */ - -function initGlobalAPI (Vue) { - // config - var configDef = {}; - configDef.get = function () { return config; }; - if (process.env.NODE_ENV !== 'production') { - configDef.set = function () { - 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: warn, - extend: extend, - mergeOptions: mergeOptions, - defineReactive: defineReactive - }; - - Vue.set = set; - Vue.delete = del; - Vue.nextTick = nextTick; - - Vue.options = Object.create(null); - ASSET_TYPES.forEach(function (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$1(Vue); - initExtend(Vue); - initAssetRegisters(Vue); -} - -initGlobalAPI(Vue$3); - -Object.defineProperty(Vue$3.prototype, '$isServer', { - get: isServerRendering -}); - -Object.defineProperty(Vue$3.prototype, '$ssrContext', { - get: function get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}); - -Vue$3.version = '2.5.3'; - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -var xlinkNS = 'http://www.w3.org/1999/xlink'; - -var isXlink = function (name) { - return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' -}; - -var getXlinkProp = function (name) { - return isXlink(name) ? name.slice(6, name.length) : '' -}; - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - -var namespaceMap = { - svg: 'http://www.w3.org/2000/svg', - math: 'http://www.w3.org/1998/Math/MathML' -}; - -var 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. -var 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 -); - -var isPreTag = function (tag) { return tag === 'pre'; }; - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - -var unknownElementCache = Object.create(null); -function isUnknownElement (tag) { - /* 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] - } - var el = document.createElement(tag); - if (tag.indexOf('-') > -1) { - // http://stackoverflow.com/a/28210364/1070244 - return (unknownElementCache[tag] = ( - el.constructor === window.HTMLUnknownElement || - el.constructor === window.HTMLElement - )) - } else { - return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString())) - } -} - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ -function query (el) { - if (typeof el === 'string') { - var selected = document.querySelector(el); - if (!selected) { - process.env.NODE_ENV !== 'production' && warn( - 'Cannot find element: ' + el - ); - return document.createElement('div') - } - return selected - } else { - return el - } -} - -/* */ - -function createElement$1 (tagName, vnode) { - var 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 -} - -function createElementNS (namespace, tagName) { - return document.createElementNS(namespaceMap[namespace], tagName) -} - -function createTextNode (text) { - return document.createTextNode(text) -} - -function createComment (text) { - return document.createComment(text) -} - -function insertBefore (parentNode, newNode, referenceNode) { - parentNode.insertBefore(newNode, referenceNode); -} - -function removeChild (node, child) { - node.removeChild(child); -} - -function appendChild (node, child) { - node.appendChild(child); -} - -function parentNode (node) { - return node.parentNode -} - -function nextSibling (node) { - return node.nextSibling -} - -function tagName (node) { - return node.tagName -} - -function setTextContent (node, text) { - node.textContent = text; -} - -function setAttribute (node, key, val) { - node.setAttribute(key, val); -} - - -var nodeOps = Object.freeze({ - createElement: createElement$1, - createElementNS: createElementNS, - createTextNode: createTextNode, - createComment: createComment, - insertBefore: insertBefore, - removeChild: removeChild, - appendChild: appendChild, - parentNode: parentNode, - nextSibling: nextSibling, - tagName: tagName, - setTextContent: setTextContent, - setAttribute: setAttribute -}); - -/* */ - -var ref = { - create: function create (_, vnode) { - registerRef(vnode); - }, - update: function update (oldVnode, vnode) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true); - registerRef(vnode); - } - }, - destroy: function destroy (vnode) { - registerRef(vnode, true); - } -}; - -function registerRef (vnode, isRemoval) { - var key = vnode.data.ref; - if (!key) { return } - - var vm = vnode.context; - var ref = vnode.componentInstance || vnode.elm; - var refs = vm.$refs; - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref); - } else if (refs[key] === ref) { - refs[key] = undefined; - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref]; - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref); - } - } else { - refs[key] = ref; - } - } -} - -/** - * 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. - */ - -var emptyNode = new VNode('', {}, []); - -var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; - -function sameVnode (a, b) { - return ( - a.key === b.key && ( - ( - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - isUndef(b.asyncFactory.error) - ) - ) - ) -} - -function sameInputType (a, b) { - if (a.tag !== 'input') { return true } - var i; - var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type; - var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type; - return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) -} - -function createKeyToOldIdx (children, beginIdx, endIdx) { - var i, key; - var map = {}; - for (i = beginIdx; i <= endIdx; ++i) { - key = children[i].key; - if (isDef(key)) { map[key] = i; } - } - return map -} - -function createPatchFunction (backend) { - var i, j; - var cbs = {}; - - var modules = backend.modules; - var nodeOps = backend.nodeOps; - - 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) { - var parent = nodeOps.parentNode(el); - // element may have already been removed due to v-html / v-text - if (isDef(parent)) { - nodeOps.removeChild(parent, el); - } - } - - var inPre = 0; - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - vnode.isRootInsert = !nested; // for transition enter check - if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { - return - } - - var data = vnode.data; - var children = vnode.children; - var tag = vnode.tag; - if (isDef(tag)) { - if (process.env.NODE_ENV !== 'production') { - if (data && data.pre) { - inPre++; - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(function (ignore) { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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); - - /* istanbul ignore if */ - { - createChildren(vnode, children, insertedVnodeQueue); - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - - if (process.env.NODE_ENV !== 'production' && data && data.pre) { - inPre--; - } - } 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) { - var i = vnode.data; - if (isDef(i)) { - var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; - if (isDef(i = i.hook) && isDef(i = i.init)) { - i(vnode, false /* hydrating */, parentElm, refElm); - } - // 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); - 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) { - var 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. - var 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$$1) { - if (isDef(parent)) { - if (isDef(ref$$1)) { - if (ref$$1.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref$$1); - } - } else { - nodeOps.appendChild(parent, elm); - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); - } - } - - function isPatchable (vnode) { - while (vnode.componentInstance) { - vnode = vnode.componentInstance._vnode; - } - return isDef(vnode.tag) - } - - function invokeCreateHooks (vnode, insertedVnodeQueue) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](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) { - var i; - if (isDef(i = vnode.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, ''); - } else { - var ancestor = vnode; - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, ''); - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm); - } - } - - function invokeDestroyHook (vnode) { - var i, j; - var 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 (parentElm, vnodes, startIdx, endIdx) { - for (; startIdx <= endIdx; ++startIdx) { - var ch = vnodes[startIdx]; - if (isDef(ch)) { - if (isDef(ch.tag)) { - removeAndInvokeRemoveHook(ch); - invokeDestroyHook(ch); - } else { // Text node - removeNode(ch.elm); - } - } - } - } - - function removeAndInvokeRemoveHook (vnode, rm) { - if (isDef(rm) || isDef(vnode.data)) { - var i; - var 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) { - var oldStartIdx = 0; - var newStartIdx = 0; - var oldEndIdx = oldCh.length - 1; - var oldStartVnode = oldCh[0]; - var oldEndVnode = oldCh[oldEndIdx]; - var newEndIdx = newCh.length - 1; - var newStartVnode = newCh[0]; - var newEndVnode = newCh[newEndIdx]; - var 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 - var canMove = !removeOnly; - - 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); - oldStartVnode = oldCh[++oldStartIdx]; - newStartVnode = newCh[++newStartIdx]; - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); - oldEndVnode = oldCh[--oldEndIdx]; - newEndVnode = newCh[--newEndIdx]; - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); - 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); - 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); - } else { - vnodeToMove = oldCh[idxInOld]; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ); - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue); - 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); - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx); - } - } - - function findIdxInOld (node, oldCh, start, end) { - for (var i = start; i < end; i++) { - var c = oldCh[i]; - if (isDef(c) && sameVnode(node, c)) { return i } - } - } - - function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { - if (oldVnode === vnode) { - return - } - - var 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 - } - - var i; - var data = vnode.data; - if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { - i(oldVnode, vnode); - } - - var oldCh = oldVnode.children; - var 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 (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 (var i = 0; i < queue.length; ++i) { - queue[i].data.hook.insert(queue[i]); - } - } - } - - var bailed = 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 - var isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key'); - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm; - vnode.isAsyncPlaceholder = true; - return true - } - if (process.env.NODE_ENV !== 'production') { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm; - var tag = vnode.tag; - var data = vnode.data; - var children = vnode.children; - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('server innerHTML: ', i); - console.warn('client innerHTML: ', elm.innerHTML); - } - return false - } - } else { - // iterate and compare children lists - var childrenMatch = true; - var childNode = elm.firstChild; - for (var i$1 = 0; i$1 < children.length; i$1++) { - if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue)) { - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); - } - return false - } - } - } - } - if (isDef(data)) { - for (var key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text; - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } - return - } - - var isInitialPatch = false; - var insertedVnodeQueue = []; - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true; - createElm(vnode, insertedVnodeQueue, parentElm, refElm); - } else { - var isRealElement = isDef(oldVnode.nodeType); - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 (process.env.NODE_ENV !== 'production') { - 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 - var oldElm = oldVnode.elm; - var parentElm$1 = 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$1, - nodeOps.nextSibling(oldElm) - ); - - // update parent placeholder node element, recursively - if (isDef(vnode.parent)) { - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - // destroy old node - if (isDef(parentElm$1)) { - removeVnodes(parentElm$1, [oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } -} - -/* */ - -var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } -}; - -function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } -} - -function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode, 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode, 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } -} - -var emptyModifiers = Object.create(null); - -function normalizeDirectives$1 ( - dirs, - vm -) { - var res = Object.create(null); - if (!dirs) { - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - return res -} - -function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) -} - -function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } -} - -var baseModules = [ - ref, - directives -]; - -/* */ - -function updateAttrs (oldVnode, vnode) { - var opts = vnode.componentOptions; - if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { - return - } - if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(attrs.__ob__)) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - setAttr(elm, key, cur); - } - } - // #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 ((isIE9 || 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, key, value) { - 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, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'); - } else if (isXlink(key)) { - if (isFalsyAttrValue(value)) { - el.removeAttributeNS(xlinkNS, getXlinkProp(key)); - } else { - el.setAttributeNS(xlinkNS, key, value); - } - } else { - if (isFalsyAttrValue(value)) { - el.removeAttribute(key); - } else { - el.setAttribute(key, value); - } - } -} - -var attrs = { - create: updateAttrs, - update: updateAttrs -}; - -/* */ - -function updateClass (oldVnode, vnode) { - var el = vnode.elm; - var data = vnode.data; - var oldData = oldVnode.data; - if ( - isUndef(data.staticClass) && - isUndef(data.class) && ( - isUndef(oldData) || ( - isUndef(oldData.staticClass) && - isUndef(oldData.class) - ) - ) - ) { - return - } - - var cls = genClassForVnode(vnode); - - // handle transition classes - var 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; - } -} - -var klass = { - create: updateClass, - update: updateClass -}; - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - process.env.NODE_ENV !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index$1; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - len = val.length; - - if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) { - index$1 = val.lastIndexOf('.'); - if (index$1 > -1) { - return { - exp: val.slice(0, index$1), - key: '"' + val.slice(index$1 + 1) + '"' - } - } else { - return { - exp: val, - key: null - } - } - } - - str = val; - index$1 = 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 () { - return str.charCodeAt(++index$1) -} - -function eof () { - return index$1 >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var inBracket = 1; - expressionPos = index$1; - while (!eof()) { - chr = next(); - if (isStringStart(chr)) { - parseString(chr); - continue - } - if (chr === 0x5B) { inBracket++; } - if (chr === 0x5D) { inBracket--; } - if (inBracket === 0) { - expressionEndPos = index$1; - break - } - } -} - -function parseString (chr) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -/* */ - -var warn$1; - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; -var CHECKBOX_RADIO_TOKEN = '__c'; - -function model ( - el, - dir, - _warn -) { - warn$1 = _warn; - var value = dir.value; - var modifiers = dir.modifiers; - var tag = el.tag; - var type = el.attrsMap.type; - - if (process.env.NODE_ENV !== 'production') { - // inputs with type="file" are read only and setting the input's - // value will throw an error. - if (tag === 'input' && type === 'file') { - warn$1( - "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" + - "File inputs are read only. Use a v-on:change listener instead." - ); - } - } - - 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 (process.env.NODE_ENV !== 'production') { - warn$1( - "<" + (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.' - ); - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var valueBinding = getBindingAttr(el, 'value') || 'null'; - var trueValueBinding = getBindingAttr(el, 'true-value') || 'true'; - var 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&&(" + value + "=$$a.concat([$$v]))}" + - "else{$$i>-1&&(" + value + "=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}" + - "}else{" + (genAssignmentCode(value, '$$c')) + "}", - null, true - ); -} - -function genRadioModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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') + "})"; - - var assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'; - var code = "var $$selectedVal = " + selectedVal + ";"; - code = code + " " + (genAssignmentCode(value, assignment)); - addHandler(el, 'change', code, null, true); -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var type = el.attrsMap.type; - var ref = modifiers || {}; - var lazy = ref.lazy; - var number = ref.number; - var trim = ref.trim; - var needCompositionGuard = !lazy && type !== 'range'; - var event = lazy - ? 'change' - : type === 'range' - ? RANGE_TOKEN - : 'input'; - - var valueExpression = '$event.target.value'; - if (trim) { - valueExpression = "$event.target.value.trim()"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var 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()'); - } -} - -/* */ - -// 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 - var 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]; - } -} - -var target$1; - -function createOnceHandler (handler, event, capture) { - var _target = target$1; // save current target element in closure - return function onceHandler () { - var res = handler.apply(null, arguments); - if (res !== null) { - remove$2(event, onceHandler, capture, _target); - } - } -} - -function add$1 ( - event, - handler, - once$$1, - capture, - passive -) { - handler = withMacroTask(handler); - if (once$$1) { handler = createOnceHandler(handler, event, capture); } - target$1.addEventListener( - event, - handler, - supportsPassive - ? { capture: capture, passive: passive } - : capture - ); -} - -function remove$2 ( - event, - handler, - capture, - _target -) { - (_target || target$1).removeEventListener( - event, - handler._withTask || handler, - capture - ); -} - -function updateDOMListeners (oldVnode, vnode) { - if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { - return - } - var on = vnode.data.on || {}; - var oldOn = oldVnode.data.on || {}; - target$1 = vnode.elm; - normalizeEvents(on); - updateListeners(on, oldOn, add$1, remove$2, vnode.context); - target$1 = undefined; -} - -var events = { - create: updateDOMListeners, - update: updateDOMListeners -}; - -/* */ - -function updateDOMProps (oldVnode, vnode) { - if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { - return - } - var key, cur; - var elm = vnode.elm; - var oldProps = oldVnode.data.domProps || {}; - var props = vnode.data.domProps || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(props.__ob__)) { - props = vnode.data.domProps = extend({}, props); - } - - for (key in oldProps) { - if (isUndef(props[key])) { - 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') { - // 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 - var strCur = isUndef(cur) ? '' : String(cur); - if (shouldUpdateValue(elm, strCur)) { - elm.value = strCur; - } - } else { - elm[key] = cur; - } - } -} - -// check platforms/web/util/attrs.js acceptValue - - -function shouldUpdateValue (elm, checkVal) { - return (!elm.composing && ( - elm.tagName === 'OPTION' || - isDirty(elm, checkVal) || - isInputChanged(elm, checkVal) - )) -} - -function isDirty (elm, checkVal) { - // return true when textbox (.number and .trim) loses focus and its value is - // not equal to the updated value - var notInFocus = true; - // #6157 - // work around IE bug when accessing document.activeElement in an iframe - try { notInFocus = document.activeElement !== elm; } catch (e) {} - return notInFocus && elm.value !== checkVal -} - -function isInputChanged (elm, newVal) { - var value = elm.value; - var modifiers = elm._vModifiers; // injected by v-model runtime - if (isDef(modifiers) && modifiers.number) { - return toNumber(value) !== toNumber(newVal) - } - if (isDef(modifiers) && modifiers.trim) { - return value.trim() !== newVal.trim() - } - return value !== newVal -} - -var domProps = { - create: updateDOMProps, - update: updateDOMProps -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -var cssVarRE = /^--/; -var importantRE = /\s*!important$/; -var setProp = function (el, name, val) { - /* istanbul ignore if */ - if (cssVarRE.test(name)) { - el.style.setProperty(name, val); - } else if (importantRE.test(val)) { - el.style.setProperty(name, val.replace(importantRE, ''), 'important'); - } else { - var 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 (var i = 0, len = val.length; i < len; i++) { - el.style[normalizedName] = val[i]; - } - } else { - el.style[normalizedName] = val; - } - } -}; - -var vendorNames = ['Webkit', 'Moz', 'ms']; - -var emptyStyle; -var normalize = cached(function (prop) { - emptyStyle = emptyStyle || document.createElement('div').style; - prop = camelize(prop); - if (prop !== 'filter' && (prop in emptyStyle)) { - return prop - } - var capName = prop.charAt(0).toUpperCase() + prop.slice(1); - for (var i = 0; i < vendorNames.length; i++) { - var name = vendorNames[i] + capName; - if (name in emptyStyle) { - return name - } - } -}); - -function updateStyle (oldVnode, vnode) { - var data = vnode.data; - var oldData = oldVnode.data; - - if (isUndef(data.staticStyle) && isUndef(data.style) && - isUndef(oldData.staticStyle) && isUndef(oldData.style) - ) { - return - } - - var cur, name; - var el = vnode.elm; - var oldStaticStyle = oldData.staticStyle; - var oldStyleBinding = oldData.normalizedStyle || oldData.style || {}; - - // if static style exists, stylebinding already merged into it when doing normalizeStyleData - var oldStyle = oldStaticStyle || oldStyleBinding; - - var 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; - - var newStyle = getStyle(vnode, true); - - for (name in oldStyle) { - if (isUndef(newStyle[name])) { - setProp(el, name, ''); - } - } - for (name in newStyle) { - cur = newStyle[name]; - if (cur !== oldStyle[name]) { - // ie9 setting to null has no effect, must use empty string - setProp(el, name, cur == null ? '' : cur); - } - } -} - -var style = { - create: updateStyle, - update: updateStyle -}; - -/* */ - -/** - * Add class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ -function addClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.add(c); }); - } else { - el.classList.add(cls); - } - } else { - var 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 - */ -function removeClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.remove(c); }); - } else { - el.classList.remove(cls); - } - if (!el.classList.length) { - el.removeAttribute('class'); - } - } else { - var cur = " " + (el.getAttribute('class') || '') + " "; - var tar = ' ' + cls + ' '; - while (cur.indexOf(tar) >= 0) { - cur = cur.replace(tar, ' '); - } - cur = cur.trim(); - if (cur) { - el.setAttribute('class', cur); - } else { - el.removeAttribute('class'); - } - } -} - -/* */ - -function resolveTransition (def) { - if (!def) { - return - } - /* istanbul ignore else */ - if (typeof def === 'object') { - var res = {}; - if (def.css !== false) { - extend(res, autoCssTransition(def.name || 'v')); - } - extend(res, def); - return res - } else if (typeof def === 'string') { - return autoCssTransition(def) - } -} - -var autoCssTransition = cached(function (name) { - return { - enterClass: (name + "-enter"), - enterToClass: (name + "-enter-to"), - enterActiveClass: (name + "-enter-active"), - leaveClass: (name + "-leave"), - leaveToClass: (name + "-leave-to"), - leaveActiveClass: (name + "-leave-active") - } -}); - -var hasTransition = inBrowser && !isIE9; -var TRANSITION = 'transition'; -var ANIMATION = 'animation'; - -// Transition property/event sniffing -var transitionProp = 'transition'; -var transitionEndEvent = 'transitionend'; -var animationProp = 'animation'; -var 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 -var raf = inBrowser - ? window.requestAnimationFrame - ? window.requestAnimationFrame.bind(window) - : setTimeout - : /* istanbul ignore next */ function (fn) { return fn(); }; - -function nextFrame (fn) { - raf(function () { - raf(fn); - }); -} - -function addTransitionClass (el, cls) { - var transitionClasses = el._transitionClasses || (el._transitionClasses = []); - if (transitionClasses.indexOf(cls) < 0) { - transitionClasses.push(cls); - addClass(el, cls); - } -} - -function removeTransitionClass (el, cls) { - if (el._transitionClasses) { - remove(el._transitionClasses, cls); - } - removeClass(el, cls); -} - -function whenTransitionEnds ( - el, - expectedType, - cb -) { - var ref = getTransitionInfo(el, expectedType); - var type = ref.type; - var timeout = ref.timeout; - var propCount = ref.propCount; - if (!type) { return cb() } - var event = type === TRANSITION ? transitionEndEvent : animationEndEvent; - var ended = 0; - var end = function () { - el.removeEventListener(event, onEnd); - cb(); - }; - var onEnd = function (e) { - if (e.target === el) { - if (++ended >= propCount) { - end(); - } - } - }; - setTimeout(function () { - if (ended < propCount) { - end(); - } - }, timeout + 1); - el.addEventListener(event, onEnd); -} - -var transformRE = /\b(transform|all)(,|$)/; - -function getTransitionInfo (el, expectedType) { - var styles = window.getComputedStyle(el); - var transitionDelays = styles[transitionProp + 'Delay'].split(', '); - var transitionDurations = styles[transitionProp + 'Duration'].split(', '); - var transitionTimeout = getTimeout(transitionDelays, transitionDurations); - var animationDelays = styles[animationProp + 'Delay'].split(', '); - var animationDurations = styles[animationProp + 'Duration'].split(', '); - var animationTimeout = getTimeout(animationDelays, animationDurations); - - var type; - var timeout = 0; - var 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; - } - var hasTransform = - type === TRANSITION && - transformRE.test(styles[transitionProp + 'Property']); - return { - type: type, - timeout: timeout, - propCount: propCount, - hasTransform: hasTransform - } -} - -function getTimeout (delays, durations) { - /* istanbul ignore next */ - while (delays.length < durations.length) { - delays = delays.concat(delays); - } - - return Math.max.apply(null, durations.map(function (d, i) { - return toMs(d) + toMs(delays[i]) - })) -} - -function toMs (s) { - return Number(s.slice(0, -1)) * 1000 -} - -/* */ - -function enter (vnode, toggleDisplay) { - var el = vnode.elm; - - // call leave callback now - if (isDef(el._leaveCb)) { - el._leaveCb.cancelled = true; - el._leaveCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return - } - - /* istanbul ignore if */ - if (isDef(el._enterCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var enterClass = data.enterClass; - var enterToClass = data.enterToClass; - var enterActiveClass = data.enterActiveClass; - var appearClass = data.appearClass; - var appearToClass = data.appearToClass; - var appearActiveClass = data.appearActiveClass; - var beforeEnter = data.beforeEnter; - var enter = data.enter; - var afterEnter = data.afterEnter; - var enterCancelled = data.enterCancelled; - var beforeAppear = data.beforeAppear; - var appear = data.appear; - var afterAppear = data.afterAppear; - var appearCancelled = data.appearCancelled; - var duration = data.duration; - - // 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. - var context = activeInstance; - var transitionNode = activeInstance.$vnode; - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent; - context = transitionNode.context; - } - - var isAppear = !context._isMounted || !vnode.isRootInsert; - - if (isAppear && !appear && appear !== '') { - return - } - - var startClass = isAppear && appearClass - ? appearClass - : enterClass; - var activeClass = isAppear && appearActiveClass - ? appearActiveClass - : enterActiveClass; - var toClass = isAppear && appearToClass - ? appearToClass - : enterToClass; - - var beforeEnterHook = isAppear - ? (beforeAppear || beforeEnter) - : beforeEnter; - var enterHook = isAppear - ? (typeof appear === 'function' ? appear : enter) - : enter; - var afterEnterHook = isAppear - ? (afterAppear || afterEnter) - : afterEnter; - var enterCancelledHook = isAppear - ? (appearCancelled || enterCancelled) - : enterCancelled; - - var explicitEnterDuration = toNumber( - isObject(duration) - ? duration.enter - : duration - ); - - if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) { - checkDuration(explicitEnterDuration, 'enter', vnode); - } - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(enterHook); - - var cb = el._enterCb = once(function () { - if (expectsCSS) { - removeTransitionClass(el, toClass); - removeTransitionClass(el, activeClass); - } - 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', function () { - var parent = el.parentNode; - var 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(function () { - addTransitionClass(el, toClass); - removeTransitionClass(el, startClass); - if (!cb.cancelled && !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(); - } -} - -function leave (vnode, rm) { - var el = vnode.elm; - - // call enter callback now - if (isDef(el._enterCb)) { - el._enterCb.cancelled = true; - el._enterCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return rm() - } - - /* istanbul ignore if */ - if (isDef(el._leaveCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var leaveClass = data.leaveClass; - var leaveToClass = data.leaveToClass; - var leaveActiveClass = data.leaveActiveClass; - var beforeLeave = data.beforeLeave; - var leave = data.leave; - var afterLeave = data.afterLeave; - var leaveCancelled = data.leaveCancelled; - var delayLeave = data.delayLeave; - var duration = data.duration; - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(leave); - - var explicitLeaveDuration = toNumber( - isObject(duration) - ? duration.leave - : duration - ); - - if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) { - checkDuration(explicitLeaveDuration, 'leave', vnode); - } - - var cb = el._leaveCb = once(function () { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null; - } - if (expectsCSS) { - removeTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveActiveClass); - } - 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 - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode; - } - beforeLeave && beforeLeave(el); - if (expectsCSS) { - addTransitionClass(el, leaveClass); - addTransitionClass(el, leaveActiveClass); - nextFrame(function () { - addTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveClass); - if (!cb.cancelled && !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) { - if (isUndef(fn)) { - return false - } - var invokerFns = fn.fns; - if (isDef(invokerFns)) { - // invoker - return getHookArgumentsLength( - Array.isArray(invokerFns) - ? invokerFns[0] - : invokerFns - ) - } else { - return (fn._length || fn.length) > 1 - } -} - -function _enter (_, vnode) { - if (vnode.data.show !== true) { - enter(vnode); - } -} - -var transition = inBrowser ? { - create: _enter, - activate: _enter, - remove: function remove$$1 (vnode, rm) { - /* istanbul ignore else */ - if (vnode.data.show !== true) { - leave(vnode, rm); - } else { - rm(); - } - } -} : {}; - -var platformModules = [ - attrs, - klass, - events, - domProps, - style, - transition -]; - -/* */ - -// the directive module should be applied last, after all -// built-in modules have been applied. -var modules = platformModules.concat(baseModules); - -var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules }); - -/** - * Not type checking this file because flow doesn't like attaching - * properties to Elements. - */ - -/* istanbul ignore if */ -if (isIE9) { - // http://www.matts411.com/post/internet-explorer-9-oninput/ - document.addEventListener('selectionchange', function () { - var el = document.activeElement; - if (el && el.vmodel) { - trigger(el, 'input'); - } - }); -} - -var directive = { - inserted: function inserted (el, binding, vnode, oldVnode) { - if (vnode.tag === 'select') { - // #6903 - if (oldVnode.elm && !oldVnode.elm._vOptions) { - mergeVNodeHook(vnode, 'postpatch', function () { - 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) { - // 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); - if (!isAndroid) { - el.addEventListener('compositionstart', onCompositionStart); - el.addEventListener('compositionend', onCompositionEnd); - } - /* istanbul ignore if */ - if (isIE9) { - el.vmodel = true; - } - } - } - }, - - componentUpdated: function 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. - var prevOptions = el._vOptions; - var curOptions = el._vOptions = [].map.call(el.options, getValue); - if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) { - // trigger change event if - // no matching option found for at least one value - var needReset = el.multiple - ? binding.value.some(function (v) { return 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(function () { - actuallySetSelected(el, binding, vm); - }, 0); - } -} - -function actuallySetSelected (el, binding, vm) { - var value = binding.value; - var isMultiple = el.multiple; - if (isMultiple && !Array.isArray(value)) { - process.env.NODE_ENV !== 'production' && 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 - } - var selected, option; - for (var 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(function (o) { return !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) { - var e = document.createEvent('HTMLEvents'); - e.initEvent(type, true, true); - el.dispatchEvent(e); -} - -/* */ - -// recursively search for possible transition defined inside the component root -function locateNode (vnode) { - return vnode.componentInstance && (!vnode.data || !vnode.data.transition) - ? locateNode(vnode.componentInstance._vnode) - : vnode -} - -var show = { - bind: function bind (el, ref, vnode) { - var value = ref.value; - - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - var originalDisplay = el.__vOriginalDisplay = - el.style.display === 'none' ? '' : el.style.display; - if (value && transition$$1) { - vnode.data.show = true; - enter(vnode, function () { - el.style.display = originalDisplay; - }); - } else { - el.style.display = value ? originalDisplay : 'none'; - } - }, - - update: function update (el, ref, vnode) { - var value = ref.value; - var oldValue = ref.oldValue; - - /* istanbul ignore if */ - if (value === oldValue) { return } - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - if (transition$$1) { - vnode.data.show = true; - if (value) { - enter(vnode, function () { - el.style.display = el.__vOriginalDisplay; - }); - } else { - leave(vnode, function () { - el.style.display = 'none'; - }); - } - } else { - el.style.display = value ? el.__vOriginalDisplay : 'none'; - } - }, - - unbind: function unbind ( - el, - binding, - vnode, - oldVnode, - isDestroy - ) { - if (!isDestroy) { - el.style.display = el.__vOriginalDisplay; - } - } -}; - -var platformDirectives = { - model: directive, - show: show -}; - -/* */ - -// Provides transition support for a single element/component. -// supports transition mode (out-in / in-out) - -var 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) { - var compOptions = vnode && vnode.componentOptions; - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -function extractTransitionData (comp) { - var data = {}; - var options = comp.$options; - // props - for (var key in options.propsData) { - data[key] = comp[key]; - } - // events. - // extract listeners and pass them directly to the transition methods - var listeners = options._parentListeners; - for (var key$1 in listeners) { - data[camelize(key$1)] = listeners[key$1]; - } - return data -} - -function placeholder (h, rawChild) { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode) { - while ((vnode = vnode.parent)) { - if (vnode.data.transition) { - return true - } - } -} - -function isSameChild (child, oldChild) { - return oldChild.key === child.key && oldChild.tag === child.tag -} - -var Transition = { - name: 'transition', - props: transitionProps, - abstract: true, - - render: function render (h) { - var this$1 = this; - - var children = this.$options._renderChildren; - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if (process.env.NODE_ENV !== 'production' && children.length > 1) { - warn( - '<transition> can only be used on a single element. Use ' + - '<transition-group> for lists.', - this.$parent - ); - } - - var mode = this.mode; - - // warn invalid mode - if (process.env.NODE_ENV !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in' - ) { - warn( - 'invalid <transition> mode: ' + mode, - this.$parent - ); - } - - var rawChild = 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 - var 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. - var id = "__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; - - var data = (child.data || (child.data = {})).transition = extractTransitionData(this); - var oldRawChild = this._vnode; - var 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(function (d) { return d.name === 'show'; })) { - child.data.show = true; - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // replace old child transition data with fresh one - // important for dynamic transitions! - var oldData = 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', function () { - this$1._leaving = false; - this$1.$forceUpdate(); - }); - return placeholder(h, rawChild) - } else if (mode === 'in-out') { - if (isAsyncPlaceholder(child)) { - return oldRawChild - } - var delayedLeave; - var performLeave = function () { delayedLeave(); }; - mergeVNodeHook(data, 'afterEnter', performLeave); - mergeVNodeHook(data, 'enterCancelled', performLeave); - mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; }); - } - } - - return rawChild - } -}; - -/* */ - -// 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. - -var props = extend({ - tag: String, - moveClass: String -}, transitionProps); - -delete props.mode; - -var TransitionGroup = { - props: props, - - render: function render (h) { - var tag = this.tag || this.$vnode.data.tag || 'span'; - var map = Object.create(null); - var prevChildren = this.prevChildren = this.children; - var rawChildren = this.$slots.default || []; - var children = this.children = []; - var transitionData = extractTransitionData(this); - - for (var i = 0; i < rawChildren.length; i++) { - var c = 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 (process.env.NODE_ENV !== 'production') { - var opts = c.componentOptions; - var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag; - warn(("<transition-group> children must be keyed: <" + name + ">")); - } - } - } - - if (prevChildren) { - var kept = []; - var removed = []; - for (var i$1 = 0; i$1 < prevChildren.length; i$1++) { - var c$1 = prevChildren[i$1]; - c$1.data.transition = transitionData; - c$1.data.pos = c$1.elm.getBoundingClientRect(); - if (map[c$1.key]) { - kept.push(c$1); - } else { - removed.push(c$1); - } - } - this.kept = h(tag, null, kept); - this.removed = removed; - } - - return h(tag, null, children) - }, - - beforeUpdate: function beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ); - this._vnode = this.kept; - }, - - updated: function updated () { - var children = this.prevChildren; - var moveClass = 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(function (c) { - if (c.data.moved) { - var el = c.elm; - var s = el.style; - addTransitionClass(el, moveClass); - s.transform = s.WebkitTransform = s.transitionDuration = ''; - el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { - if (!e || /transform$/.test(e.propertyName)) { - el.removeEventListener(transitionEndEvent, cb); - el._moveCb = null; - removeTransitionClass(el, moveClass); - } - }); - } - }); - }, - - methods: { - hasMove: function hasMove (el, moveClass) { - /* 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. - var clone = el.cloneNode(); - if (el._transitionClasses) { - el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); }); - } - addClass(clone, moveClass); - clone.style.display = 'none'; - this.$el.appendChild(clone); - var info = getTransitionInfo(clone); - this.$el.removeChild(clone); - return (this._hasMove = info.hasTransform) - } - } -}; - -function callPendingCbs (c) { - /* istanbul ignore if */ - if (c.elm._moveCb) { - c.elm._moveCb(); - } - /* istanbul ignore if */ - if (c.elm._enterCb) { - c.elm._enterCb(); - } -} - -function recordPosition (c) { - c.data.newPos = c.elm.getBoundingClientRect(); -} - -function applyTranslation (c) { - var oldPos = c.data.pos; - var newPos = c.data.newPos; - var dx = oldPos.left - newPos.left; - var dy = oldPos.top - newPos.top; - if (dx || dy) { - c.data.moved = true; - var s = c.elm.style; - s.transform = s.WebkitTransform = "translate(" + dx + "px," + dy + "px)"; - s.transitionDuration = '0s'; - } -} - -var platformComponents = { - Transition: Transition, - TransitionGroup: TransitionGroup -}; - -/* */ - -// install platform specific utils -Vue$3.config.mustUseProp = mustUseProp; -Vue$3.config.isReservedTag = isReservedTag; -Vue$3.config.isReservedAttr = isReservedAttr; -Vue$3.config.getTagNamespace = getTagNamespace; -Vue$3.config.isUnknownElement = isUnknownElement; - -// install platform runtime directives & components -extend(Vue$3.options.directives, platformDirectives); -extend(Vue$3.options.components, platformComponents); - -// install platform patch function -Vue$3.prototype.__patch__ = inBrowser ? patch : noop; - -// public mount method -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && inBrowser ? query(el) : undefined; - return mountComponent(this, el, hydrating) -}; - -// devtools global hook -/* istanbul ignore next */ -Vue$3.nextTick(function () { - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue$3); - } else if (process.env.NODE_ENV !== 'production' && isChrome) { - console[console.info ? 'info' : 'log']( - 'Download the Vue Devtools extension for a better development experience:\n' + - 'https://github.com/vuejs/vue-devtools' - ); - } - } - if (process.env.NODE_ENV !== 'production' && - config.productionTip !== false && - inBrowser && typeof console !== 'undefined' - ) { - 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); - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - if (process.env.NODE_ENV !== 'production' && staticClass) { - var expression = parseText(staticClass, options.delimiters); - if (expression) { - 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">.' - ); - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass); - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } -} - -function genData (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -var klass$1 = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData -}; - -/* */ - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - if (staticStyle) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(staticStyle, options.delimiters); - if (expression) { - 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.staticStyle = JSON.stringify(parseStyleText(staticStyle)); - } - - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:(" + (el.styleBinding) + "),"; - } - return data -} - -var style$1 = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$1 -}; - -/* */ - -var decoder; - -var he = { - decode: function decode (html) { - decoder = decoder || document.createElement('div'); - decoder.innerHTML = html; - return decoder.textContent - } -}; - -/* */ - -var 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) -var 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 -var 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' -); - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if (process.env.NODE_ENV !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href' - ? options.shouldDecodeNewlinesForHref - : options.shouldDecodeNewlines; - attrs[i] = { - name: args[1], - value: decodeAttr(value, shouldDecodeNewlines) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if (process.env.NODE_ENV !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn$2; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn$2 = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn$2(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn$2, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - process.env.NODE_ENV !== 'production' && warn$2( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - function checkRootConstraints (el) { - if (process.env.NODE_ENV !== 'production') { - if (el.tag === 'slot' || el.tag === 'template') { - warnOnce( - "Cannot use <" + (el.tag) + "> as component root element because it may " + - 'contain multiple nodes.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else if (process.env.NODE_ENV !== 'production') { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - if (process.env.NODE_ENV !== 'production') { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if (process.env.NODE_ENV !== 'production' && el.tag === 'template') { - warn$2("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - process.env.NODE_ENV !== 'production' && warn$2( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else if (process.env.NODE_ENV !== 'production') { - warn$2( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if (process.env.NODE_ENV !== 'production' && children[i].text !== ' ') { - warn$2( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if (process.env.NODE_ENV !== 'production' && el.key) { - warn$2( - "`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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && slotScope) { - warn$2( - "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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // 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); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn$2); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if (process.env.NODE_ENV !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(value, delimiters); - if (expression) { - warn$2( - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - // #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'); - } - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - process.env.NODE_ENV !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn$2('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _el = el; - while (_el) { - if (_el.for && _el.alias === value) { - warn$2( - "<" + (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 = _el.parent; - } -} - -/* */ - -/** - * Expand input[v-model] with dyanmic 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]"> - */ - -function preTransformNode (el, options) { - if (el.tag === 'input') { - var map = el.attrsMap; - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - var typeBinding = getBindingAttr(el, 'type'); - var ifCondition = getAndRemoveAttr(el, 'v-if', true); - var ifConditionExtra = ifCondition ? ("&&(" + ifCondition + ")") : ""; - var hasElse = getAndRemoveAttr(el, 'v-else', true) != null; - var elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true); - // 1. checkbox - var 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 - var 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 - var 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value; - el.attrsList.push({ name: name, value: value }); -} - -var model$2 = { - preTransformNode: preTransformNode -}; - -var modules$1 = [ - klass$1, - style$1, - model$2 -]; - -/* */ - -function text (el, dir) { - if (dir.value) { - addProp(el, 'textContent', ("_s(" + (dir.value) + ")")); - } -} - -/* */ - -function html (el, dir) { - if (dir.value) { - addProp(el, 'innerHTML', ("_s(" + (dir.value) + ")")); - } -} - -var directives$1 = { - model: model, - text: text, - html: html -}; - -/* */ - -var baseOptions = { - expectHTML: true, - modules: modules$1, - directives: directives$1, - isPreTag: isPreTag, - isUnaryTag: isUnaryTag, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - staticKeys: genStaticKeys(modules$1) -}; - -/* */ - -var isStaticKey; -var isPlatformReservedTag; - -var genStaticKeysCached = cached(genStaticKeys$1); - -/** - * 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. - */ -function optimize (root, options) { - if (!root) { return } - isStaticKey = genStaticKeysCached(options.staticKeys || ''); - isPlatformReservedTag = options.isReservedTag || no; - // first pass: mark all non-static nodes. - markStatic$1(root); - // second pass: mark static roots. - markStaticRoots(root, false); -} - -function genStaticKeys$1 (keys) { - return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + - (keys ? ',' + keys : '') - ) -} - -function markStatic$1 (node) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - markStatic$1(child); - if (!child.static) { - node.static = false; - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].block; - markStatic$1(block); - if (!block.static) { - node.static = false; - } - } - } - } -} - -function markStaticRoots (node, isInFor) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - markStaticRoots(node.children[i], isInFor || !!node.for); - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - markStaticRoots(node.ifConditions[i$1].block, isInFor); - } - } - } -} - -function isStatic (node) { - 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) { - while (node.parent) { - node = node.parent; - if (node.tag !== 'template') { - return false - } - if (node.for) { - return true - } - } - return false -} - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if (process.env.NODE_ENV !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -function on (el, dir) { - if (process.env.NODE_ENV !== 'production' && dir.modifiers) { - warn("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData$2(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - process.env.NODE_ENV !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if (process.env.NODE_ENV !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData$2 (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if (process.env.NODE_ENV !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn; - delete options.warn; - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - if (process.env.NODE_ENV !== 'production') { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 (process.env.NODE_ENV !== 'production') { - if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -// `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. -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref$1 = createCompiler(baseOptions); -var compileToFunctions = ref$1.compileToFunctions; - -/* */ - -// check whether current browser encodes a char inside attribute values -var div; -function getShouldDecode (href) { - div = div || document.createElement('div'); - div.innerHTML = href ? "<a href=\"\n\"/>" : "<div a=\"\n\"/>"; - return div.innerHTML.indexOf(' ') > 0 -} - -// #3663: IE encodes newlines inside attribute values while other browsers don't -var shouldDecodeNewlines = inBrowser ? getShouldDecode(false) : false; -// #6828: chrome encodes content in a[href] -var shouldDecodeNewlinesForHref = inBrowser ? getShouldDecode(true) : false; - -/* */ - -var idToTemplate = cached(function (id) { - var el = query(id); - return el && el.innerHTML -}); - -var mount = Vue$3.prototype.$mount; -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && query(el); - - /* istanbul ignore if */ - if (el === document.body || el === document.documentElement) { - process.env.NODE_ENV !== 'production' && warn( - "Do not mount Vue to <html> or <body> - mount to normal elements instead." - ); - return this - } - - var options = this.$options; - // resolve template/el and convert to render function - if (!options.render) { - var template = options.template; - if (template) { - if (typeof template === 'string') { - if (template.charAt(0) === '#') { - template = idToTemplate(template); - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !template) { - warn( - ("Template element not found or is empty: " + (options.template)), - this - ); - } - } - } else if (template.nodeType) { - template = template.innerHTML; - } else { - if (process.env.NODE_ENV !== 'production') { - warn('invalid template option:' + template, this); - } - return this - } - } else if (el) { - template = getOuterHTML(el); - } - if (template) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - mark('compile'); - } - - var ref = compileToFunctions(template, { - shouldDecodeNewlines: shouldDecodeNewlines, - shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref, - delimiters: options.delimiters, - comments: options.comments - }, this); - var render = ref.render; - var staticRenderFns = ref.staticRenderFns; - options.render = render; - options.staticRenderFns = staticRenderFns; - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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) { - if (el.outerHTML) { - return el.outerHTML - } else { - var container = document.createElement('div'); - container.appendChild(el.cloneNode(true)); - return container.innerHTML - } -} - -Vue$3.compile = compileToFunctions; - -export default Vue$3; diff --git a/dist/vue.js b/dist/vue.js deleted file mode 100644 index 0f3458d64bb..00000000000 --- a/dist/vue.js +++ /dev/null @@ -1,10606 +0,0 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 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'; - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real 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. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: "development" !== 'production', - - /** - * Whether to enable devtools - */ - devtools: "development" !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -{ - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if ("development" !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - 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) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -{ - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - "development" !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && "development" !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if ("development" !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if ("development" !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if ("development" !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -var mark; -var measure; - -{ - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -{ - var 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,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - "development" !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var 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; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - "development" !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - "timeout (" + (res.timeout) + "ms)" - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - -function getFirstComponentChild (children) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var c = children[i]; - if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { - return c - } - } - } -} - -/* */ - -/* */ - -function initEvents (vm) { - vm._events = Object.create(null); - vm._hasHookEvent = false; - // init parent attached events - var listeners = vm.$options._parentListeners; - if (listeners) { - updateComponentListeners(vm, listeners); - } -} - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -function eventsMixin (Vue) { - var hookRE = /^hook:/; - Vue.prototype.$on = function (event, fn) { - var this$1 = this; - - var vm = this; - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$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, fn) { - var vm = 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, fn) { - var this$1 = this; - - var vm = this; - // all - if (!arguments.length) { - vm._events = Object.create(null); - return vm - } - // array of events - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$off(event[i], fn); - } - return vm - } - // specific event - var cbs = vm._events[event]; - if (!cbs) { - return vm - } - if (!fn) { - vm._events[event] = null; - return vm - } - if (fn) { - // specific handler - var cb; - var i$1 = cbs.length; - while (i$1--) { - cb = cbs[i$1]; - if (cb === fn || cb.fn === fn) { - cbs.splice(i$1, 1); - break - } - } - } - return vm - }; - - Vue.prototype.$emit = function (event) { - var vm = this; - { - var 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 + "\"." - ); - } - } - var cbs = vm._events[event]; - if (cbs) { - cbs = cbs.length > 1 ? toArray(cbs) : cbs; - var args = toArray(arguments, 1); - for (var i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args); - } catch (e) { - handleError(e, vm, ("event handler for \"" + event + "\"")); - } - } - } - return vm - }; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; -var isUpdatingChildComponent = false; - -function initLifecycle (vm) { - var options = vm.$options; - - // locate first non-abstract parent - var 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._watcher = null; - vm._inactive = null; - vm._directInactive = false; - vm._isMounted = false; - vm._isDestroyed = false; - vm._isBeingDestroyed = false; -} - -function lifecycleMixin (Vue) { - Vue.prototype._update = function (vnode, hydrating) { - var vm = this; - if (vm._isMounted) { - callHook(vm, 'beforeUpdate'); - } - var prevEl = vm.$el; - var prevVnode = vm._vnode; - var prevActiveInstance = activeInstance; - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ); - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null; - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode); - } - activeInstance = prevActiveInstance; - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el; - } - // updated hook is called by the scheduler to ensure that children are - // updated in a parent's updated hook. - }; - - Vue.prototype.$forceUpdate = function () { - var vm = this; - if (vm._watcher) { - vm._watcher.update(); - } - }; - - Vue.prototype.$destroy = function () { - var vm = this; - if (vm._isBeingDestroyed) { - return - } - callHook(vm, 'beforeDestroy'); - vm._isBeingDestroyed = true; - // remove self from parent - var parent = vm.$parent; - if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { - remove(parent.$children, vm); - } - // teardown watchers - if (vm._watcher) { - vm._watcher.teardown(); - } - var i = vm._watchers.length; - while (i--) { - vm._watchers[i].teardown(); - } - // 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; - } - }; -} - -function mountComponent ( - vm, - el, - hydrating -) { - vm.$el = el; - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode; - { - /* 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'); - - var updateComponent; - /* istanbul ignore if */ - if ("development" !== 'production' && config.performance && mark) { - updateComponent = function () { - var name = vm._name; - var id = vm._uid; - var startTag = "vue-perf-start:" + id; - var endTag = "vue-perf-end:" + id; - - mark(startTag); - var 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 = function () { - vm._update(vm._render(), hydrating); - }; - } - - vm._watcher = new Watcher(vm, updateComponent, noop); - hydrating = false; - - // 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 -} - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - { - isUpdatingChildComponent = true; - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - { - isUpdatingChildComponent = false; - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index = queue.length = activatedChildren.length = 0; - has = {}; - { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // 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]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var 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; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = expOrFn.toString(); - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - "development" !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var sharedPropertyDefinition = { - enumerable: true, - configurable: true, - get: noop, - set: noop -}; - -function proxy (target, sourceKey, key) { - sharedPropertyDefinition.get = function proxyGetter () { - return this[sourceKey][key] - }; - sharedPropertyDefinition.set = function proxySetter (val) { - this[sourceKey][key] = val; - }; - Object.defineProperty(target, key, sharedPropertyDefinition); -} - -function initState (vm) { - vm._watchers = []; - var opts = vm.$options; - if (opts.props) { initProps(vm, opts.props); } - if (opts.methods) { initMethods(vm, opts.methods); } - if (opts.data) { - initData(vm); - } else { - observe(vm._data = {}, true /* asRootData */); - } - if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch && opts.watch !== nativeWatch) { - initWatch(vm, opts.watch); - } -} - -function initProps (vm, propsOptions) { - var propsData = vm.$options.propsData || {}; - var props = vm._props = {}; - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - var keys = vm.$options._propKeys = []; - var isRoot = !vm.$parent; - // root instance props should be converted - observerState.shouldConvert = isRoot; - var loop = function ( key ) { - keys.push(key); - var value = validateProp(key, propsOptions, propsData, vm); - /* istanbul ignore else */ - { - var 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, function () { - if (vm.$parent && !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 - ); - } - }); - } - // 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); - } - }; - - for (var key in propsOptions) loop( key ); - observerState.shouldConvert = true; -} - -function initData (vm) { - var data = vm.$options.data; - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {}; - if (!isPlainObject(data)) { - data = {}; - "development" !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', - vm - ); - } - // proxy data on instance - var keys = Object.keys(data); - var props = vm.$options.props; - var methods = vm.$options.methods; - var i = keys.length; - while (i--) { - var key = keys[i]; - { - if (methods && hasOwn(methods, key)) { - warn( - ("Method \"" + key + "\" has already been defined as a data property."), - vm - ); - } - } - if (props && hasOwn(props, key)) { - "development" !== 'production' && 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 - observe(data, true /* asRootData */); -} - -function getData (data, vm) { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, "data()"); - return {} - } -} - -var computedWatcherOptions = { lazy: true }; - -function initComputed (vm, computed) { - var watchers = vm._computedWatchers = Object.create(null); - // computed properties are just getters during SSR - var isSSR = isServerRendering(); - - for (var key in computed) { - var userDef = computed[key]; - var getter = typeof userDef === 'function' ? userDef : userDef.get; - if ("development" !== 'production' && 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 (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); - } - } - } -} - -function defineComputed ( - target, - key, - userDef -) { - var shouldCache = !isServerRendering(); - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef; - sharedPropertyDefinition.set = noop; - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop; - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop; - } - if ("development" !== 'production' && - 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 () { - var watcher = this._computedWatchers && this._computedWatchers[key]; - if (watcher) { - if (watcher.dirty) { - watcher.evaluate(); - } - if (Dep.target) { - watcher.depend(); - } - return watcher.value - } - } -} - -function initMethods (vm, methods) { - var props = vm.$options.props; - for (var key in methods) { - { - if (methods[key] == null) { - warn( - "Method \"" + key + "\" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm); - } -} - -function initWatch (vm, watch) { - for (var key in watch) { - var handler = watch[key]; - if (Array.isArray(handler)) { - for (var i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]); - } - } else { - createWatcher(vm, key, handler); - } - } -} - -function createWatcher ( - vm, - keyOrFn, - handler, - options -) { - if (isPlainObject(handler)) { - options = handler; - handler = handler.handler; - } - if (typeof handler === 'string') { - handler = vm[handler]; - } - return vm.$watch(keyOrFn, handler, options) -} - -function stateMixin (Vue) { - // flow somehow has problems with directly declared definition object - // when using Object.defineProperty, so we have to procedurally build up - // the object here. - var dataDef = {}; - dataDef.get = function () { return this._data }; - var propsDef = {}; - propsDef.get = function () { return this._props }; - { - dataDef.set = function (newData) { - 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, - cb, - options - ) { - var vm = this; - if (isPlainObject(cb)) { - return createWatcher(vm, expOrFn, cb, options) - } - options = options || {}; - options.user = true; - var watcher = new Watcher(vm, expOrFn, cb, options); - if (options.immediate) { - cb.call(vm, watcher.value); - } - return function unwatchFn () { - watcher.teardown(); - } - }; -} - -/* */ - -function initProvide (vm) { - var provide = vm.$options.provide; - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide; - } -} - -function initInjections (vm) { - var result = resolveInject(vm.$options.inject, vm); - if (result) { - observerState.shouldConvert = false; - Object.keys(result).forEach(function (key) { - /* istanbul ignore else */ - { - defineReactive(vm, key, result[key], function () { - warn( - "Avoid mutating an injected value directly since the changes will be " + - "overwritten whenever the provided component re-renders. " + - "injection being mutated: \"" + key + "\"", - vm - ); - }); - } - }); - observerState.shouldConvert = true; - } -} - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if ("development" !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if ("development" !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - "development" !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - "development" !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - "development" !== 'production' && 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 ("development" !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -function initRender (vm) { - vm._vnode = null; // the root of the child tree - var options = vm.$options; - var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree - var renderContext = parentVnode && parentVnode.context; - vm.$slots = resolveSlots(options._renderChildren, renderContext); - vm.$scopedSlots = 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 - vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; - // normalization is always applied for the public version, used in - // user-written render functions. - vm.$createElement = function (a, b, c, d) { return 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 - var parentData = parentVnode && parentVnode.data; - - /* istanbul ignore else */ - { - defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () { - !isUpdatingChildComponent && warn("$attrs is readonly.", vm); - }, true); - defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () { - !isUpdatingChildComponent && warn("$listeners is readonly.", vm); - }, true); - } -} - -function renderMixin (Vue) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype); - - Vue.prototype.$nextTick = function (fn) { - return nextTick(fn, this) - }; - - Vue.prototype._render = function () { - var vm = this; - var ref = vm.$options; - var render = ref.render; - var _parentVnode = ref._parentVnode; - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (var key in vm.$slots) { - var slot = vm.$slots[key]; - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */); - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode; - // render self - var vnode; - try { - vnode = render.call(vm._renderProxy, vm.$createElement); - } catch (e) { - handleError(e, vm, "render"); - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); - } catch (e) { - handleError(e, vm, "renderError"); - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if ("development" !== 'production' && Array.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 - }; -} - -/* */ - -var uid$1 = 0; - -function initMixin (Vue) { - Vue.prototype._init = function (options) { - var vm = this; - // a uid - vm._uid = uid$1++; - - var startTag, endTag; - /* istanbul ignore if */ - if ("development" !== 'production' && config.performance && mark) { - startTag = "vue-perf-start:" + (vm._uid); - endTag = "vue-perf-end:" + (vm._uid); - mark(startTag); - } - - // a flag to avoid this being observed - vm._isVue = 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); - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ); - } - /* istanbul ignore else */ - { - initProxy(vm); - } - // expose real self - vm._self = vm; - initLifecycle(vm); - initEvents(vm); - initRender(vm); - callHook(vm, 'beforeCreate'); - initInjections(vm); // resolve injections before data/props - initState(vm); - initProvide(vm); // resolve provide after data/props - callHook(vm, 'created'); - - /* istanbul ignore if */ - if ("development" !== 'production' && 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); - } - }; -} - -function initInternalComponent (vm, options) { - var opts = vm.$options = Object.create(vm.constructor.options); - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent; - opts.propsData = options.propsData; - opts._parentVnode = options._parentVnode; - opts._parentListeners = options._parentListeners; - opts._renderChildren = options._renderChildren; - opts._componentTag = options._componentTag; - opts._parentElm = options._parentElm; - opts._refElm = options._refElm; - if (options.render) { - opts.render = options.render; - opts.staticRenderFns = options.staticRenderFns; - } -} - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -function Vue$3 (options) { - if ("development" !== 'production' && - !(this instanceof Vue$3) - ) { - warn('Vue is a constructor and should be called with the `new` keyword'); - } - this._init(options); -} - -initMixin(Vue$3); -stateMixin(Vue$3); -eventsMixin(Vue$3); -lifecycleMixin(Vue$3); -renderMixin(Vue$3); - -/* */ - -function initUse (Vue) { - Vue.use = function (plugin) { - var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - var args = toArray(arguments, 1); - args.unshift(this); - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args); - } else if (typeof plugin === 'function') { - plugin.apply(null, args); - } - installedPlugins.push(plugin); - return this - }; -} - -/* */ - -function initMixin$1 (Vue) { - Vue.mixin = function (mixin) { - this.options = mergeOptions(this.options, mixin); - return this - }; -} - -/* */ - -function initExtend (Vue) { - /** - * 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 - */ - Vue.extend = function (extendOptions) { - extendOptions = extendOptions || {}; - var Super = this; - var SuperId = Super.cid; - var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - var name = extendOptions.name || Super.options.name; - { - if (!/^[a-zA-Z][\w-]*$/.test(name)) { - warn( - 'Invalid component name: "' + name + '". Component names ' + - 'can only contain alphanumeric characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - } - - var Sub = function VueComponent (options) { - this._init(options); - }; - 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$1(Sub); - } - if (Sub.options.computed) { - initComputed$1(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$1 (Comp) { - var props = Comp.options.props; - for (var key in props) { - proxy(Comp.prototype, "_props", key); - } -} - -function initComputed$1 (Comp) { - var computed = Comp.options.computed; - for (var key in computed) { - defineComputed(Comp.prototype, key, computed[key]); - } -} - -/* */ - -function initAssetRegisters (Vue) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.forEach(function (type) { - Vue[type] = function ( - id, - definition - ) { - if (!definition) { - return this.options[type + 's'][id] - } else { - /* istanbul ignore if */ - { - if (type === 'component' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ); - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id; - definition = this.options._base.extend(definition); - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition }; - } - this.options[type + 's'][id] = definition; - return definition - } - }; - }); -} - -/* */ - -function getComponentName (opts) { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern, name) { - if (Array.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, filter) { - var cache = keepAliveInstance.cache; - var keys = keepAliveInstance.keys; - var _vnode = keepAliveInstance._vnode; - for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode); - } - } - } -} - -function pruneCacheEntry ( - cache, - key, - keys, - current -) { - var cached$$1 = cache[key]; - if (cached$$1 && cached$$1 !== current) { - cached$$1.componentInstance.$destroy(); - } - cache[key] = null; - remove(keys, key); -} - -var patternTypes = [String, RegExp, Array]; - -var KeepAlive = { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created: function created () { - this.cache = Object.create(null); - this.keys = []; - }, - - destroyed: function destroyed () { - var this$1 = this; - - for (var key in this$1.cache) { - pruneCacheEntry(this$1.cache, key, this$1.keys); - } - }, - - watch: { - include: function include (val) { - pruneCache(this, function (name) { return matches(val, name); }); - }, - exclude: function exclude (val) { - pruneCache(this, function (name) { return !matches(val, name); }); - } - }, - - render: function render () { - var vnode = getFirstComponentChild(this.$slots.default); - var componentOptions = vnode && vnode.componentOptions; - if (componentOptions) { - // check pattern - var name = getComponentName(componentOptions); - if (name && ( - (this.exclude && matches(this.exclude, name)) || - (this.include && !matches(this.include, name)) - )) { - return vnode - } - - var ref = this; - var cache = ref.cache; - var keys = ref.keys; - var 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 { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } - } - - vnode.data.keepAlive = true; - } - return vnode - } -}; - -var builtInComponents = { - KeepAlive: KeepAlive -}; - -/* */ - -function initGlobalAPI (Vue) { - // config - var configDef = {}; - configDef.get = function () { return config; }; - { - configDef.set = function () { - 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: warn, - extend: extend, - mergeOptions: mergeOptions, - defineReactive: defineReactive - }; - - Vue.set = set; - Vue.delete = del; - Vue.nextTick = nextTick; - - Vue.options = Object.create(null); - ASSET_TYPES.forEach(function (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$1(Vue); - initExtend(Vue); - initAssetRegisters(Vue); -} - -initGlobalAPI(Vue$3); - -Object.defineProperty(Vue$3.prototype, '$isServer', { - get: isServerRendering -}); - -Object.defineProperty(Vue$3.prototype, '$ssrContext', { - get: function get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}); - -Vue$3.version = '2.5.3'; - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -var xlinkNS = 'http://www.w3.org/1999/xlink'; - -var isXlink = function (name) { - return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' -}; - -var getXlinkProp = function (name) { - return isXlink(name) ? name.slice(6, name.length) : '' -}; - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - -var namespaceMap = { - svg: 'http://www.w3.org/2000/svg', - math: 'http://www.w3.org/1998/Math/MathML' -}; - -var 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. -var 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 -); - -var isPreTag = function (tag) { return tag === 'pre'; }; - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - -var unknownElementCache = Object.create(null); -function isUnknownElement (tag) { - /* 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] - } - var el = document.createElement(tag); - if (tag.indexOf('-') > -1) { - // http://stackoverflow.com/a/28210364/1070244 - return (unknownElementCache[tag] = ( - el.constructor === window.HTMLUnknownElement || - el.constructor === window.HTMLElement - )) - } else { - return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString())) - } -} - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ -function query (el) { - if (typeof el === 'string') { - var selected = document.querySelector(el); - if (!selected) { - "development" !== 'production' && warn( - 'Cannot find element: ' + el - ); - return document.createElement('div') - } - return selected - } else { - return el - } -} - -/* */ - -function createElement$1 (tagName, vnode) { - var 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 -} - -function createElementNS (namespace, tagName) { - return document.createElementNS(namespaceMap[namespace], tagName) -} - -function createTextNode (text) { - return document.createTextNode(text) -} - -function createComment (text) { - return document.createComment(text) -} - -function insertBefore (parentNode, newNode, referenceNode) { - parentNode.insertBefore(newNode, referenceNode); -} - -function removeChild (node, child) { - node.removeChild(child); -} - -function appendChild (node, child) { - node.appendChild(child); -} - -function parentNode (node) { - return node.parentNode -} - -function nextSibling (node) { - return node.nextSibling -} - -function tagName (node) { - return node.tagName -} - -function setTextContent (node, text) { - node.textContent = text; -} - -function setAttribute (node, key, val) { - node.setAttribute(key, val); -} - - -var nodeOps = Object.freeze({ - createElement: createElement$1, - createElementNS: createElementNS, - createTextNode: createTextNode, - createComment: createComment, - insertBefore: insertBefore, - removeChild: removeChild, - appendChild: appendChild, - parentNode: parentNode, - nextSibling: nextSibling, - tagName: tagName, - setTextContent: setTextContent, - setAttribute: setAttribute -}); - -/* */ - -var ref = { - create: function create (_, vnode) { - registerRef(vnode); - }, - update: function update (oldVnode, vnode) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true); - registerRef(vnode); - } - }, - destroy: function destroy (vnode) { - registerRef(vnode, true); - } -}; - -function registerRef (vnode, isRemoval) { - var key = vnode.data.ref; - if (!key) { return } - - var vm = vnode.context; - var ref = vnode.componentInstance || vnode.elm; - var refs = vm.$refs; - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref); - } else if (refs[key] === ref) { - refs[key] = undefined; - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref]; - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref); - } - } else { - refs[key] = ref; - } - } -} - -/** - * 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. - */ - -var emptyNode = new VNode('', {}, []); - -var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; - -function sameVnode (a, b) { - return ( - a.key === b.key && ( - ( - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - isUndef(b.asyncFactory.error) - ) - ) - ) -} - -function sameInputType (a, b) { - if (a.tag !== 'input') { return true } - var i; - var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type; - var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type; - return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) -} - -function createKeyToOldIdx (children, beginIdx, endIdx) { - var i, key; - var map = {}; - for (i = beginIdx; i <= endIdx; ++i) { - key = children[i].key; - if (isDef(key)) { map[key] = i; } - } - return map -} - -function createPatchFunction (backend) { - var i, j; - var cbs = {}; - - var modules = backend.modules; - var nodeOps = backend.nodeOps; - - 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) { - var parent = nodeOps.parentNode(el); - // element may have already been removed due to v-html / v-text - if (isDef(parent)) { - nodeOps.removeChild(parent, el); - } - } - - var inPre = 0; - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - vnode.isRootInsert = !nested; // for transition enter check - if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { - return - } - - var data = vnode.data; - var children = vnode.children; - var tag = vnode.tag; - if (isDef(tag)) { - { - if (data && data.pre) { - inPre++; - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(function (ignore) { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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); - - /* istanbul ignore if */ - { - createChildren(vnode, children, insertedVnodeQueue); - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - - if ("development" !== 'production' && data && data.pre) { - inPre--; - } - } 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) { - var i = vnode.data; - if (isDef(i)) { - var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; - if (isDef(i = i.hook) && isDef(i = i.init)) { - i(vnode, false /* hydrating */, parentElm, refElm); - } - // 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); - 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) { - var 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. - var 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$$1) { - if (isDef(parent)) { - if (isDef(ref$$1)) { - if (ref$$1.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref$$1); - } - } else { - nodeOps.appendChild(parent, elm); - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); - } - } - - function isPatchable (vnode) { - while (vnode.componentInstance) { - vnode = vnode.componentInstance._vnode; - } - return isDef(vnode.tag) - } - - function invokeCreateHooks (vnode, insertedVnodeQueue) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](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) { - var i; - if (isDef(i = vnode.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, ''); - } else { - var ancestor = vnode; - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, ''); - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm); - } - } - - function invokeDestroyHook (vnode) { - var i, j; - var 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 (parentElm, vnodes, startIdx, endIdx) { - for (; startIdx <= endIdx; ++startIdx) { - var ch = vnodes[startIdx]; - if (isDef(ch)) { - if (isDef(ch.tag)) { - removeAndInvokeRemoveHook(ch); - invokeDestroyHook(ch); - } else { // Text node - removeNode(ch.elm); - } - } - } - } - - function removeAndInvokeRemoveHook (vnode, rm) { - if (isDef(rm) || isDef(vnode.data)) { - var i; - var 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) { - var oldStartIdx = 0; - var newStartIdx = 0; - var oldEndIdx = oldCh.length - 1; - var oldStartVnode = oldCh[0]; - var oldEndVnode = oldCh[oldEndIdx]; - var newEndIdx = newCh.length - 1; - var newStartVnode = newCh[0]; - var newEndVnode = newCh[newEndIdx]; - var 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 - var canMove = !removeOnly; - - 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); - oldStartVnode = oldCh[++oldStartIdx]; - newStartVnode = newCh[++newStartIdx]; - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); - oldEndVnode = oldCh[--oldEndIdx]; - newEndVnode = newCh[--newEndIdx]; - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); - 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); - 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); - } else { - vnodeToMove = oldCh[idxInOld]; - /* istanbul ignore if */ - if ("development" !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ); - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue); - 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); - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx); - } - } - - function findIdxInOld (node, oldCh, start, end) { - for (var i = start; i < end; i++) { - var c = oldCh[i]; - if (isDef(c) && sameVnode(node, c)) { return i } - } - } - - function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { - if (oldVnode === vnode) { - return - } - - var 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 - } - - var i; - var data = vnode.data; - if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { - i(oldVnode, vnode); - } - - var oldCh = oldVnode.children; - var 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 (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 (var i = 0; i < queue.length; ++i) { - queue[i].data.hook.insert(queue[i]); - } - } - } - - var bailed = 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 - var isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key'); - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm; - vnode.isAsyncPlaceholder = true; - return true - } - { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm; - var tag = vnode.tag; - var data = vnode.data; - var children = vnode.children; - 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 ("development" !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('server innerHTML: ', i); - console.warn('client innerHTML: ', elm.innerHTML); - } - return false - } - } else { - // iterate and compare children lists - var childrenMatch = true; - var childNode = elm.firstChild; - for (var i$1 = 0; i$1 < children.length; i$1++) { - if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue)) { - 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 ("development" !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); - } - return false - } - } - } - } - if (isDef(data)) { - for (var key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text; - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } - return - } - - var isInitialPatch = false; - var insertedVnodeQueue = []; - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true; - createElm(vnode, insertedVnodeQueue, parentElm, refElm); - } else { - var isRealElement = isDef(oldVnode.nodeType); - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 { - 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 - var oldElm = oldVnode.elm; - var parentElm$1 = 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$1, - nodeOps.nextSibling(oldElm) - ); - - // update parent placeholder node element, recursively - if (isDef(vnode.parent)) { - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - // destroy old node - if (isDef(parentElm$1)) { - removeVnodes(parentElm$1, [oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } -} - -/* */ - -var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } -}; - -function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } -} - -function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode, 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode, 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } -} - -var emptyModifiers = Object.create(null); - -function normalizeDirectives$1 ( - dirs, - vm -) { - var res = Object.create(null); - if (!dirs) { - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - return res -} - -function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) -} - -function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } -} - -var baseModules = [ - ref, - directives -]; - -/* */ - -function updateAttrs (oldVnode, vnode) { - var opts = vnode.componentOptions; - if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { - return - } - if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(attrs.__ob__)) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - setAttr(elm, key, cur); - } - } - // #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 ((isIE9 || 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, key, value) { - 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, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'); - } else if (isXlink(key)) { - if (isFalsyAttrValue(value)) { - el.removeAttributeNS(xlinkNS, getXlinkProp(key)); - } else { - el.setAttributeNS(xlinkNS, key, value); - } - } else { - if (isFalsyAttrValue(value)) { - el.removeAttribute(key); - } else { - el.setAttribute(key, value); - } - } -} - -var attrs = { - create: updateAttrs, - update: updateAttrs -}; - -/* */ - -function updateClass (oldVnode, vnode) { - var el = vnode.elm; - var data = vnode.data; - var oldData = oldVnode.data; - if ( - isUndef(data.staticClass) && - isUndef(data.class) && ( - isUndef(oldData) || ( - isUndef(oldData.staticClass) && - isUndef(oldData.class) - ) - ) - ) { - return - } - - var cls = genClassForVnode(vnode); - - // handle transition classes - var 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; - } -} - -var klass = { - create: updateClass, - update: updateClass -}; - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - "development" !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index$1; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - len = val.length; - - if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) { - index$1 = val.lastIndexOf('.'); - if (index$1 > -1) { - return { - exp: val.slice(0, index$1), - key: '"' + val.slice(index$1 + 1) + '"' - } - } else { - return { - exp: val, - key: null - } - } - } - - str = val; - index$1 = 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 () { - return str.charCodeAt(++index$1) -} - -function eof () { - return index$1 >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var inBracket = 1; - expressionPos = index$1; - while (!eof()) { - chr = next(); - if (isStringStart(chr)) { - parseString(chr); - continue - } - if (chr === 0x5B) { inBracket++; } - if (chr === 0x5D) { inBracket--; } - if (inBracket === 0) { - expressionEndPos = index$1; - break - } - } -} - -function parseString (chr) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -/* */ - -var warn$1; - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; -var CHECKBOX_RADIO_TOKEN = '__c'; - -function model ( - el, - dir, - _warn -) { - warn$1 = _warn; - var value = dir.value; - var modifiers = dir.modifiers; - var tag = el.tag; - var type = el.attrsMap.type; - - { - // inputs with type="file" are read only and setting the input's - // value will throw an error. - if (tag === 'input' && type === 'file') { - warn$1( - "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" + - "File inputs are read only. Use a v-on:change listener instead." - ); - } - } - - 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 { - warn$1( - "<" + (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.' - ); - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var valueBinding = getBindingAttr(el, 'value') || 'null'; - var trueValueBinding = getBindingAttr(el, 'true-value') || 'true'; - var 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&&(" + value + "=$$a.concat([$$v]))}" + - "else{$$i>-1&&(" + value + "=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}" + - "}else{" + (genAssignmentCode(value, '$$c')) + "}", - null, true - ); -} - -function genRadioModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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') + "})"; - - var assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'; - var code = "var $$selectedVal = " + selectedVal + ";"; - code = code + " " + (genAssignmentCode(value, assignment)); - addHandler(el, 'change', code, null, true); -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var type = el.attrsMap.type; - var ref = modifiers || {}; - var lazy = ref.lazy; - var number = ref.number; - var trim = ref.trim; - var needCompositionGuard = !lazy && type !== 'range'; - var event = lazy - ? 'change' - : type === 'range' - ? RANGE_TOKEN - : 'input'; - - var valueExpression = '$event.target.value'; - if (trim) { - valueExpression = "$event.target.value.trim()"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var 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()'); - } -} - -/* */ - -// 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 - var 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]; - } -} - -var target$1; - -function createOnceHandler (handler, event, capture) { - var _target = target$1; // save current target element in closure - return function onceHandler () { - var res = handler.apply(null, arguments); - if (res !== null) { - remove$2(event, onceHandler, capture, _target); - } - } -} - -function add$1 ( - event, - handler, - once$$1, - capture, - passive -) { - handler = withMacroTask(handler); - if (once$$1) { handler = createOnceHandler(handler, event, capture); } - target$1.addEventListener( - event, - handler, - supportsPassive - ? { capture: capture, passive: passive } - : capture - ); -} - -function remove$2 ( - event, - handler, - capture, - _target -) { - (_target || target$1).removeEventListener( - event, - handler._withTask || handler, - capture - ); -} - -function updateDOMListeners (oldVnode, vnode) { - if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { - return - } - var on = vnode.data.on || {}; - var oldOn = oldVnode.data.on || {}; - target$1 = vnode.elm; - normalizeEvents(on); - updateListeners(on, oldOn, add$1, remove$2, vnode.context); - target$1 = undefined; -} - -var events = { - create: updateDOMListeners, - update: updateDOMListeners -}; - -/* */ - -function updateDOMProps (oldVnode, vnode) { - if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { - return - } - var key, cur; - var elm = vnode.elm; - var oldProps = oldVnode.data.domProps || {}; - var props = vnode.data.domProps || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(props.__ob__)) { - props = vnode.data.domProps = extend({}, props); - } - - for (key in oldProps) { - if (isUndef(props[key])) { - 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') { - // 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 - var strCur = isUndef(cur) ? '' : String(cur); - if (shouldUpdateValue(elm, strCur)) { - elm.value = strCur; - } - } else { - elm[key] = cur; - } - } -} - -// check platforms/web/util/attrs.js acceptValue - - -function shouldUpdateValue (elm, checkVal) { - return (!elm.composing && ( - elm.tagName === 'OPTION' || - isDirty(elm, checkVal) || - isInputChanged(elm, checkVal) - )) -} - -function isDirty (elm, checkVal) { - // return true when textbox (.number and .trim) loses focus and its value is - // not equal to the updated value - var notInFocus = true; - // #6157 - // work around IE bug when accessing document.activeElement in an iframe - try { notInFocus = document.activeElement !== elm; } catch (e) {} - return notInFocus && elm.value !== checkVal -} - -function isInputChanged (elm, newVal) { - var value = elm.value; - var modifiers = elm._vModifiers; // injected by v-model runtime - if (isDef(modifiers) && modifiers.number) { - return toNumber(value) !== toNumber(newVal) - } - if (isDef(modifiers) && modifiers.trim) { - return value.trim() !== newVal.trim() - } - return value !== newVal -} - -var domProps = { - create: updateDOMProps, - update: updateDOMProps -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -var cssVarRE = /^--/; -var importantRE = /\s*!important$/; -var setProp = function (el, name, val) { - /* istanbul ignore if */ - if (cssVarRE.test(name)) { - el.style.setProperty(name, val); - } else if (importantRE.test(val)) { - el.style.setProperty(name, val.replace(importantRE, ''), 'important'); - } else { - var 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 (var i = 0, len = val.length; i < len; i++) { - el.style[normalizedName] = val[i]; - } - } else { - el.style[normalizedName] = val; - } - } -}; - -var vendorNames = ['Webkit', 'Moz', 'ms']; - -var emptyStyle; -var normalize = cached(function (prop) { - emptyStyle = emptyStyle || document.createElement('div').style; - prop = camelize(prop); - if (prop !== 'filter' && (prop in emptyStyle)) { - return prop - } - var capName = prop.charAt(0).toUpperCase() + prop.slice(1); - for (var i = 0; i < vendorNames.length; i++) { - var name = vendorNames[i] + capName; - if (name in emptyStyle) { - return name - } - } -}); - -function updateStyle (oldVnode, vnode) { - var data = vnode.data; - var oldData = oldVnode.data; - - if (isUndef(data.staticStyle) && isUndef(data.style) && - isUndef(oldData.staticStyle) && isUndef(oldData.style) - ) { - return - } - - var cur, name; - var el = vnode.elm; - var oldStaticStyle = oldData.staticStyle; - var oldStyleBinding = oldData.normalizedStyle || oldData.style || {}; - - // if static style exists, stylebinding already merged into it when doing normalizeStyleData - var oldStyle = oldStaticStyle || oldStyleBinding; - - var 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; - - var newStyle = getStyle(vnode, true); - - for (name in oldStyle) { - if (isUndef(newStyle[name])) { - setProp(el, name, ''); - } - } - for (name in newStyle) { - cur = newStyle[name]; - if (cur !== oldStyle[name]) { - // ie9 setting to null has no effect, must use empty string - setProp(el, name, cur == null ? '' : cur); - } - } -} - -var style = { - create: updateStyle, - update: updateStyle -}; - -/* */ - -/** - * Add class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ -function addClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.add(c); }); - } else { - el.classList.add(cls); - } - } else { - var 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 - */ -function removeClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.remove(c); }); - } else { - el.classList.remove(cls); - } - if (!el.classList.length) { - el.removeAttribute('class'); - } - } else { - var cur = " " + (el.getAttribute('class') || '') + " "; - var tar = ' ' + cls + ' '; - while (cur.indexOf(tar) >= 0) { - cur = cur.replace(tar, ' '); - } - cur = cur.trim(); - if (cur) { - el.setAttribute('class', cur); - } else { - el.removeAttribute('class'); - } - } -} - -/* */ - -function resolveTransition (def) { - if (!def) { - return - } - /* istanbul ignore else */ - if (typeof def === 'object') { - var res = {}; - if (def.css !== false) { - extend(res, autoCssTransition(def.name || 'v')); - } - extend(res, def); - return res - } else if (typeof def === 'string') { - return autoCssTransition(def) - } -} - -var autoCssTransition = cached(function (name) { - return { - enterClass: (name + "-enter"), - enterToClass: (name + "-enter-to"), - enterActiveClass: (name + "-enter-active"), - leaveClass: (name + "-leave"), - leaveToClass: (name + "-leave-to"), - leaveActiveClass: (name + "-leave-active") - } -}); - -var hasTransition = inBrowser && !isIE9; -var TRANSITION = 'transition'; -var ANIMATION = 'animation'; - -// Transition property/event sniffing -var transitionProp = 'transition'; -var transitionEndEvent = 'transitionend'; -var animationProp = 'animation'; -var 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 -var raf = inBrowser - ? window.requestAnimationFrame - ? window.requestAnimationFrame.bind(window) - : setTimeout - : /* istanbul ignore next */ function (fn) { return fn(); }; - -function nextFrame (fn) { - raf(function () { - raf(fn); - }); -} - -function addTransitionClass (el, cls) { - var transitionClasses = el._transitionClasses || (el._transitionClasses = []); - if (transitionClasses.indexOf(cls) < 0) { - transitionClasses.push(cls); - addClass(el, cls); - } -} - -function removeTransitionClass (el, cls) { - if (el._transitionClasses) { - remove(el._transitionClasses, cls); - } - removeClass(el, cls); -} - -function whenTransitionEnds ( - el, - expectedType, - cb -) { - var ref = getTransitionInfo(el, expectedType); - var type = ref.type; - var timeout = ref.timeout; - var propCount = ref.propCount; - if (!type) { return cb() } - var event = type === TRANSITION ? transitionEndEvent : animationEndEvent; - var ended = 0; - var end = function () { - el.removeEventListener(event, onEnd); - cb(); - }; - var onEnd = function (e) { - if (e.target === el) { - if (++ended >= propCount) { - end(); - } - } - }; - setTimeout(function () { - if (ended < propCount) { - end(); - } - }, timeout + 1); - el.addEventListener(event, onEnd); -} - -var transformRE = /\b(transform|all)(,|$)/; - -function getTransitionInfo (el, expectedType) { - var styles = window.getComputedStyle(el); - var transitionDelays = styles[transitionProp + 'Delay'].split(', '); - var transitionDurations = styles[transitionProp + 'Duration'].split(', '); - var transitionTimeout = getTimeout(transitionDelays, transitionDurations); - var animationDelays = styles[animationProp + 'Delay'].split(', '); - var animationDurations = styles[animationProp + 'Duration'].split(', '); - var animationTimeout = getTimeout(animationDelays, animationDurations); - - var type; - var timeout = 0; - var 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; - } - var hasTransform = - type === TRANSITION && - transformRE.test(styles[transitionProp + 'Property']); - return { - type: type, - timeout: timeout, - propCount: propCount, - hasTransform: hasTransform - } -} - -function getTimeout (delays, durations) { - /* istanbul ignore next */ - while (delays.length < durations.length) { - delays = delays.concat(delays); - } - - return Math.max.apply(null, durations.map(function (d, i) { - return toMs(d) + toMs(delays[i]) - })) -} - -function toMs (s) { - return Number(s.slice(0, -1)) * 1000 -} - -/* */ - -function enter (vnode, toggleDisplay) { - var el = vnode.elm; - - // call leave callback now - if (isDef(el._leaveCb)) { - el._leaveCb.cancelled = true; - el._leaveCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return - } - - /* istanbul ignore if */ - if (isDef(el._enterCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var enterClass = data.enterClass; - var enterToClass = data.enterToClass; - var enterActiveClass = data.enterActiveClass; - var appearClass = data.appearClass; - var appearToClass = data.appearToClass; - var appearActiveClass = data.appearActiveClass; - var beforeEnter = data.beforeEnter; - var enter = data.enter; - var afterEnter = data.afterEnter; - var enterCancelled = data.enterCancelled; - var beforeAppear = data.beforeAppear; - var appear = data.appear; - var afterAppear = data.afterAppear; - var appearCancelled = data.appearCancelled; - var duration = data.duration; - - // 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. - var context = activeInstance; - var transitionNode = activeInstance.$vnode; - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent; - context = transitionNode.context; - } - - var isAppear = !context._isMounted || !vnode.isRootInsert; - - if (isAppear && !appear && appear !== '') { - return - } - - var startClass = isAppear && appearClass - ? appearClass - : enterClass; - var activeClass = isAppear && appearActiveClass - ? appearActiveClass - : enterActiveClass; - var toClass = isAppear && appearToClass - ? appearToClass - : enterToClass; - - var beforeEnterHook = isAppear - ? (beforeAppear || beforeEnter) - : beforeEnter; - var enterHook = isAppear - ? (typeof appear === 'function' ? appear : enter) - : enter; - var afterEnterHook = isAppear - ? (afterAppear || afterEnter) - : afterEnter; - var enterCancelledHook = isAppear - ? (appearCancelled || enterCancelled) - : enterCancelled; - - var explicitEnterDuration = toNumber( - isObject(duration) - ? duration.enter - : duration - ); - - if ("development" !== 'production' && explicitEnterDuration != null) { - checkDuration(explicitEnterDuration, 'enter', vnode); - } - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(enterHook); - - var cb = el._enterCb = once(function () { - if (expectsCSS) { - removeTransitionClass(el, toClass); - removeTransitionClass(el, activeClass); - } - 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', function () { - var parent = el.parentNode; - var 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(function () { - addTransitionClass(el, toClass); - removeTransitionClass(el, startClass); - if (!cb.cancelled && !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(); - } -} - -function leave (vnode, rm) { - var el = vnode.elm; - - // call enter callback now - if (isDef(el._enterCb)) { - el._enterCb.cancelled = true; - el._enterCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return rm() - } - - /* istanbul ignore if */ - if (isDef(el._leaveCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var leaveClass = data.leaveClass; - var leaveToClass = data.leaveToClass; - var leaveActiveClass = data.leaveActiveClass; - var beforeLeave = data.beforeLeave; - var leave = data.leave; - var afterLeave = data.afterLeave; - var leaveCancelled = data.leaveCancelled; - var delayLeave = data.delayLeave; - var duration = data.duration; - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(leave); - - var explicitLeaveDuration = toNumber( - isObject(duration) - ? duration.leave - : duration - ); - - if ("development" !== 'production' && isDef(explicitLeaveDuration)) { - checkDuration(explicitLeaveDuration, 'leave', vnode); - } - - var cb = el._leaveCb = once(function () { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null; - } - if (expectsCSS) { - removeTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveActiveClass); - } - 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 - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode; - } - beforeLeave && beforeLeave(el); - if (expectsCSS) { - addTransitionClass(el, leaveClass); - addTransitionClass(el, leaveActiveClass); - nextFrame(function () { - addTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveClass); - if (!cb.cancelled && !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) { - if (isUndef(fn)) { - return false - } - var invokerFns = fn.fns; - if (isDef(invokerFns)) { - // invoker - return getHookArgumentsLength( - Array.isArray(invokerFns) - ? invokerFns[0] - : invokerFns - ) - } else { - return (fn._length || fn.length) > 1 - } -} - -function _enter (_, vnode) { - if (vnode.data.show !== true) { - enter(vnode); - } -} - -var transition = inBrowser ? { - create: _enter, - activate: _enter, - remove: function remove$$1 (vnode, rm) { - /* istanbul ignore else */ - if (vnode.data.show !== true) { - leave(vnode, rm); - } else { - rm(); - } - } -} : {}; - -var platformModules = [ - attrs, - klass, - events, - domProps, - style, - transition -]; - -/* */ - -// the directive module should be applied last, after all -// built-in modules have been applied. -var modules = platformModules.concat(baseModules); - -var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules }); - -/** - * Not type checking this file because flow doesn't like attaching - * properties to Elements. - */ - -/* istanbul ignore if */ -if (isIE9) { - // http://www.matts411.com/post/internet-explorer-9-oninput/ - document.addEventListener('selectionchange', function () { - var el = document.activeElement; - if (el && el.vmodel) { - trigger(el, 'input'); - } - }); -} - -var directive = { - inserted: function inserted (el, binding, vnode, oldVnode) { - if (vnode.tag === 'select') { - // #6903 - if (oldVnode.elm && !oldVnode.elm._vOptions) { - mergeVNodeHook(vnode, 'postpatch', function () { - 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) { - // 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); - if (!isAndroid) { - el.addEventListener('compositionstart', onCompositionStart); - el.addEventListener('compositionend', onCompositionEnd); - } - /* istanbul ignore if */ - if (isIE9) { - el.vmodel = true; - } - } - } - }, - - componentUpdated: function 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. - var prevOptions = el._vOptions; - var curOptions = el._vOptions = [].map.call(el.options, getValue); - if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) { - // trigger change event if - // no matching option found for at least one value - var needReset = el.multiple - ? binding.value.some(function (v) { return 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(function () { - actuallySetSelected(el, binding, vm); - }, 0); - } -} - -function actuallySetSelected (el, binding, vm) { - var value = binding.value; - var isMultiple = el.multiple; - if (isMultiple && !Array.isArray(value)) { - "development" !== 'production' && 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 - } - var selected, option; - for (var 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(function (o) { return !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) { - var e = document.createEvent('HTMLEvents'); - e.initEvent(type, true, true); - el.dispatchEvent(e); -} - -/* */ - -// recursively search for possible transition defined inside the component root -function locateNode (vnode) { - return vnode.componentInstance && (!vnode.data || !vnode.data.transition) - ? locateNode(vnode.componentInstance._vnode) - : vnode -} - -var show = { - bind: function bind (el, ref, vnode) { - var value = ref.value; - - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - var originalDisplay = el.__vOriginalDisplay = - el.style.display === 'none' ? '' : el.style.display; - if (value && transition$$1) { - vnode.data.show = true; - enter(vnode, function () { - el.style.display = originalDisplay; - }); - } else { - el.style.display = value ? originalDisplay : 'none'; - } - }, - - update: function update (el, ref, vnode) { - var value = ref.value; - var oldValue = ref.oldValue; - - /* istanbul ignore if */ - if (value === oldValue) { return } - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - if (transition$$1) { - vnode.data.show = true; - if (value) { - enter(vnode, function () { - el.style.display = el.__vOriginalDisplay; - }); - } else { - leave(vnode, function () { - el.style.display = 'none'; - }); - } - } else { - el.style.display = value ? el.__vOriginalDisplay : 'none'; - } - }, - - unbind: function unbind ( - el, - binding, - vnode, - oldVnode, - isDestroy - ) { - if (!isDestroy) { - el.style.display = el.__vOriginalDisplay; - } - } -}; - -var platformDirectives = { - model: directive, - show: show -}; - -/* */ - -// Provides transition support for a single element/component. -// supports transition mode (out-in / in-out) - -var 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) { - var compOptions = vnode && vnode.componentOptions; - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -function extractTransitionData (comp) { - var data = {}; - var options = comp.$options; - // props - for (var key in options.propsData) { - data[key] = comp[key]; - } - // events. - // extract listeners and pass them directly to the transition methods - var listeners = options._parentListeners; - for (var key$1 in listeners) { - data[camelize(key$1)] = listeners[key$1]; - } - return data -} - -function placeholder (h, rawChild) { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode) { - while ((vnode = vnode.parent)) { - if (vnode.data.transition) { - return true - } - } -} - -function isSameChild (child, oldChild) { - return oldChild.key === child.key && oldChild.tag === child.tag -} - -var Transition = { - name: 'transition', - props: transitionProps, - abstract: true, - - render: function render (h) { - var this$1 = this; - - var children = this.$options._renderChildren; - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if ("development" !== 'production' && children.length > 1) { - warn( - '<transition> can only be used on a single element. Use ' + - '<transition-group> for lists.', - this.$parent - ); - } - - var mode = this.mode; - - // warn invalid mode - if ("development" !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in' - ) { - warn( - 'invalid <transition> mode: ' + mode, - this.$parent - ); - } - - var rawChild = 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 - var 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. - var id = "__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; - - var data = (child.data || (child.data = {})).transition = extractTransitionData(this); - var oldRawChild = this._vnode; - var 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(function (d) { return d.name === 'show'; })) { - child.data.show = true; - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // replace old child transition data with fresh one - // important for dynamic transitions! - var oldData = 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', function () { - this$1._leaving = false; - this$1.$forceUpdate(); - }); - return placeholder(h, rawChild) - } else if (mode === 'in-out') { - if (isAsyncPlaceholder(child)) { - return oldRawChild - } - var delayedLeave; - var performLeave = function () { delayedLeave(); }; - mergeVNodeHook(data, 'afterEnter', performLeave); - mergeVNodeHook(data, 'enterCancelled', performLeave); - mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; }); - } - } - - return rawChild - } -}; - -/* */ - -// 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. - -var props = extend({ - tag: String, - moveClass: String -}, transitionProps); - -delete props.mode; - -var TransitionGroup = { - props: props, - - render: function render (h) { - var tag = this.tag || this.$vnode.data.tag || 'span'; - var map = Object.create(null); - var prevChildren = this.prevChildren = this.children; - var rawChildren = this.$slots.default || []; - var children = this.children = []; - var transitionData = extractTransitionData(this); - - for (var i = 0; i < rawChildren.length; i++) { - var c = 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 { - var opts = c.componentOptions; - var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag; - warn(("<transition-group> children must be keyed: <" + name + ">")); - } - } - } - - if (prevChildren) { - var kept = []; - var removed = []; - for (var i$1 = 0; i$1 < prevChildren.length; i$1++) { - var c$1 = prevChildren[i$1]; - c$1.data.transition = transitionData; - c$1.data.pos = c$1.elm.getBoundingClientRect(); - if (map[c$1.key]) { - kept.push(c$1); - } else { - removed.push(c$1); - } - } - this.kept = h(tag, null, kept); - this.removed = removed; - } - - return h(tag, null, children) - }, - - beforeUpdate: function beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ); - this._vnode = this.kept; - }, - - updated: function updated () { - var children = this.prevChildren; - var moveClass = 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(function (c) { - if (c.data.moved) { - var el = c.elm; - var s = el.style; - addTransitionClass(el, moveClass); - s.transform = s.WebkitTransform = s.transitionDuration = ''; - el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { - if (!e || /transform$/.test(e.propertyName)) { - el.removeEventListener(transitionEndEvent, cb); - el._moveCb = null; - removeTransitionClass(el, moveClass); - } - }); - } - }); - }, - - methods: { - hasMove: function hasMove (el, moveClass) { - /* 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. - var clone = el.cloneNode(); - if (el._transitionClasses) { - el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); }); - } - addClass(clone, moveClass); - clone.style.display = 'none'; - this.$el.appendChild(clone); - var info = getTransitionInfo(clone); - this.$el.removeChild(clone); - return (this._hasMove = info.hasTransform) - } - } -}; - -function callPendingCbs (c) { - /* istanbul ignore if */ - if (c.elm._moveCb) { - c.elm._moveCb(); - } - /* istanbul ignore if */ - if (c.elm._enterCb) { - c.elm._enterCb(); - } -} - -function recordPosition (c) { - c.data.newPos = c.elm.getBoundingClientRect(); -} - -function applyTranslation (c) { - var oldPos = c.data.pos; - var newPos = c.data.newPos; - var dx = oldPos.left - newPos.left; - var dy = oldPos.top - newPos.top; - if (dx || dy) { - c.data.moved = true; - var s = c.elm.style; - s.transform = s.WebkitTransform = "translate(" + dx + "px," + dy + "px)"; - s.transitionDuration = '0s'; - } -} - -var platformComponents = { - Transition: Transition, - TransitionGroup: TransitionGroup -}; - -/* */ - -// install platform specific utils -Vue$3.config.mustUseProp = mustUseProp; -Vue$3.config.isReservedTag = isReservedTag; -Vue$3.config.isReservedAttr = isReservedAttr; -Vue$3.config.getTagNamespace = getTagNamespace; -Vue$3.config.isUnknownElement = isUnknownElement; - -// install platform runtime directives & components -extend(Vue$3.options.directives, platformDirectives); -extend(Vue$3.options.components, platformComponents); - -// install platform patch function -Vue$3.prototype.__patch__ = inBrowser ? patch : noop; - -// public mount method -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && inBrowser ? query(el) : undefined; - return mountComponent(this, el, hydrating) -}; - -// devtools global hook -/* istanbul ignore next */ -Vue$3.nextTick(function () { - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue$3); - } else if ("development" !== 'production' && isChrome) { - console[console.info ? 'info' : 'log']( - 'Download the Vue Devtools extension for a better development experience:\n' + - 'https://github.com/vuejs/vue-devtools' - ); - } - } - if ("development" !== 'production' && - config.productionTip !== false && - inBrowser && typeof console !== 'undefined' - ) { - 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); - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - if ("development" !== 'production' && staticClass) { - var expression = parseText(staticClass, options.delimiters); - if (expression) { - 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">.' - ); - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass); - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } -} - -function genData (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -var klass$1 = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData -}; - -/* */ - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - if (staticStyle) { - /* istanbul ignore if */ - { - var expression = parseText(staticStyle, options.delimiters); - if (expression) { - 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.staticStyle = JSON.stringify(parseStyleText(staticStyle)); - } - - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:(" + (el.styleBinding) + "),"; - } - return data -} - -var style$1 = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$1 -}; - -/* */ - -var decoder; - -var he = { - decode: function decode (html) { - decoder = decoder || document.createElement('div'); - decoder.innerHTML = html; - return decoder.textContent - } -}; - -/* */ - -var 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) -var 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 -var 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' -); - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if ("development" !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href' - ? options.shouldDecodeNewlinesForHref - : options.shouldDecodeNewlines; - attrs[i] = { - name: args[1], - value: decodeAttr(value, shouldDecodeNewlines) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if ("development" !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn$2; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn$2 = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn$2(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn$2, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - "development" !== 'production' && warn$2( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - 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.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if ("development" !== 'production' && el.tag === 'template') { - warn$2("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - "development" !== 'production' && warn$2( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else { - warn$2( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if ("development" !== 'production' && children[i].text !== ' ') { - warn$2( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if ("development" !== 'production' && el.key) { - warn$2( - "`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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if ("development" !== 'production' && slotScope) { - warn$2( - "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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // 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); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn$2); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if ("development" !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - { - var expression = parseText(value, delimiters); - if (expression) { - warn$2( - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - // #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'); - } - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - "development" !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn$2('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _el = el; - while (_el) { - if (_el.for && _el.alias === value) { - warn$2( - "<" + (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 = _el.parent; - } -} - -/* */ - -/** - * Expand input[v-model] with dyanmic 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]"> - */ - -function preTransformNode (el, options) { - if (el.tag === 'input') { - var map = el.attrsMap; - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - var typeBinding = getBindingAttr(el, 'type'); - var ifCondition = getAndRemoveAttr(el, 'v-if', true); - var ifConditionExtra = ifCondition ? ("&&(" + ifCondition + ")") : ""; - var hasElse = getAndRemoveAttr(el, 'v-else', true) != null; - var elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true); - // 1. checkbox - var 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 - var 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 - var 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value; - el.attrsList.push({ name: name, value: value }); -} - -var model$2 = { - preTransformNode: preTransformNode -}; - -var modules$1 = [ - klass$1, - style$1, - model$2 -]; - -/* */ - -function text (el, dir) { - if (dir.value) { - addProp(el, 'textContent', ("_s(" + (dir.value) + ")")); - } -} - -/* */ - -function html (el, dir) { - if (dir.value) { - addProp(el, 'innerHTML', ("_s(" + (dir.value) + ")")); - } -} - -var directives$1 = { - model: model, - text: text, - html: html -}; - -/* */ - -var baseOptions = { - expectHTML: true, - modules: modules$1, - directives: directives$1, - isPreTag: isPreTag, - isUnaryTag: isUnaryTag, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - staticKeys: genStaticKeys(modules$1) -}; - -/* */ - -var isStaticKey; -var isPlatformReservedTag; - -var genStaticKeysCached = cached(genStaticKeys$1); - -/** - * 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. - */ -function optimize (root, options) { - if (!root) { return } - isStaticKey = genStaticKeysCached(options.staticKeys || ''); - isPlatformReservedTag = options.isReservedTag || no; - // first pass: mark all non-static nodes. - markStatic$1(root); - // second pass: mark static roots. - markStaticRoots(root, false); -} - -function genStaticKeys$1 (keys) { - return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + - (keys ? ',' + keys : '') - ) -} - -function markStatic$1 (node) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - markStatic$1(child); - if (!child.static) { - node.static = false; - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].block; - markStatic$1(block); - if (!block.static) { - node.static = false; - } - } - } - } -} - -function markStaticRoots (node, isInFor) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - markStaticRoots(node.children[i], isInFor || !!node.for); - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - markStaticRoots(node.ifConditions[i$1].block, isInFor); - } - } - } -} - -function isStatic (node) { - 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) { - while (node.parent) { - node = node.parent; - if (node.tag !== 'template') { - return false - } - if (node.for) { - return true - } - } - return false -} - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if ("development" !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -function on (el, dir) { - if ("development" !== 'production' && dir.modifiers) { - warn("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData$2(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - "development" !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if ("development" !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData$2 (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if ("development" !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn; - delete options.warn; - - /* istanbul ignore if */ - { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -// `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. -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref$1 = createCompiler(baseOptions); -var compileToFunctions = ref$1.compileToFunctions; - -/* */ - -// check whether current browser encodes a char inside attribute values -var div; -function getShouldDecode (href) { - div = div || document.createElement('div'); - div.innerHTML = href ? "<a href=\"\n\"/>" : "<div a=\"\n\"/>"; - return div.innerHTML.indexOf(' ') > 0 -} - -// #3663: IE encodes newlines inside attribute values while other browsers don't -var shouldDecodeNewlines = inBrowser ? getShouldDecode(false) : false; -// #6828: chrome encodes content in a[href] -var shouldDecodeNewlinesForHref = inBrowser ? getShouldDecode(true) : false; - -/* */ - -var idToTemplate = cached(function (id) { - var el = query(id); - return el && el.innerHTML -}); - -var mount = Vue$3.prototype.$mount; -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && query(el); - - /* istanbul ignore if */ - if (el === document.body || el === document.documentElement) { - "development" !== 'production' && warn( - "Do not mount Vue to <html> or <body> - mount to normal elements instead." - ); - return this - } - - var options = this.$options; - // resolve template/el and convert to render function - if (!options.render) { - var template = options.template; - if (template) { - if (typeof template === 'string') { - if (template.charAt(0) === '#') { - template = idToTemplate(template); - /* istanbul ignore if */ - if ("development" !== 'production' && !template) { - warn( - ("Template element not found or is empty: " + (options.template)), - this - ); - } - } - } else if (template.nodeType) { - template = template.innerHTML; - } else { - { - warn('invalid template option:' + template, this); - } - return this - } - } else if (el) { - template = getOuterHTML(el); - } - if (template) { - /* istanbul ignore if */ - if ("development" !== 'production' && config.performance && mark) { - mark('compile'); - } - - var ref = compileToFunctions(template, { - shouldDecodeNewlines: shouldDecodeNewlines, - shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref, - delimiters: options.delimiters, - comments: options.comments - }, this); - var render = ref.render; - var staticRenderFns = ref.staticRenderFns; - options.render = render; - options.staticRenderFns = staticRenderFns; - - /* istanbul ignore if */ - if ("development" !== 'production' && 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) { - if (el.outerHTML) { - return el.outerHTML - } else { - var container = document.createElement('div'); - container.appendChild(el.cloneNode(true)); - return container.innerHTML - } -} - -Vue$3.compile = compileToFunctions; - -return Vue$3; - -}))); diff --git a/dist/vue.min.js b/dist/vue.min.js deleted file mode 100644 index 3bc6c463096..00000000000 --- a/dist/vue.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 Evan You - * Released under the MIT License. - */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";function e(e){return void 0===e||null===e}function t(e){return void 0!==e&&null!==e}function n(e){return!0===e}function r(e){return!1===e}function i(e){return"string"==typeof e||"number"==typeof e||"boolean"==typeof e}function o(e){return null!==e&&"object"==typeof e}function a(e){return"[object Object]"===Oi.call(e)}function s(e){return"[object RegExp]"===Oi.call(e)}function c(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function u(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function l(e){var t=parseFloat(e);return isNaN(t)?e:t}function f(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i<r.length;i++)n[r[i]]=!0;return t?function(e){return n[e.toLowerCase()]}:function(e){return n[e]}}function d(e,t){if(e.length){var n=e.indexOf(t);if(n>-1)return e.splice(n,1)}}function p(e,t){return Ei.call(e,t)}function v(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}function h(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n}function m(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function y(e,t){for(var n in t)e[n]=t[n];return e}function g(e){for(var t={},n=0;n<e.length;n++)e[n]&&y(t,e[n]);return t}function _(e,t,n){}function b(e,t){if(e===t)return!0;var n=o(e),r=o(t);if(!n||!r)return!n&&!r&&String(e)===String(t);try{var i=Array.isArray(e),a=Array.isArray(t);if(i&&a)return e.length===t.length&&e.every(function(e,n){return b(e,t[n])});if(i||a)return!1;var s=Object.keys(e),c=Object.keys(t);return s.length===c.length&&s.every(function(n){return b(e[n],t[n])})}catch(e){return!1}}function $(e,t){for(var n=0;n<e.length;n++)if(b(e[n],t))return n;return-1}function C(e){var t=!1;return function(){t||(t=!0,e.apply(this,arguments))}}function w(e){var t=(e+"").charCodeAt(0);return 36===t||95===t}function x(e,t,n,r){Object.defineProperty(e,t,{value:n,enumerable:!!r,writable:!0,configurable:!0})}function k(e){if(!Vi.test(e)){var t=e.split(".");return function(e){for(var n=0;n<t.length;n++){if(!e)return;e=e[t[n]]}return e}}}function A(e){return"function"==typeof e&&/native code/.test(e.toString())}function O(e){co.target&&uo.push(co.target),co.target=e}function S(){co.target=uo.pop()}function T(e){return new lo(void 0,void 0,void 0,String(e))}function E(e,t){var n=e.componentOptions,r=new lo(e.tag,e.data,e.children,e.text,e.elm,e.context,n,e.asyncFactory);return r.ns=e.ns,r.isStatic=e.isStatic,r.key=e.key,r.isComment=e.isComment,r.isCloned=!0,t&&(e.children&&(r.children=j(e.children,!0)),n&&n.children&&(n.children=j(n.children,!0))),r}function j(e,t){for(var n=e.length,r=new Array(n),i=0;i<n;i++)r[i]=E(e[i],t);return r}function N(e,t,n){e.__proto__=t}function L(e,t,n){for(var r=0,i=n.length;r<i;r++){var o=n[r];x(e,o,t[o])}}function I(e,t){if(o(e)&&!(e instanceof lo)){var n;return p(e,"__ob__")&&e.__ob__ instanceof go?n=e.__ob__:yo.shouldConvert&&!ro()&&(Array.isArray(e)||a(e))&&Object.isExtensible(e)&&!e._isVue&&(n=new go(e)),t&&n&&n.vmCount++,n}}function M(e,t,n,r,i){var o=new co,a=Object.getOwnPropertyDescriptor(e,t);if(!a||!1!==a.configurable){var s=a&&a.get,c=a&&a.set,u=!i&&I(n);Object.defineProperty(e,t,{enumerable:!0,configurable:!0,get:function(){var t=s?s.call(e):n;return co.target&&(o.depend(),u&&(u.dep.depend(),Array.isArray(t)&&F(t))),t},set:function(t){var r=s?s.call(e):n;t===r||t!==t&&r!==r||(c?c.call(e,t):n=t,u=!i&&I(t),o.notify())}})}}function D(e,t,n){if(Array.isArray(e)&&c(t))return e.length=Math.max(e.length,t),e.splice(t,1,n),n;if(t in e&&!(t in Object.prototype))return e[t]=n,n;var r=e.__ob__;return e._isVue||r&&r.vmCount?n:r?(M(r.value,t,n),r.dep.notify(),n):(e[t]=n,n)}function P(e,t){if(Array.isArray(e)&&c(t))e.splice(t,1);else{var n=e.__ob__;e._isVue||n&&n.vmCount||p(e,t)&&(delete e[t],n&&n.dep.notify())}}function F(e){for(var t=void 0,n=0,r=e.length;n<r;n++)(t=e[n])&&t.__ob__&&t.__ob__.dep.depend(),Array.isArray(t)&&F(t)}function R(e,t){if(!t)return e;for(var n,r,i,o=Object.keys(t),s=0;s<o.length;s++)r=e[n=o[s]],i=t[n],p(e,n)?a(r)&&a(i)&&R(r,i):D(e,n,i);return e}function H(e,t,n){return n?function(){var r="function"==typeof t?t.call(n):t,i="function"==typeof e?e.call(n):e;return r?R(r,i):i}:t?e?function(){return R("function"==typeof t?t.call(this):t,"function"==typeof e?e.call(this):e)}:t:e}function B(e,t){return t?e?e.concat(t):Array.isArray(t)?t:[t]:e}function U(e,t,n,r){var i=Object.create(e||null);return t?y(i,t):i}function V(e,t){var n=e.props;if(n){var r,i,o={};if(Array.isArray(n))for(r=n.length;r--;)"string"==typeof(i=n[r])&&(o[Ni(i)]={type:null});else if(a(n))for(var s in n)i=n[s],o[Ni(s)]=a(i)?i:{type:i};e.props=o}}function z(e,t){var n=e.inject,r=e.inject={};if(Array.isArray(n))for(var i=0;i<n.length;i++)r[n[i]]={from:n[i]};else if(a(n))for(var o in n){var s=n[o];r[o]=a(s)?y({from:o},s):{from:s}}}function K(e){var t=e.directives;if(t)for(var n in t){var r=t[n];"function"==typeof r&&(t[n]={bind:r,update:r})}}function J(e,t,n){function r(r){var i=_o[r]||Co;c[r]=i(e[r],t[r],n,r)}"function"==typeof t&&(t=t.options),V(t,n),z(t,n),K(t);var i=t.extends;if(i&&(e=J(e,i,n)),t.mixins)for(var o=0,a=t.mixins.length;o<a;o++)e=J(e,t.mixins[o],n);var s,c={};for(s in e)r(s);for(s in t)p(e,s)||r(s);return c}function q(e,t,n,r){if("string"==typeof n){var i=e[t];if(p(i,n))return i[n];var o=Ni(n);if(p(i,o))return i[o];var a=Li(o);if(p(i,a))return i[a];var s=i[n]||i[o]||i[a];return s}}function W(e,t,n,r){var i=t[e],o=!p(n,e),a=n[e];if(Y(Boolean,i.type)&&(o&&!p(i,"default")?a=!1:Y(String,i.type)||""!==a&&a!==Mi(e)||(a=!0)),void 0===a){a=G(r,i,e);var s=yo.shouldConvert;yo.shouldConvert=!0,I(a),yo.shouldConvert=s}return a}function G(e,t,n){if(p(t,"default")){var r=t.default;return e&&e.$options.propsData&&void 0===e.$options.propsData[n]&&void 0!==e._props[n]?e._props[n]:"function"==typeof r&&"Function"!==Z(t.type)?r.call(e):r}}function Z(e){var t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:""}function Y(e,t){if(!Array.isArray(t))return Z(t)===Z(e);for(var n=0,r=t.length;n<r;n++)if(Z(t[n])===Z(e))return!0;return!1}function Q(e,t,n){if(t)for(var r=t;r=r.$parent;){var i=r.$options.errorCaptured;if(i)for(var o=0;o<i.length;o++)try{if(!1===i[o].call(r,e,t,n))return}catch(e){X(e,r,"errorCaptured hook")}}X(e,t,n)}function X(e,t,n){if(Bi.errorHandler)try{return Bi.errorHandler.call(null,e,t,n)}catch(e){ee(e,null,"config.errorHandler")}ee(e,t,n)}function ee(e,t,n){if(!Ki||"undefined"==typeof console)throw e;console.error(e)}function te(){xo=!1;var e=wo.slice(0);wo.length=0;for(var t=0;t<e.length;t++)e[t]()}function ne(e){return e._withTask||(e._withTask=function(){ko=!0;var t=e.apply(null,arguments);return ko=!1,t})}function re(e,t){var n;if(wo.push(function(){if(e)try{e.call(t)}catch(e){Q(e,t,"nextTick")}else n&&n(t)}),xo||(xo=!0,ko?$o():bo()),!e&&"undefined"!=typeof Promise)return new Promise(function(e){n=e})}function ie(e){function t(){var e=arguments,n=t.fns;if(!Array.isArray(n))return n.apply(null,arguments);for(var r=n.slice(),i=0;i<r.length;i++)r[i].apply(null,e)}return t.fns=e,t}function oe(t,n,r,i,o){var a,s,c,u;for(a in t)s=t[a],c=n[a],u=Eo(a),e(s)||(e(c)?(e(s.fns)&&(s=t[a]=ie(s)),r(u.name,s,u.once,u.capture,u.passive)):s!==c&&(c.fns=s,t[a]=c));for(a in n)e(t[a])&&i((u=Eo(a)).name,n[a],u.capture)}function ae(r,i,o){function a(){o.apply(this,arguments),d(s.fns,a)}r instanceof lo&&(r=r.data.hook||(r.data.hook={}));var s,c=r[i];e(c)?s=ie([a]):t(c.fns)&&n(c.merged)?(s=c).fns.push(a):s=ie([c,a]),s.merged=!0,r[i]=s}function se(n,r,i){var o=r.options.props;if(!e(o)){var a={},s=n.attrs,c=n.props;if(t(s)||t(c))for(var u in o){var l=Mi(u);ce(a,c,u,l,!0)||ce(a,s,u,l,!1)}return a}}function ce(e,n,r,i,o){if(t(n)){if(p(n,r))return e[r]=n[r],o||delete n[r],!0;if(p(n,i))return e[r]=n[i],o||delete n[i],!0}return!1}function ue(e){for(var t=0;t<e.length;t++)if(Array.isArray(e[t]))return Array.prototype.concat.apply([],e);return e}function le(e){return i(e)?[T(e)]:Array.isArray(e)?de(e):void 0}function fe(e){return t(e)&&t(e.text)&&r(e.isComment)}function de(r,o){var a,s,c,u,l=[];for(a=0;a<r.length;a++)e(s=r[a])||"boolean"==typeof s||(u=l[c=l.length-1],Array.isArray(s)?s.length>0&&(fe((s=de(s,(o||"")+"_"+a))[0])&&fe(u)&&(l[c]=T(u.text+s[0].text),s.shift()),l.push.apply(l,s)):i(s)?fe(u)?l[c]=T(u.text+s):""!==s&&l.push(T(s)):fe(s)&&fe(u)?l[c]=T(u.text+s.text):(n(r._isVList)&&t(s.tag)&&e(s.key)&&t(o)&&(s.key="__vlist"+o+"_"+a+"__"),l.push(s)));return l}function pe(e,t){return(e.__esModule||oo&&"Module"===e[Symbol.toStringTag])&&(e=e.default),o(e)?t.extend(e):e}function ve(e,t,n,r,i){var o=po();return o.asyncFactory=e,o.asyncMeta={data:t,context:n,children:r,tag:i},o}function he(r,i,a){if(n(r.error)&&t(r.errorComp))return r.errorComp;if(t(r.resolved))return r.resolved;if(n(r.loading)&&t(r.loadingComp))return r.loadingComp;if(!t(r.contexts)){var s=r.contexts=[a],c=!0,u=function(){for(var e=0,t=s.length;e<t;e++)s[e].$forceUpdate()},l=C(function(e){r.resolved=pe(e,i),c||u()}),f=C(function(e){t(r.errorComp)&&(r.error=!0,u())}),d=r(l,f);return o(d)&&("function"==typeof d.then?e(r.resolved)&&d.then(l,f):t(d.component)&&"function"==typeof d.component.then&&(d.component.then(l,f),t(d.error)&&(r.errorComp=pe(d.error,i)),t(d.loading)&&(r.loadingComp=pe(d.loading,i),0===d.delay?r.loading=!0:setTimeout(function(){e(r.resolved)&&e(r.error)&&(r.loading=!0,u())},d.delay||200)),t(d.timeout)&&setTimeout(function(){e(r.resolved)&&f(null)},d.timeout))),c=!1,r.loading?r.loadingComp:r.resolved}r.contexts.push(a)}function me(e){return e.isComment&&e.asyncFactory}function ye(e){if(Array.isArray(e))for(var n=0;n<e.length;n++){var r=e[n];if(t(r)&&(t(r.componentOptions)||me(r)))return r}}function ge(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&$e(e,t)}function _e(e,t,n){n?To.$once(e,t):To.$on(e,t)}function be(e,t){To.$off(e,t)}function $e(e,t,n){To=e,oe(t,n||{},_e,be,e),To=void 0}function Ce(e,t){var n={};if(!e)return n;for(var r=0,i=e.length;r<i;r++){var o=e[r],a=o.data;if(a&&a.attrs&&a.attrs.slot&&delete a.attrs.slot,o.context!==t&&o.functionalContext!==t||!a||null==a.slot)(n.default||(n.default=[])).push(o);else{var s=o.data.slot,c=n[s]||(n[s]=[]);"template"===o.tag?c.push.apply(c,o.children):c.push(o)}}for(var u in n)n[u].every(we)&&delete n[u];return n}function we(e){return e.isComment||" "===e.text}function xe(e,t){t=t||{};for(var n=0;n<e.length;n++)Array.isArray(e[n])?xe(e[n],t):t[e[n].key]=e[n].fn;return t}function ke(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}function Ae(e,t,n){e.$el=t,e.$options.render||(e.$options.render=po),je(e,"beforeMount");var r;return r=function(){e._update(e._render(),n)},e._watcher=new Ro(e,r,_),n=!1,null==e.$vnode&&(e._isMounted=!0,je(e,"mounted")),e}function Oe(e,t,n,r,i){var o=!!(i||e.$options._renderChildren||r.data.scopedSlots||e.$scopedSlots!==Ui);if(e.$options._parentVnode=r,e.$vnode=r,e._vnode&&(e._vnode.parent=r),e.$options._renderChildren=i,e.$attrs=r.data&&r.data.attrs||Ui,e.$listeners=n||Ui,t&&e.$options.props){yo.shouldConvert=!1;for(var a=e._props,s=e.$options._propKeys||[],c=0;c<s.length;c++){var u=s[c];a[u]=W(u,e.$options.props,t,e)}yo.shouldConvert=!0,e.$options.propsData=t}if(n){var l=e.$options._parentListeners;e.$options._parentListeners=n,$e(e,n,l)}o&&(e.$slots=Ce(i,r.context),e.$forceUpdate())}function Se(e){for(;e&&(e=e.$parent);)if(e._inactive)return!0;return!1}function Te(e,t){if(t){if(e._directInactive=!1,Se(e))return}else if(e._directInactive)return;if(e._inactive||null===e._inactive){e._inactive=!1;for(var n=0;n<e.$children.length;n++)Te(e.$children[n]);je(e,"activated")}}function Ee(e,t){if(!(t&&(e._directInactive=!0,Se(e))||e._inactive)){e._inactive=!0;for(var n=0;n<e.$children.length;n++)Ee(e.$children[n]);je(e,"deactivated")}}function je(e,t){var n=e.$options[t];if(n)for(var r=0,i=n.length;r<i;r++)try{n[r].call(e)}catch(n){Q(n,e,t+" hook")}e._hasHookEvent&&e.$emit("hook:"+t)}function Ne(){Po=No.length=Lo.length=0,Io={},Mo=Do=!1}function Le(){Do=!0;var e,t;for(No.sort(function(e,t){return e.id-t.id}),Po=0;Po<No.length;Po++)t=(e=No[Po]).id,Io[t]=null,e.run();var n=Lo.slice(),r=No.slice();Ne(),De(n),Ie(r),io&&Bi.devtools&&io.emit("flush")}function Ie(e){for(var t=e.length;t--;){var n=e[t],r=n.vm;r._watcher===n&&r._isMounted&&je(r,"updated")}}function Me(e){e._inactive=!1,Lo.push(e)}function De(e){for(var t=0;t<e.length;t++)e[t]._inactive=!0,Te(e[t],!0)}function Pe(e){var t=e.id;if(null==Io[t]){if(Io[t]=!0,Do){for(var n=No.length-1;n>Po&&No[n].id>e.id;)n--;No.splice(n+1,0,e)}else No.push(e);Mo||(Mo=!0,re(Le))}}function Fe(e){Ho.clear(),Re(e,Ho)}function Re(e,t){var n,r,i=Array.isArray(e);if((i||o(e))&&Object.isExtensible(e)){if(e.__ob__){var a=e.__ob__.dep.id;if(t.has(a))return;t.add(a)}if(i)for(n=e.length;n--;)Re(e[n],t);else for(n=(r=Object.keys(e)).length;n--;)Re(e[r[n]],t)}}function He(e,t,n){Bo.get=function(){return this[t][n]},Bo.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Bo)}function Be(e){e._watchers=[];var t=e.$options;t.props&&Ue(e,t.props),t.methods&&We(e,t.methods),t.data?Ve(e):I(e._data={},!0),t.computed&&Ke(e,t.computed),t.watch&&t.watch!==Qi&&Ge(e,t.watch)}function Ue(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[],o=!e.$parent;yo.shouldConvert=o;for(var a in t)!function(o){i.push(o);var a=W(o,t,n,e);M(r,o,a),o in e||He(e,"_props",o)}(a);yo.shouldConvert=!0}function Ve(e){var t=e.$options.data;a(t=e._data="function"==typeof t?ze(t,e):t||{})||(t={});for(var n=Object.keys(t),r=e.$options.props,i=n.length;i--;){var o=n[i];r&&p(r,o)||w(o)||He(e,"_data",o)}I(t,!0)}function ze(e,t){try{return e.call(t,t)}catch(e){return Q(e,t,"data()"),{}}}function Ke(e,t){var n=e._computedWatchers=Object.create(null),r=ro();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new Ro(e,a||_,_,Uo)),i in e||Je(e,i,o)}}function Je(e,t,n){var r=!ro();"function"==typeof n?(Bo.get=r?qe(t):n,Bo.set=_):(Bo.get=n.get?r&&!1!==n.cache?qe(t):n.get:_,Bo.set=n.set?n.set:_),Object.defineProperty(e,t,Bo)}function qe(e){return function(){var t=this._computedWatchers&&this._computedWatchers[e];if(t)return t.dirty&&t.evaluate(),co.target&&t.depend(),t.value}}function We(e,t){for(var n in t)e[n]=null==t[n]?_:h(t[n],e)}function Ge(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i<r.length;i++)Ze(e,n,r[i]);else Ze(e,n,r)}}function Ze(e,t,n,r){return a(n)&&(r=n,n=n.handler),"string"==typeof n&&(n=e[n]),e.$watch(t,n,r)}function Ye(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}function Qe(e){var t=Xe(e.$options.inject,e);t&&(yo.shouldConvert=!1,Object.keys(t).forEach(function(n){M(e,n,t[n])}),yo.shouldConvert=!0)}function Xe(e,t){if(e){for(var n=Object.create(null),r=oo?Reflect.ownKeys(e).filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}):Object.keys(e),i=0;i<r.length;i++){for(var o=r[i],a=e[o].from,s=t;s;){if(s._provided&&a in s._provided){n[o]=s._provided[a];break}s=s.$parent}if(!s&&"default"in e[o]){var c=e[o].default;n[o]="function"==typeof c?c.call(t):c}}return n}}function et(e,n){var r,i,a,s,c;if(Array.isArray(e)||"string"==typeof e)for(r=new Array(e.length),i=0,a=e.length;i<a;i++)r[i]=n(e[i],i);else if("number"==typeof e)for(r=new Array(e),i=0;i<e;i++)r[i]=n(i+1,i);else if(o(e))for(s=Object.keys(e),r=new Array(s.length),i=0,a=s.length;i<a;i++)c=s[i],r[i]=n(e[c],c,i);return t(r)&&(r._isVList=!0),r}function tt(e,t,n,r){var i,o=this.$scopedSlots[e];if(o)n=n||{},r&&(n=y(y({},r),n)),i=o(n)||t;else{var a=this.$slots[e];a&&(a._rendered=!0),i=a||t}var s=n&&n.slot;return s?this.$createElement("template",{slot:s},i):i}function nt(e){return q(this.$options,"filters",e,!0)||Pi}function rt(e,t,n,r){var i=Bi.keyCodes[t]||n;return i?Array.isArray(i)?-1===i.indexOf(e):i!==e:r?Mi(r)!==t:void 0}function it(e,t,n,r,i){if(n)if(o(n)){Array.isArray(n)&&(n=g(n));var a;for(var s in n)!function(o){if("class"===o||"style"===o||Ti(o))a=e;else{var s=e.attrs&&e.attrs.type;a=r||Bi.mustUseProp(t,s,o)?e.domProps||(e.domProps={}):e.attrs||(e.attrs={})}o in a||(a[o]=n[o],i&&((e.on||(e.on={}))["update:"+o]=function(e){n[o]=e}))}(s)}else;return e}function ot(e,t){var n=this.$options,r=n.cached||(n.cached=[]),i=r[e];return i&&!t?Array.isArray(i)?j(i):E(i):(i=r[e]=n.staticRenderFns[e].call(this._renderProxy,null,this),st(i,"__static__"+e,!1),i)}function at(e,t,n){return st(e,"__once__"+t+(n?"_"+n:""),!0),e}function st(e,t,n){if(Array.isArray(e))for(var r=0;r<e.length;r++)e[r]&&"string"!=typeof e[r]&&ct(e[r],t+"_"+r,n);else ct(e,t,n)}function ct(e,t,n){e.isStatic=!0,e.key=t,e.isOnce=n}function ut(e,t){if(t)if(a(t)){var n=e.on=e.on?y({},e.on):{};for(var r in t){var i=n[r],o=t[r];n[r]=i?[].concat(i,o):o}}else;return e}function lt(e){e._o=at,e._n=l,e._s=u,e._l=et,e._t=tt,e._q=b,e._i=$,e._m=ot,e._f=nt,e._k=rt,e._b=it,e._v=T,e._e=po,e._u=xe,e._g=ut}function ft(e,t,r,i,o){var a=o.options;this.data=e,this.props=t,this.children=r,this.parent=i,this.listeners=e.on||Ui,this.injections=Xe(a.inject,i),this.slots=function(){return Ce(r,i)};var s=Object.create(i),c=n(a._compiled),u=!c;c&&(this.$options=a,this.$slots=this.slots(),this.$scopedSlots=e.scopedSlots||Ui),a._scopeId?this._c=function(e,t,n,r){var o=_t(s,e,t,n,r,u);return o&&(o.functionalScopeId=a._scopeId,o.functionalContext=i),o}:this._c=function(e,t,n,r){return _t(s,e,t,n,r,u)}}function dt(e,n,r,i,o){var a=e.options,s={},c=a.props;if(t(c))for(var u in c)s[u]=W(u,c,n||Ui);else t(r.attrs)&&pt(s,r.attrs),t(r.props)&&pt(s,r.props);var l=new ft(r,s,o,i,e),f=a.render.call(null,l._c,l);return f instanceof lo&&(f.functionalContext=i,f.functionalOptions=a,r.slot&&((f.data||(f.data={})).slot=r.slot)),f}function pt(e,t){for(var n in t)e[Ni(n)]=t[n]}function vt(r,i,a,s,c){if(!e(r)){var u=a.$options._base;if(o(r)&&(r=u.extend(r)),"function"==typeof r){var l;if(e(r.cid)&&(l=r,void 0===(r=he(l,u,a))))return ve(l,i,a,s,c);i=i||{},xt(r),t(i.model)&>(r.options,i);var f=se(i,r,c);if(n(r.options.functional))return dt(r,f,i,a,s);var d=i.on;if(i.on=i.nativeOn,n(r.options.abstract)){var p=i.slot;i={},p&&(i.slot=p)}mt(i);var v=r.options.name||c;return new lo("vue-component-"+r.cid+(v?"-"+v:""),i,void 0,void 0,void 0,a,{Ctor:r,propsData:f,listeners:d,tag:c,children:s},l)}}}function ht(e,n,r,i){var o=e.componentOptions,a={_isComponent:!0,parent:n,propsData:o.propsData,_componentTag:o.tag,_parentVnode:e,_parentListeners:o.listeners,_renderChildren:o.children,_parentElm:r||null,_refElm:i||null},s=e.data.inlineTemplate;return t(s)&&(a.render=s.render,a.staticRenderFns=s.staticRenderFns),new o.Ctor(a)}function mt(e){e.hook||(e.hook={});for(var t=0;t<zo.length;t++){var n=zo[t],r=e.hook[n],i=Vo[n];e.hook[n]=r?yt(i,r):i}}function yt(e,t){return function(n,r,i,o){e(n,r,i,o),t(n,r,i,o)}}function gt(e,n){var r=e.model&&e.model.prop||"value",i=e.model&&e.model.event||"input";(n.props||(n.props={}))[r]=n.model.value;var o=n.on||(n.on={});t(o[i])?o[i]=[n.model.callback].concat(o[i]):o[i]=n.model.callback}function _t(e,t,r,o,a,s){return(Array.isArray(r)||i(r))&&(a=o,o=r,r=void 0),n(s)&&(a=Jo),bt(e,t,r,o,a)}function bt(e,n,r,i,o){if(t(r)&&t(r.__ob__))return po();if(t(r)&&t(r.is)&&(n=r.is),!n)return po();Array.isArray(i)&&"function"==typeof i[0]&&((r=r||{}).scopedSlots={default:i[0]},i.length=0),o===Jo?i=le(i):o===Ko&&(i=ue(i));var a,s;if("string"==typeof n){var c;s=e.$vnode&&e.$vnode.ns||Bi.getTagNamespace(n),a=Bi.isReservedTag(n)?new lo(Bi.parsePlatformTagName(n),r,i,void 0,void 0,e):t(c=q(e.$options,"components",n))?vt(c,r,e,i,n):new lo(n,r,i,void 0,void 0,e)}else a=vt(n,r,e,i);return t(a)?(s&&$t(a,s),a):po()}function $t(r,i,o){if(r.ns=i,"foreignObject"===r.tag&&(i=void 0,o=!0),t(r.children))for(var a=0,s=r.children.length;a<s;a++){var c=r.children[a];t(c.tag)&&(e(c.ns)||n(o))&&$t(c,i,o)}}function Ct(e){e._vnode=null;var t=e.$options,n=e.$vnode=t._parentVnode,r=n&&n.context;e.$slots=Ce(t._renderChildren,r),e.$scopedSlots=Ui,e._c=function(t,n,r,i){return _t(e,t,n,r,i,!1)},e.$createElement=function(t,n,r,i){return _t(e,t,n,r,i,!0)};var i=n&&n.data;M(e,"$attrs",i&&i.attrs||Ui,null,!0),M(e,"$listeners",t._parentListeners||Ui,null,!0)}function wt(e,t){var n=e.$options=Object.create(e.constructor.options);n.parent=t.parent,n.propsData=t.propsData,n._parentVnode=t._parentVnode,n._parentListeners=t._parentListeners,n._renderChildren=t._renderChildren,n._componentTag=t._componentTag,n._parentElm=t._parentElm,n._refElm=t._refElm,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}function xt(e){var t=e.options;if(e.super){var n=xt(e.super);if(n!==e.superOptions){e.superOptions=n;var r=kt(e);r&&y(e.extendOptions,r),(t=e.options=J(n,e.extendOptions)).name&&(t.components[t.name]=e)}}return t}function kt(e){var t,n=e.options,r=e.extendOptions,i=e.sealedOptions;for(var o in n)n[o]!==i[o]&&(t||(t={}),t[o]=At(n[o],r[o],i[o]));return t}function At(e,t,n){if(Array.isArray(e)){var r=[];n=Array.isArray(n)?n:[n],t=Array.isArray(t)?t:[t];for(var i=0;i<e.length;i++)(t.indexOf(e[i])>=0||n.indexOf(e[i])<0)&&r.push(e[i]);return r}return e}function Ot(e){this._init(e)}function St(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=m(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}function Tt(e){e.mixin=function(e){return this.options=J(this.options,e),this}}function Et(e){e.cid=0;var t=1;e.extend=function(e){e=e||{};var n=this,r=n.cid,i=e._Ctor||(e._Ctor={});if(i[r])return i[r];var o=e.name||n.options.name,a=function(e){this._init(e)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=t++,a.options=J(n.options,e),a.super=n,a.options.props&&jt(a),a.options.computed&&Nt(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,Ri.forEach(function(e){a[e]=n[e]}),o&&(a.options.components[o]=a),a.superOptions=n.options,a.extendOptions=e,a.sealedOptions=y({},a.options),i[r]=a,a}}function jt(e){var t=e.options.props;for(var n in t)He(e.prototype,"_props",n)}function Nt(e){var t=e.options.computed;for(var n in t)Je(e.prototype,n,t[n])}function Lt(e){Ri.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&a(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}function It(e){return e&&(e.Ctor.options.name||e.tag)}function Mt(e,t){return Array.isArray(e)?e.indexOf(t)>-1:"string"==typeof e?e.split(",").indexOf(t)>-1:!!s(e)&&e.test(t)}function Dt(e,t){var n=e.cache,r=e.keys,i=e._vnode;for(var o in n){var a=n[o];if(a){var s=It(a.componentOptions);s&&!t(s)&&Pt(n,o,r,i)}}}function Pt(e,t,n,r){var i=e[t];i&&i!==r&&i.componentInstance.$destroy(),e[t]=null,d(n,t)}function Ft(e){for(var n=e.data,r=e,i=e;t(i.componentInstance);)(i=i.componentInstance._vnode).data&&(n=Rt(i.data,n));for(;t(r=r.parent);)r.data&&(n=Rt(n,r.data));return Ht(n.staticClass,n.class)}function Rt(e,n){return{staticClass:Bt(e.staticClass,n.staticClass),class:t(e.class)?[e.class,n.class]:n.class}}function Ht(e,n){return t(e)||t(n)?Bt(e,Ut(n)):""}function Bt(e,t){return e?t?e+" "+t:e:t||""}function Ut(e){return Array.isArray(e)?Vt(e):o(e)?zt(e):"string"==typeof e?e:""}function Vt(e){for(var n,r="",i=0,o=e.length;i<o;i++)t(n=Ut(e[i]))&&""!==n&&(r&&(r+=" "),r+=n);return r}function zt(e){var t="";for(var n in e)e[n]&&(t&&(t+=" "),t+=n);return t}function Kt(e){return ha(e)?"svg":"math"===e?"math":void 0}function Jt(e){if("string"==typeof e){var t=document.querySelector(e);return t||document.createElement("div")}return e}function qt(e,t){var n=e.data.ref;if(n){var r=e.context,i=e.componentInstance||e.elm,o=r.$refs;t?Array.isArray(o[n])?d(o[n],i):o[n]===i&&(o[n]=void 0):e.data.refInFor?Array.isArray(o[n])?o[n].indexOf(i)<0&&o[n].push(i):o[n]=[i]:o[n]=i}}function Wt(r,i){return r.key===i.key&&(r.tag===i.tag&&r.isComment===i.isComment&&t(r.data)===t(i.data)&&Gt(r,i)||n(r.isAsyncPlaceholder)&&r.asyncFactory===i.asyncFactory&&e(i.asyncFactory.error))}function Gt(e,n){if("input"!==e.tag)return!0;var r,i=t(r=e.data)&&t(r=r.attrs)&&r.type,o=t(r=n.data)&&t(r=r.attrs)&&r.type;return i===o||ga(i)&&ga(o)}function Zt(e,n,r){var i,o,a={};for(i=n;i<=r;++i)t(o=e[i].key)&&(a[o]=i);return a}function Yt(e,t){(e.data.directives||t.data.directives)&&Qt(e,t)}function Qt(e,t){var n,r,i,o=e===$a,a=t===$a,s=Xt(e.data.directives,e.context),c=Xt(t.data.directives,t.context),u=[],l=[];for(n in c)r=s[n],i=c[n],r?(i.oldValue=r.value,tn(i,"update",t,e),i.def&&i.def.componentUpdated&&l.push(i)):(tn(i,"bind",t,e),i.def&&i.def.inserted&&u.push(i));if(u.length){var f=function(){for(var n=0;n<u.length;n++)tn(u[n],"inserted",t,e)};o?ae(t,"insert",f):f()}if(l.length&&ae(t,"postpatch",function(){for(var n=0;n<l.length;n++)tn(l[n],"componentUpdated",t,e)}),!o)for(n in s)c[n]||tn(s[n],"unbind",e,e,a)}function Xt(e,t){var n=Object.create(null);if(!e)return n;var r,i;for(r=0;r<e.length;r++)(i=e[r]).modifiers||(i.modifiers=xa),n[en(i)]=i,i.def=q(t.$options,"directives",i.name,!0);return n}function en(e){return e.rawName||e.name+"."+Object.keys(e.modifiers||{}).join(".")}function tn(e,t,n,r,i){var o=e.def&&e.def[t];if(o)try{o(n.elm,e,n,r,i)}catch(r){Q(r,n.context,"directive "+e.name+" "+t+" hook")}}function nn(n,r){var i=r.componentOptions;if(!(t(i)&&!1===i.Ctor.options.inheritAttrs||e(n.data.attrs)&&e(r.data.attrs))){var o,a,s=r.elm,c=n.data.attrs||{},u=r.data.attrs||{};t(u.__ob__)&&(u=r.data.attrs=y({},u));for(o in u)a=u[o],c[o]!==a&&rn(s,o,a);(Wi||Gi)&&u.value!==c.value&&rn(s,"value",u.value);for(o in c)e(u[o])&&(la(o)?s.removeAttributeNS(ua,fa(o)):sa(o)||s.removeAttribute(o))}}function rn(e,t,n){ca(t)?da(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):sa(t)?e.setAttribute(t,da(n)||"false"===n?"false":"true"):la(t)?da(n)?e.removeAttributeNS(ua,fa(t)):e.setAttributeNS(ua,t,n):da(n)?e.removeAttribute(t):e.setAttribute(t,n)}function on(n,r){var i=r.elm,o=r.data,a=n.data;if(!(e(o.staticClass)&&e(o.class)&&(e(a)||e(a.staticClass)&&e(a.class)))){var s=Ft(r),c=i._transitionClasses;t(c)&&(s=Bt(s,Ut(c))),s!==i._prevClass&&(i.setAttribute("class",s),i._prevClass=s)}}function an(e){function t(){(a||(a=[])).push(e.slice(v,i).trim()),v=i+1}var n,r,i,o,a,s=!1,c=!1,u=!1,l=!1,f=0,d=0,p=0,v=0;for(i=0;i<e.length;i++)if(r=n,n=e.charCodeAt(i),s)39===n&&92!==r&&(s=!1);else if(c)34===n&&92!==r&&(c=!1);else if(u)96===n&&92!==r&&(u=!1);else if(l)47===n&&92!==r&&(l=!1);else if(124!==n||124===e.charCodeAt(i+1)||124===e.charCodeAt(i-1)||f||d||p){switch(n){case 34:c=!0;break;case 39:s=!0;break;case 96:u=!0;break;case 40:p++;break;case 41:p--;break;case 91:d++;break;case 93:d--;break;case 123:f++;break;case 125:f--}if(47===n){for(var h=i-1,m=void 0;h>=0&&" "===(m=e.charAt(h));h--);m&&Sa.test(m)||(l=!0)}}else void 0===o?(v=i+1,o=e.slice(0,i).trim()):t();if(void 0===o?o=e.slice(0,i).trim():0!==v&&t(),a)for(i=0;i<a.length;i++)o=sn(o,a[i]);return o}function sn(e,t){var n=t.indexOf("(");return n<0?'_f("'+t+'")('+e+")":'_f("'+t.slice(0,n)+'")('+e+","+t.slice(n+1)}function cn(e){console.error("[Vue compiler]: "+e)}function un(e,t){return e?e.map(function(e){return e[t]}).filter(function(e){return e}):[]}function ln(e,t,n){(e.props||(e.props=[])).push({name:t,value:n})}function fn(e,t,n){(e.attrs||(e.attrs=[])).push({name:t,value:n})}function dn(e,t,n,r,i,o){(e.directives||(e.directives=[])).push({name:t,rawName:n,value:r,arg:i,modifiers:o})}function pn(e,t,n,r,i,o){r&&r.capture&&(delete r.capture,t="!"+t),r&&r.once&&(delete r.once,t="~"+t),r&&r.passive&&(delete r.passive,t="&"+t);var a;r&&r.native?(delete r.native,a=e.nativeEvents||(e.nativeEvents={})):a=e.events||(e.events={});var s={value:n,modifiers:r},c=a[t];Array.isArray(c)?i?c.unshift(s):c.push(s):a[t]=c?i?[s,c]:[c,s]:s}function vn(e,t,n){var r=hn(e,":"+t)||hn(e,"v-bind:"+t);if(null!=r)return an(r);if(!1!==n){var i=hn(e,t);if(null!=i)return JSON.stringify(i)}}function hn(e,t,n){var r;if(null!=(r=e.attrsMap[t]))for(var i=e.attrsList,o=0,a=i.length;o<a;o++)if(i[o].name===t){i.splice(o,1);break}return n&&delete e.attrsMap[t],r}function mn(e,t,n){var r=n||{},i=r.number,o="$$v";r.trim&&(o="(typeof $$v === 'string'? $$v.trim(): $$v)"),i&&(o="_n("+o+")");var a=yn(t,o);e.model={value:"("+t+")",expression:'"'+t+'"',callback:"function ($$v) {"+a+"}"}}function yn(e,t){var n=gn(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function gn(e){if(Zo=e.length,e.indexOf("[")<0||e.lastIndexOf("]")<Zo-1)return(Xo=e.lastIndexOf("."))>-1?{exp:e.slice(0,Xo),key:'"'+e.slice(Xo+1)+'"'}:{exp:e,key:null};for(Yo=e,Xo=ea=ta=0;!bn();)$n(Qo=_n())?wn(Qo):91===Qo&&Cn(Qo);return{exp:e.slice(0,ea),key:e.slice(ea+1,ta)}}function _n(){return Yo.charCodeAt(++Xo)}function bn(){return Xo>=Zo}function $n(e){return 34===e||39===e}function Cn(e){var t=1;for(ea=Xo;!bn();)if(e=_n(),$n(e))wn(e);else if(91===e&&t++,93===e&&t--,0===t){ta=Xo;break}}function wn(e){for(var t=e;!bn()&&(e=_n())!==t;);}function xn(e,t,n){var r=n&&n.number,i=vn(e,"value")||"null",o=vn(e,"true-value")||"true",a=vn(e,"false-value")||"false";ln(e,"checked","Array.isArray("+t+")?_i("+t+","+i+")>-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),pn(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+t+"=$$a.concat([$$v]))}else{$$i>-1&&("+t+"=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{"+yn(t,"$$c")+"}",null,!0)}function kn(e,t,n){var r=n&&n.number,i=vn(e,"value")||"null";ln(e,"checked","_q("+t+","+(i=r?"_n("+i+")":i)+")"),pn(e,"change",yn(t,i),null,!0)}function An(e,t,n){var r="var $$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 '+(n&&n.number?"_n(val)":"val")+"})")+";";pn(e,"change",r=r+" "+yn(t,"$event.target.multiple ? $$selectedVal : $$selectedVal[0]"),null,!0)}function On(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?Ta:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=yn(t,l);c&&(f="if($event.target.composing)return;"+f),ln(e,"value","("+t+")"),pn(e,u,f,null,!0),(s||a)&&pn(e,"blur","$forceUpdate()")}function Sn(e){if(t(e[Ta])){var n=qi?"change":"input";e[n]=[].concat(e[Ta],e[n]||[]),delete e[Ta]}t(e[Ea])&&(e.change=[].concat(e[Ea],e.change||[]),delete e[Ea])}function Tn(e,t,n){var r=na;return function i(){null!==e.apply(null,arguments)&&jn(t,i,n,r)}}function En(e,t,n,r,i){t=ne(t),n&&(t=Tn(t,e,r)),na.addEventListener(e,t,Xi?{capture:r,passive:i}:r)}function jn(e,t,n,r){(r||na).removeEventListener(e,t._withTask||t,n)}function Nn(t,n){if(!e(t.data.on)||!e(n.data.on)){var r=n.data.on||{},i=t.data.on||{};na=n.elm,Sn(r),oe(r,i,En,jn,n.context),na=void 0}}function Ln(n,r){if(!e(n.data.domProps)||!e(r.data.domProps)){var i,o,a=r.elm,s=n.data.domProps||{},c=r.data.domProps||{};t(c.__ob__)&&(c=r.data.domProps=y({},c));for(i in s)e(c[i])&&(a[i]="");for(i in c){if(o=c[i],"textContent"===i||"innerHTML"===i){if(r.children&&(r.children.length=0),o===s[i])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===i){a._value=o;var u=e(o)?"":String(o);In(a,u)&&(a.value=u)}else a[i]=o}}}function In(e,t){return!e.composing&&("OPTION"===e.tagName||Mn(e,t)||Dn(e,t))}function Mn(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}function Dn(e,n){var r=e.value,i=e._vModifiers;return t(i)&&i.number?l(r)!==l(n):t(i)&&i.trim?r.trim()!==n.trim():r!==n}function Pn(e){var t=Fn(e.style);return e.staticStyle?y(e.staticStyle,t):t}function Fn(e){return Array.isArray(e)?g(e):"string"==typeof e?La(e):e}function Rn(e,t){var n,r={};if(t)for(var i=e;i.componentInstance;)(i=i.componentInstance._vnode).data&&(n=Pn(i.data))&&y(r,n);(n=Pn(e.data))&&y(r,n);for(var o=e;o=o.parent;)o.data&&(n=Pn(o.data))&&y(r,n);return r}function Hn(n,r){var i=r.data,o=n.data;if(!(e(i.staticStyle)&&e(i.style)&&e(o.staticStyle)&&e(o.style))){var a,s,c=r.elm,u=o.staticStyle,l=o.normalizedStyle||o.style||{},f=u||l,d=Fn(r.data.style)||{};r.data.normalizedStyle=t(d.__ob__)?y({},d):d;var p=Rn(r,!0);for(s in f)e(p[s])&&Da(c,s,"");for(s in p)(a=p[s])!==f[s]&&Da(c,s,null==a?"":a)}}function Bn(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function Un(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function Vn(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&y(t,Ha(e.name||"v")),y(t,e),t}return"string"==typeof e?Ha(e):void 0}}function zn(e){Wa(function(){Wa(e)})}function Kn(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),Bn(e,t))}function Jn(e,t){e._transitionClasses&&d(e._transitionClasses,t),Un(e,t)}function qn(e,t,n){var r=Wn(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===Ua?Ka:qa,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c<a&&u()},o+1),e.addEventListener(s,l)}function Wn(e,t){var n,r=window.getComputedStyle(e),i=r[za+"Delay"].split(", "),o=r[za+"Duration"].split(", "),a=Gn(i,o),s=r[Ja+"Delay"].split(", "),c=r[Ja+"Duration"].split(", "),u=Gn(s,c),l=0,f=0;return t===Ua?a>0&&(n=Ua,l=a,f=o.length):t===Va?u>0&&(n=Va,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?Ua:Va:null)?n===Ua?o.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===Ua&&Ga.test(r[za+"Property"])}}function Gn(e,t){for(;e.length<t.length;)e=e.concat(e);return Math.max.apply(null,t.map(function(t,n){return Zn(t)+Zn(e[n])}))}function Zn(e){return 1e3*Number(e.slice(0,-1))}function Yn(n,r){var i=n.elm;t(i._leaveCb)&&(i._leaveCb.cancelled=!0,i._leaveCb());var a=Vn(n.data.transition);if(!e(a)&&!t(i._enterCb)&&1===i.nodeType){for(var s=a.css,c=a.type,u=a.enterClass,f=a.enterToClass,d=a.enterActiveClass,p=a.appearClass,v=a.appearToClass,h=a.appearActiveClass,m=a.beforeEnter,y=a.enter,g=a.afterEnter,_=a.enterCancelled,b=a.beforeAppear,$=a.appear,w=a.afterAppear,x=a.appearCancelled,k=a.duration,A=jo,O=jo.$vnode;O&&O.parent;)A=(O=O.parent).context;var S=!A._isMounted||!n.isRootInsert;if(!S||$||""===$){var T=S&&p?p:u,E=S&&h?h:d,j=S&&v?v:f,N=S?b||m:m,L=S&&"function"==typeof $?$:y,I=S?w||g:g,M=S?x||_:_,D=l(o(k)?k.enter:k),P=!1!==s&&!Wi,F=er(L),R=i._enterCb=C(function(){P&&(Jn(i,j),Jn(i,E)),R.cancelled?(P&&Jn(i,T),M&&M(i)):I&&I(i),i._enterCb=null});n.data.show||ae(n,"insert",function(){var e=i.parentNode,t=e&&e._pending&&e._pending[n.key];t&&t.tag===n.tag&&t.elm._leaveCb&&t.elm._leaveCb(),L&&L(i,R)}),N&&N(i),P&&(Kn(i,T),Kn(i,E),zn(function(){Kn(i,j),Jn(i,T),R.cancelled||F||(Xn(D)?setTimeout(R,D):qn(i,c,R))})),n.data.show&&(r&&r(),L&&L(i,R)),P||F||R()}}}function Qn(n,r){function i(){x.cancelled||(n.data.show||((a.parentNode._pending||(a.parentNode._pending={}))[n.key]=n),v&&v(a),b&&(Kn(a,f),Kn(a,p),zn(function(){Kn(a,d),Jn(a,f),x.cancelled||$||(Xn(w)?setTimeout(x,w):qn(a,u,x))})),h&&h(a,x),b||$||x())}var a=n.elm;t(a._enterCb)&&(a._enterCb.cancelled=!0,a._enterCb());var s=Vn(n.data.transition);if(e(s))return r();if(!t(a._leaveCb)&&1===a.nodeType){var c=s.css,u=s.type,f=s.leaveClass,d=s.leaveToClass,p=s.leaveActiveClass,v=s.beforeLeave,h=s.leave,m=s.afterLeave,y=s.leaveCancelled,g=s.delayLeave,_=s.duration,b=!1!==c&&!Wi,$=er(h),w=l(o(_)?_.leave:_),x=a._leaveCb=C(function(){a.parentNode&&a.parentNode._pending&&(a.parentNode._pending[n.key]=null),b&&(Jn(a,d),Jn(a,p)),x.cancelled?(b&&Jn(a,f),y&&y(a)):(r(),m&&m(a)),a._leaveCb=null});g?g(i):i()}}function Xn(e){return"number"==typeof e&&!isNaN(e)}function er(n){if(e(n))return!1;var r=n.fns;return t(r)?er(Array.isArray(r)?r[0]:r):(n._length||n.length)>1}function tr(e,t){!0!==t.data.show&&Yn(t)}function nr(e,t,n){rr(e,t,n),(qi||Gi)&&setTimeout(function(){rr(e,t,n)},0)}function rr(e,t,n){var r=t.value,i=e.multiple;if(!i||Array.isArray(r)){for(var o,a,s=0,c=e.options.length;s<c;s++)if(a=e.options[s],i)o=$(r,or(a))>-1,a.selected!==o&&(a.selected=o);else if(b(or(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function ir(e,t){return t.every(function(t){return!b(t,e)})}function or(e){return"_value"in e?e._value:e.value}function ar(e){e.target.composing=!0}function sr(e){e.target.composing&&(e.target.composing=!1,cr(e.target,"input"))}function cr(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function ur(e){return!e.componentInstance||e.data&&e.data.transition?e:ur(e.componentInstance._vnode)}function lr(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?lr(ye(t.children)):e}function fr(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[Ni(o)]=i[o];return t}function dr(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}function pr(e){for(;e=e.parent;)if(e.data.transition)return!0}function vr(e,t){return t.key===e.key&&t.tag===e.tag}function hr(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function mr(e){e.data.newPos=e.elm.getBoundingClientRect()}function yr(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}function gr(e,t){var n=t?as(t):is;if(n.test(e)){for(var r,i,o=[],a=n.lastIndex=0;r=n.exec(e);){(i=r.index)>a&&o.push(JSON.stringify(e.slice(a,i)));var s=an(r[1].trim());o.push("_s("+s+")"),a=i+r[0].length}return a<e.length&&o.push(JSON.stringify(e.slice(a))),o.join("+")}}function _r(e,t){var n=t?Fs:Ps;return e.replace(n,function(e){return Ds[e]})}function br(e,t){function n(t){l+=t,e=e.substring(t)}function r(e,n,r){var i,s;if(null==n&&(n=l),null==r&&(r=l),e&&(s=e.toLowerCase()),e)for(i=a.length-1;i>=0&&a[i].lowerCasedTag!==s;i--);else i=0;if(i>=0){for(var c=a.length-1;c>=i;c--)t.end&&t.end(a[c].tag,n,r);a.length=i,o=i&&a[i-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,r):"p"===s&&(t.start&&t.start(e,[],!1,n,r),t.end&&t.end(e,n,r))}for(var i,o,a=[],s=t.expectHTML,c=t.isUnaryTag||Di,u=t.canBeLeftOpenTag||Di,l=0;e;){if(i=e,o&&Is(o)){var f=0,d=o.toLowerCase(),p=Ms[d]||(Ms[d]=new RegExp("([\\s\\S]*?)(</"+d+"[^>]*>)","i")),v=e.replace(p,function(e,n,r){return f=r.length,Is(d)||"noscript"===d||(n=n.replace(/<!--([\s\S]*?)-->/g,"$1").replace(/<!\[CDATA\[([\s\S]*?)]]>/g,"$1")),Hs(d,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});l+=e.length-v.length,e=v,r(d,l-f,l)}else{var h=e.indexOf("<");if(0===h){if(bs.test(e)){var m=e.indexOf("--\x3e");if(m>=0){t.shouldKeepComment&&t.comment(e.substring(4,m)),n(m+3);continue}}if($s.test(e)){var y=e.indexOf("]>");if(y>=0){n(y+2);continue}}var g=e.match(_s);if(g){n(g[0].length);continue}var _=e.match(gs);if(_){var b=l;n(_[0].length),r(_[1],b,l);continue}var $=function(){var t=e.match(ms);if(t){var r={tagName:t[1],attrs:[],start:l};n(t[0].length);for(var i,o;!(i=e.match(ys))&&(o=e.match(ps));)n(o[0].length),r.attrs.push(o);if(i)return r.unarySlash=i[1],n(i[0].length),r.end=l,r}}();if($){!function(e){var n=e.tagName,i=e.unarySlash;s&&("p"===o&&ds(n)&&r(o),u(n)&&o===n&&r(n));for(var l=c(n)||!!i,f=e.attrs.length,d=new Array(f),p=0;p<f;p++){var v=e.attrs[p];Cs&&-1===v[0].indexOf('""')&&(""===v[3]&&delete v[3],""===v[4]&&delete v[4],""===v[5]&&delete v[5]);var h=v[3]||v[4]||v[5]||"",m="a"===n&&"href"===v[1]?t.shouldDecodeNewlinesForHref:t.shouldDecodeNewlines;d[p]={name:v[1],value:_r(h,m)}}l||(a.push({tag:n,lowerCasedTag:n.toLowerCase(),attrs:d}),o=n),t.start&&t.start(n,d,l,e.start,e.end)}($),Hs(o,e)&&n(1);continue}}var C=void 0,w=void 0,x=void 0;if(h>=0){for(w=e.slice(h);!(gs.test(w)||ms.test(w)||bs.test(w)||$s.test(w)||(x=w.indexOf("<",1))<0);)h+=x,w=e.slice(h);C=e.substring(0,h),n(h)}h<0&&(C=e,e=""),t.chars&&C&&t.chars(C)}if(e===i){t.chars&&t.chars(e);break}}r()}function $r(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:Rr(t),parent:n,children:[]}}function Cr(e,t){function n(e){e.pre&&(s=!1),Ss(e.tag)&&(c=!1)}ws=t.warn||cn,Ss=t.isPreTag||Di,Ts=t.mustUseProp||Di,Es=t.getTagNamespace||Di,ks=un(t.modules,"transformNode"),As=un(t.modules,"preTransformNode"),Os=un(t.modules,"postTransformNode"),xs=t.delimiters;var r,i,o=[],a=!1!==t.preserveWhitespace,s=!1,c=!1;return br(e,{warn:ws,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,start:function(e,a,u){var l=i&&i.ns||Es(e);qi&&"svg"===l&&(a=Ur(a));var f=$r(e,a,i);l&&(f.ns=l),Br(f)&&!ro()&&(f.forbidden=!0);for(var d=0;d<As.length;d++)f=As[d](f,t)||f;if(s||(wr(f),f.pre&&(s=!0)),Ss(f.tag)&&(c=!0),s?xr(f):f.processed||(Sr(f),Tr(f),Lr(f),kr(f,t)),r?o.length||r.if&&(f.elseif||f.else)&&Nr(r,{exp:f.elseif,block:f}):r=f,i&&!f.forbidden)if(f.elseif||f.else)Er(f,i);else if(f.slotScope){i.plain=!1;var p=f.slotTarget||'"default"';(i.scopedSlots||(i.scopedSlots={}))[p]=f}else i.children.push(f),f.parent=i;u?n(f):(i=f,o.push(f));for(var v=0;v<Os.length;v++)Os[v](f,t)},end:function(){var e=o[o.length-1],t=e.children[e.children.length-1];t&&3===t.type&&" "===t.text&&!c&&e.children.pop(),o.length-=1,i=o[o.length-1],n(e)},chars:function(e){if(i&&(!qi||"textarea"!==i.tag||i.attrsMap.placeholder!==e)){var t=i.children;if(e=c||e.trim()?Hr(i)?e:Ws(e):a&&t.length?" ":""){var n;!s&&" "!==e&&(n=gr(e,xs))?t.push({type:2,expression:n,text:e}):" "===e&&t.length&&" "===t[t.length-1].text||t.push({type:3,text:e})}}},comment:function(e){i.children.push({type:3,text:e,isComment:!0})}}),r}function wr(e){null!=hn(e,"v-pre")&&(e.pre=!0)}function xr(e){var t=e.attrsList.length;if(t)for(var n=e.attrs=new Array(t),r=0;r<t;r++)n[r]={name:e.attrsList[r].name,value:JSON.stringify(e.attrsList[r].value)};else e.pre||(e.plain=!0)}function kr(e,t){Ar(e),e.plain=!e.key&&!e.attrsList.length,Or(e),Ir(e),Mr(e);for(var n=0;n<ks.length;n++)e=ks[n](e,t)||e;Dr(e)}function Ar(e){var t=vn(e,"key");t&&(e.key=t)}function Or(e){var t=vn(e,"ref");t&&(e.ref=t,e.refInFor=Pr(e))}function Sr(e){var t;if(t=hn(e,"v-for")){var n=t.match(Vs);if(!n)return;e.for=n[2].trim();var r=n[1].trim(),i=r.match(zs);i?(e.alias=i[1].trim(),e.iterator1=i[2].trim(),i[3]&&(e.iterator2=i[3].trim())):e.alias=r}}function Tr(e){var t=hn(e,"v-if");if(t)e.if=t,Nr(e,{exp:t,block:e});else{null!=hn(e,"v-else")&&(e.else=!0);var n=hn(e,"v-else-if");n&&(e.elseif=n)}}function Er(e,t){var n=jr(t.children);n&&n.if&&Nr(n,{exp:e.elseif,block:e})}function jr(e){for(var t=e.length;t--;){if(1===e[t].type)return e[t];e.pop()}}function Nr(e,t){e.ifConditions||(e.ifConditions=[]),e.ifConditions.push(t)}function Lr(e){null!=hn(e,"v-once")&&(e.once=!0)}function Ir(e){if("slot"===e.tag)e.slotName=vn(e,"name");else{var t;"template"===e.tag?(t=hn(e,"scope"),e.slotScope=t||hn(e,"slot-scope")):(t=hn(e,"slot-scope"))&&(e.slotScope=t);var n=vn(e,"slot");n&&(e.slotTarget='""'===n?'"default"':n,"template"===e.tag||e.slotScope||fn(e,"slot",n))}}function Mr(e){var t;(t=vn(e,"is"))&&(e.component=t),null!=hn(e,"inline-template")&&(e.inlineTemplate=!0)}function Dr(e){var t,n,r,i,o,a,s,c=e.attrsList;for(t=0,n=c.length;t<n;t++)if(r=i=c[t].name,o=c[t].value,Us.test(r))if(e.hasBindings=!0,(a=Fr(r))&&(r=r.replace(qs,"")),Js.test(r))r=r.replace(Js,""),o=an(o),s=!1,a&&(a.prop&&(s=!0,"innerHtml"===(r=Ni(r))&&(r="innerHTML")),a.camel&&(r=Ni(r)),a.sync&&pn(e,"update:"+Ni(r),yn(o,"$event"))),s||!e.component&&Ts(e.tag,e.attrsMap.type,r)?ln(e,r,o):fn(e,r,o);else if(Bs.test(r))pn(e,r=r.replace(Bs,""),o,a,!1,ws);else{var u=(r=r.replace(Us,"")).match(Ks),l=u&&u[1];l&&(r=r.slice(0,-(l.length+1))),dn(e,r,i,o,l,a)}else fn(e,r,JSON.stringify(o)),!e.component&&"muted"===r&&Ts(e.tag,e.attrsMap.type,r)&&ln(e,r,"true")}function Pr(e){for(var t=e;t;){if(void 0!==t.for)return!0;t=t.parent}return!1}function Fr(e){var t=e.match(qs);if(t){var n={};return t.forEach(function(e){n[e.slice(1)]=!0}),n}}function Rr(e){for(var t={},n=0,r=e.length;n<r;n++)t[e[n].name]=e[n].value;return t}function Hr(e){return"script"===e.tag||"style"===e.tag}function Br(e){return"style"===e.tag||"script"===e.tag&&(!e.attrsMap.type||"text/javascript"===e.attrsMap.type)}function Ur(e){for(var t=[],n=0;n<e.length;n++){var r=e[n];Gs.test(r.name)||(r.name=r.name.replace(Zs,""),t.push(r))}return t}function Vr(e){return $r(e.tag,e.attrsList.slice(),e.parent)}function zr(e,t,n){e.attrsMap[t]=n,e.attrsList.push({name:t,value:n})}function Kr(e,t){e&&(js=Xs(t.staticKeys||""),Ns=t.isReservedTag||Di,Jr(e),qr(e,!1))}function Jr(e){if(e.static=Wr(e),1===e.type){if(!Ns(e.tag)&&"slot"!==e.tag&&null==e.attrsMap["inline-template"])return;for(var t=0,n=e.children.length;t<n;t++){var r=e.children[t];Jr(r),r.static||(e.static=!1)}if(e.ifConditions)for(var i=1,o=e.ifConditions.length;i<o;i++){var a=e.ifConditions[i].block;Jr(a),a.static||(e.static=!1)}}}function qr(e,t){if(1===e.type){if((e.static||e.once)&&(e.staticInFor=t),e.static&&e.children.length&&(1!==e.children.length||3!==e.children[0].type))return void(e.staticRoot=!0);if(e.staticRoot=!1,e.children)for(var n=0,r=e.children.length;n<r;n++)qr(e.children[n],t||!!e.for);if(e.ifConditions)for(var i=1,o=e.ifConditions.length;i<o;i++)qr(e.ifConditions[i].block,t)}}function Wr(e){return 2!==e.type&&(3===e.type||!(!e.pre&&(e.hasBindings||e.if||e.for||Si(e.tag)||!Ns(e.tag)||Gr(e)||!Object.keys(e).every(js))))}function Gr(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}function Zr(e,t,n){var r=t?"nativeOn:{":"on:{";for(var i in e){var o=e[i];r+='"'+i+'":'+Yr(i,o)+","}return r.slice(0,-1)+"}"}function Yr(e,t){if(!t)return"function(){}";if(Array.isArray(t))return"["+t.map(function(t){return Yr(e,t)}).join(",")+"]";var n=tc.test(t.value),r=ec.test(t.value);if(t.modifiers){var i="",o="",a=[];for(var s in t.modifiers)if(ic[s])o+=ic[s],nc[s]&&a.push(s);else if("exact"===s){var c=t.modifiers;o+=rc(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+=Qr(a)),o&&(i+=o),"function($event){"+i+(n?t.value+"($event)":r?"("+t.value+")($event)":t.value)+"}"}return n||r?t.value:"function($event){"+t.value+"}"}function Qr(e){return"if(!('button' in $event)&&"+e.map(Xr).join("&&")+")return null;"}function Xr(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=nc[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key)"}function ei(e,t){var n=new ac(t);return{render:"with(this){return "+(e?ti(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function ti(e,t){if(e.staticRoot&&!e.staticProcessed)return ni(e,t);if(e.once&&!e.onceProcessed)return ri(e,t);if(e.for&&!e.forProcessed)return ai(e,t);if(e.if&&!e.ifProcessed)return ii(e,t);if("template"!==e.tag||e.slotTarget){if("slot"===e.tag)return _i(e,t);var n;if(e.component)n=bi(e.component,e,t);else{var r=e.plain?void 0:si(e,t),i=e.inlineTemplate?null:pi(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o<t.transforms.length;o++)n=t.transforms[o](e,n);return n}return pi(e,t)||"void 0"}function ni(e,t){return e.staticProcessed=!0,t.staticRenderFns.push("with(this){return "+ti(e,t)+"}"),"_m("+(t.staticRenderFns.length-1)+(e.staticInFor?",true":"")+")"}function ri(e,t){if(e.onceProcessed=!0,e.if&&!e.ifProcessed)return ii(e,t);if(e.staticInFor){for(var n="",r=e.parent;r;){if(r.for){n=r.key;break}r=r.parent}return n?"_o("+ti(e,t)+","+t.onceId+++","+n+")":ti(e,t)}return ni(e,t)}function ii(e,t,n,r){return e.ifProcessed=!0,oi(e.ifConditions.slice(),t,n,r)}function oi(e,t,n,r){function i(e){return n?n(e,t):e.once?ri(e,t):ti(e,t)}if(!e.length)return r||"_e()";var o=e.shift();return o.exp?"("+o.exp+")?"+i(o.block)+":"+oi(e,t,n,r):""+i(o.block)}function ai(e,t,n,r){var i=e.for,o=e.alias,a=e.iterator1?","+e.iterator1:"",s=e.iterator2?","+e.iterator2:"";return e.forProcessed=!0,(r||"_l")+"(("+i+"),function("+o+a+s+"){return "+(n||ti)(e,t)+"})"}function si(e,t){var n="{",r=ci(e,t);r&&(n+=r+","),e.key&&(n+="key:"+e.key+","),e.ref&&(n+="ref:"+e.ref+","),e.refInFor&&(n+="refInFor:true,"),e.pre&&(n+="pre:true,"),e.component&&(n+='tag:"'+e.tag+'",');for(var i=0;i<t.dataGenFns.length;i++)n+=t.dataGenFns[i](e);if(e.attrs&&(n+="attrs:{"+$i(e.attrs)+"},"),e.props&&(n+="domProps:{"+$i(e.props)+"},"),e.events&&(n+=Zr(e.events,!1,t.warn)+","),e.nativeEvents&&(n+=Zr(e.nativeEvents,!0,t.warn)+","),e.slotTarget&&!e.slotScope&&(n+="slot:"+e.slotTarget+","),e.scopedSlots&&(n+=li(e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=ui(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function ci(e,t){var n=e.directives;if(n){var r,i,o,a,s="directives:[",c=!1;for(r=0,i=n.length;r<i;r++){o=n[r],a=!0;var u=t.directives[o.name];u&&(a=!!u(e,o,t.warn)),a&&(c=!0,s+='{name:"'+o.name+'",rawName:"'+o.rawName+'"'+(o.value?",value:("+o.value+"),expression:"+JSON.stringify(o.value):"")+(o.arg?',arg:"'+o.arg+'"':"")+(o.modifiers?",modifiers:"+JSON.stringify(o.modifiers):"")+"},")}return c?s.slice(0,-1)+"]":void 0}}function ui(e,t){var n=e.children[0];if(1===n.type){var r=ei(n,t.options);return"inlineTemplate:{render:function(){"+r.render+"},staticRenderFns:["+r.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}function li(e,t){return"scopedSlots:_u(["+Object.keys(e).map(function(n){return fi(n,e[n],t)}).join(",")+"])"}function fi(e,t,n){return t.for&&!t.forProcessed?di(e,t,n):"{key:"+e+",fn:"+("function("+String(t.slotScope)+"){return "+("template"===t.tag?t.if?t.if+"?"+(pi(t,n)||"undefined")+":undefined":pi(t,n)||"undefined":ti(t,n))+"}")+"}"}function di(e,t,n){var r=t.for,i=t.alias,o=t.iterator1?","+t.iterator1:"",a=t.iterator2?","+t.iterator2:"";return t.forProcessed=!0,"_l(("+r+"),function("+i+o+a+"){return "+fi(e,t,n)+"})"}function pi(e,t,n,r,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag)return(r||ti)(a,t);var s=n?vi(o,t.maybeComponent):0,c=i||mi;return"["+o.map(function(e){return c(e,t)}).join(",")+"]"+(s?","+s:"")}}function vi(e,t){for(var n=0,r=0;r<e.length;r++){var i=e[r];if(1===i.type){if(hi(i)||i.ifConditions&&i.ifConditions.some(function(e){return hi(e.block)})){n=2;break}(t(i)||i.ifConditions&&i.ifConditions.some(function(e){return t(e.block)}))&&(n=1)}}return n}function hi(e){return void 0!==e.for||"template"===e.tag||"slot"===e.tag}function mi(e,t){return 1===e.type?ti(e,t):3===e.type&&e.isComment?gi(e):yi(e)}function yi(e){return"_v("+(2===e.type?e.expression:Ci(JSON.stringify(e.text)))+")"}function gi(e){return"_e("+JSON.stringify(e.text)+")"}function _i(e,t){var n=e.slotName||'"default"',r=pi(e,t),i="_t("+n+(r?","+r:""),o=e.attrs&&"{"+e.attrs.map(function(e){return Ni(e.name)+":"+e.value}).join(",")+"}",a=e.attrsMap["v-bind"];return!o&&!a||r||(i+=",null"),o&&(i+=","+o),a&&(i+=(o?"":",null")+","+a),i+")"}function bi(e,t,n){var r=t.inlineTemplate?null:pi(t,n,!0);return"_c("+e+","+si(t,n)+(r?","+r:"")+")"}function $i(e){for(var t="",n=0;n<e.length;n++){var r=e[n];t+='"'+r.name+'":'+Ci(r.value)+","}return t.slice(0,-1)}function Ci(e){return e.replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")}function wi(e,t){try{return new Function(e)}catch(n){return t.push({err:n,code:e}),_}}function xi(e){var t=Object.create(null);return function(n,r,i){delete(r=y({},r)).warn;var o=r.delimiters?String(r.delimiters)+n:n;if(t[o])return t[o];var a=e(n,r),s={},c=[];return s.render=wi(a.render,c),s.staticRenderFns=a.staticRenderFns.map(function(e){return wi(e,c)}),t[o]=s}}function ki(e){return Ls=Ls||document.createElement("div"),Ls.innerHTML=e?'<a href="\n"/>':'<div a="\n"/>',Ls.innerHTML.indexOf(" ")>0}function Ai(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}var Oi=Object.prototype.toString,Si=f("slot,component",!0),Ti=f("key,ref,slot,slot-scope,is"),Ei=Object.prototype.hasOwnProperty,ji=/-(\w)/g,Ni=v(function(e){return e.replace(ji,function(e,t){return t?t.toUpperCase():""})}),Li=v(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),Ii=/\B([A-Z])/g,Mi=v(function(e){return e.replace(Ii,"-$1").toLowerCase()}),Di=function(e,t,n){return!1},Pi=function(e){return e},Fi="data-server-rendered",Ri=["component","directive","filter"],Hi=["beforeCreate","created","beforeMount","mounted","beforeUpdate","updated","beforeDestroy","destroyed","activated","deactivated","errorCaptured"],Bi={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:Di,isReservedAttr:Di,isUnknownElement:Di,getTagNamespace:_,parsePlatformTagName:Pi,mustUseProp:Di,_lifecycleHooks:Hi},Ui=Object.freeze({}),Vi=/[^\w.$]/,zi="__proto__"in{},Ki="undefined"!=typeof window,Ji=Ki&&window.navigator.userAgent.toLowerCase(),qi=Ji&&/msie|trident/.test(Ji),Wi=Ji&&Ji.indexOf("msie 9.0")>0,Gi=Ji&&Ji.indexOf("edge/")>0,Zi=Ji&&Ji.indexOf("android")>0,Yi=Ji&&/iphone|ipad|ipod|ios/.test(Ji),Qi=(Ji&&/chrome\/\d+/.test(Ji),{}.watch),Xi=!1;if(Ki)try{var eo={};Object.defineProperty(eo,"passive",{get:function(){Xi=!0}}),window.addEventListener("test-passive",null,eo)}catch(e){}var to,no,ro=function(){return void 0===to&&(to=!Ki&&"undefined"!=typeof global&&"server"===global.process.env.VUE_ENV),to},io=Ki&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,oo="undefined"!=typeof Symbol&&A(Symbol)&&"undefined"!=typeof Reflect&&A(Reflect.ownKeys);no="undefined"!=typeof Set&&A(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var ao=_,so=0,co=function(){this.id=so++,this.subs=[]};co.prototype.addSub=function(e){this.subs.push(e)},co.prototype.removeSub=function(e){d(this.subs,e)},co.prototype.depend=function(){co.target&&co.target.addDep(this)},co.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t<n;t++)e[t].update()},co.target=null;var uo=[],lo=function(e,t,n,r,i,o,a,s){this.tag=e,this.data=t,this.children=n,this.text=r,this.elm=i,this.ns=void 0,this.context=o,this.functionalContext=void 0,this.functionalOptions=void 0,this.functionalScopeId=void 0,this.key=t&&t.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1},fo={child:{configurable:!0}};fo.child.get=function(){return this.componentInstance},Object.defineProperties(lo.prototype,fo);var po=function(e){void 0===e&&(e="");var t=new lo;return t.text=e,t.isComment=!0,t},vo=Array.prototype,ho=Object.create(vo);["push","pop","shift","unshift","splice","sort","reverse"].forEach(function(e){var t=vo[e];x(ho,e,function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];var i,o=t.apply(this,n),a=this.__ob__;switch(e){case"push":case"unshift":i=n;break;case"splice":i=n.slice(2)}return i&&a.observeArray(i),a.dep.notify(),o})});var mo=Object.getOwnPropertyNames(ho),yo={shouldConvert:!0},go=function(e){this.value=e,this.dep=new co,this.vmCount=0,x(e,"__ob__",this),Array.isArray(e)?((zi?N:L)(e,ho,mo),this.observeArray(e)):this.walk(e)};go.prototype.walk=function(e){for(var t=Object.keys(e),n=0;n<t.length;n++)M(e,t[n],e[t[n]])},go.prototype.observeArray=function(e){for(var t=0,n=e.length;t<n;t++)I(e[t])};var _o=Bi.optionMergeStrategies;_o.data=function(e,t,n){return n?H(e,t,n):t&&"function"!=typeof t?e:H(e,t)},Hi.forEach(function(e){_o[e]=B}),Ri.forEach(function(e){_o[e+"s"]=U}),_o.watch=function(e,t,n,r){if(e===Qi&&(e=void 0),t===Qi&&(t=void 0),!t)return Object.create(e||null);if(!e)return t;var i={};y(i,e);for(var o in t){var a=i[o],s=t[o];a&&!Array.isArray(a)&&(a=[a]),i[o]=a?a.concat(s):Array.isArray(s)?s:[s]}return i},_o.props=_o.methods=_o.inject=_o.computed=function(e,t,n,r){if(!e)return t;var i=Object.create(null);return y(i,e),t&&y(i,t),i},_o.provide=H;var bo,$o,Co=function(e,t){return void 0===t?e:t},wo=[],xo=!1,ko=!1;if("undefined"!=typeof setImmediate&&A(setImmediate))$o=function(){setImmediate(te)};else if("undefined"==typeof MessageChannel||!A(MessageChannel)&&"[object MessageChannelConstructor]"!==MessageChannel.toString())$o=function(){setTimeout(te,0)};else{var Ao=new MessageChannel,Oo=Ao.port2;Ao.port1.onmessage=te,$o=function(){Oo.postMessage(1)}}if("undefined"!=typeof Promise&&A(Promise)){var So=Promise.resolve();bo=function(){So.then(te),Yi&&setTimeout(_)}}else bo=$o;var To,Eo=v(function(e){var t="&"===e.charAt(0),n="~"===(e=t?e.slice(1):e).charAt(0),r="!"===(e=n?e.slice(1):e).charAt(0);return e=r?e.slice(1):e,{name:e,once:n,capture:r,passive:t}}),jo=null,No=[],Lo=[],Io={},Mo=!1,Do=!1,Po=0,Fo=0,Ro=function(e,t,n,r){this.vm=e,e._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++Fo,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new no,this.newDepIds=new no,this.expression="","function"==typeof t?this.getter=t:(this.getter=k(t),this.getter||(this.getter=function(){})),this.value=this.lazy?void 0:this.get()};Ro.prototype.get=function(){O(this);var e,t=this.vm;try{e=this.getter.call(t,t)}catch(e){if(!this.user)throw e;Q(e,t,'getter for watcher "'+this.expression+'"')}finally{this.deep&&Fe(e),S(),this.cleanupDeps()}return e},Ro.prototype.addDep=function(e){var t=e.id;this.newDepIds.has(t)||(this.newDepIds.add(t),this.newDeps.push(e),this.depIds.has(t)||e.addSub(this))},Ro.prototype.cleanupDeps=function(){for(var e=this,t=this.deps.length;t--;){var n=e.deps[t];e.newDepIds.has(n.id)||n.removeSub(e)}var r=this.depIds;this.depIds=this.newDepIds,this.newDepIds=r,this.newDepIds.clear(),r=this.deps,this.deps=this.newDeps,this.newDeps=r,this.newDeps.length=0},Ro.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():Pe(this)},Ro.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||o(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Q(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},Ro.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},Ro.prototype.depend=function(){for(var e=this,t=this.deps.length;t--;)e.deps[t].depend()},Ro.prototype.teardown=function(){var e=this;if(this.active){this.vm._isBeingDestroyed||d(this.vm._watchers,this);for(var t=this.deps.length;t--;)e.deps[t].removeSub(e);this.active=!1}};var Ho=new no,Bo={enumerable:!0,configurable:!0,get:_,set:_},Uo={lazy:!0};lt(ft.prototype);var Vo={init:function(e,t,n,r){if(!e.componentInstance||e.componentInstance._isDestroyed)(e.componentInstance=ht(e,jo,n,r)).$mount(t?e.elm:void 0,t);else if(e.data.keepAlive){var i=e;Vo.prepatch(i,i)}},prepatch:function(e,t){var n=t.componentOptions;Oe(t.componentInstance=e.componentInstance,n.propsData,n.listeners,t,n.children)},insert:function(e){var t=e.context,n=e.componentInstance;n._isMounted||(n._isMounted=!0,je(n,"mounted")),e.data.keepAlive&&(t._isMounted?Me(n):Te(n,!0))},destroy:function(e){var t=e.componentInstance;t._isDestroyed||(e.data.keepAlive?Ee(t,!0):t.$destroy())}},zo=Object.keys(Vo),Ko=1,Jo=2,qo=0;!function(e){e.prototype._init=function(e){var t=this;t._uid=qo++,t._isVue=!0,e&&e._isComponent?wt(t,e):t.$options=J(xt(t.constructor),e||{},t),t._renderProxy=t,t._self=t,ke(t),ge(t),Ct(t),je(t,"beforeCreate"),Qe(t),Be(t),Ye(t),je(t,"created"),t.$options.el&&t.$mount(t.$options.el)}}(Ot),function(e){var t={};t.get=function(){return this._data};var n={};n.get=function(){return this._props},Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",n),e.prototype.$set=D,e.prototype.$delete=P,e.prototype.$watch=function(e,t,n){var r=this;if(a(t))return Ze(r,e,t,n);(n=n||{}).user=!0;var i=new Ro(r,e,t,n);return n.immediate&&t.call(r,i.value),function(){i.teardown()}}}(Ot),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var r=this,i=this;if(Array.isArray(e))for(var o=0,a=e.length;o<a;o++)r.$on(e[o],n);else(i._events[e]||(i._events[e]=[])).push(n),t.test(e)&&(i._hasHookEvent=!0);return i},e.prototype.$once=function(e,t){function n(){r.$off(e,n),t.apply(r,arguments)}var r=this;return n.fn=t,r.$on(e,n),r},e.prototype.$off=function(e,t){var n=this,r=this;if(!arguments.length)return r._events=Object.create(null),r;if(Array.isArray(e)){for(var i=0,o=e.length;i<o;i++)n.$off(e[i],t);return r}var a=r._events[e];if(!a)return r;if(!t)return r._events[e]=null,r;if(t)for(var s,c=a.length;c--;)if((s=a[c])===t||s.fn===t){a.splice(c,1);break}return r},e.prototype.$emit=function(e){var t=this,n=t._events[e];if(n){n=n.length>1?m(n):n;for(var r=m(arguments,1),i=0,o=n.length;i<o;i++)try{n[i].apply(t,r)}catch(n){Q(n,t,'event handler for "'+e+'"')}}return t}}(Ot),function(e){e.prototype._update=function(e,t){var n=this;n._isMounted&&je(n,"beforeUpdate");var r=n.$el,i=n._vnode,o=jo;jo=n,n._vnode=e,i?n.$el=n.__patch__(i,e):(n.$el=n.__patch__(n.$el,e,t,!1,n.$options._parentElm,n.$options._refElm),n.$options._parentElm=n.$options._refElm=null),jo=o,r&&(r.__vue__=null),n.$el&&(n.$el.__vue__=n),n.$vnode&&n.$parent&&n.$vnode===n.$parent._vnode&&(n.$parent.$el=n.$el)},e.prototype.$forceUpdate=function(){var e=this;e._watcher&&e._watcher.update()},e.prototype.$destroy=function(){var e=this;if(!e._isBeingDestroyed){je(e,"beforeDestroy"),e._isBeingDestroyed=!0;var t=e.$parent;!t||t._isBeingDestroyed||e.$options.abstract||d(t.$children,e),e._watcher&&e._watcher.teardown();for(var n=e._watchers.length;n--;)e._watchers[n].teardown();e._data.__ob__&&e._data.__ob__.vmCount--,e._isDestroyed=!0,e.__patch__(e._vnode,null),je(e,"destroyed"),e.$off(),e.$el&&(e.$el.__vue__=null),e.$vnode&&(e.$vnode.parent=null)}}}(Ot),function(e){lt(e.prototype),e.prototype.$nextTick=function(e){return re(e,this)},e.prototype._render=function(){var e=this,t=e.$options,n=t.render,r=t._parentVnode;if(e._isMounted)for(var i in e.$slots){var o=e.$slots[i];o._rendered&&(e.$slots[i]=j(o,!0))}e.$scopedSlots=r&&r.data.scopedSlots||Ui,e.$vnode=r;var a;try{a=n.call(e._renderProxy,e.$createElement)}catch(t){Q(t,e,"render"),a=e._vnode}return a instanceof lo||(a=po()),a.parent=r,a}}(Ot);var Wo=[String,RegExp,Array],Go={KeepAlive:{name:"keep-alive",abstract:!0,props:{include:Wo,exclude:Wo,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){var e=this;for(var t in e.cache)Pt(e.cache,t,e.keys)},watch:{include:function(e){Dt(this,function(t){return Mt(e,t)})},exclude:function(e){Dt(this,function(t){return!Mt(e,t)})}},render:function(){var e=ye(this.$slots.default),t=e&&e.componentOptions;if(t){var n=It(t);if(n&&(this.exclude&&Mt(this.exclude,n)||this.include&&!Mt(this.include,n)))return e;var r=this,i=r.cache,o=r.keys,a=null==e.key?t.Ctor.cid+(t.tag?"::"+t.tag:""):e.key;i[a]?(e.componentInstance=i[a].componentInstance,d(o,a),o.push(a)):(i[a]=e,o.push(a),this.max&&o.length>parseInt(this.max)&&Pt(i,o[0],o,this._vnode)),e.data.keepAlive=!0}return e}}};!function(e){var t={};t.get=function(){return Bi},Object.defineProperty(e,"config",t),e.util={warn:ao,extend:y,mergeOptions:J,defineReactive:M},e.set=D,e.delete=P,e.nextTick=re,e.options=Object.create(null),Ri.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,y(e.options.components,Go),St(e),Tt(e),Et(e),Lt(e)}(Ot),Object.defineProperty(Ot.prototype,"$isServer",{get:ro}),Object.defineProperty(Ot.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Ot.version="2.5.3";var Zo,Yo,Qo,Xo,ea,ta,na,ra,ia=f("style,class"),oa=f("input,textarea,option,select,progress"),aa=function(e,t,n){return"value"===n&&oa(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},sa=f("contenteditable,draggable,spellcheck"),ca=f("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,translate,truespeed,typemustmatch,visible"),ua="http://www.w3.org/1999/xlink",la=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},fa=function(e){return la(e)?e.slice(6,e.length):""},da=function(e){return null==e||!1===e},pa={svg:"http://www.w3.org/2000/svg",math:"http://www.w3.org/1998/Math/MathML"},va=f("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"),ha=f("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",!0),ma=function(e){return va(e)||ha(e)},ya=Object.create(null),ga=f("text,number,password,search,email,tel,url"),_a=Object.freeze({createElement:function(e,t){var n=document.createElement(e);return"select"!==e?n:(t.data&&t.data.attrs&&void 0!==t.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)},createElementNS:function(e,t){return document.createElementNS(pa[e],t)},createTextNode:function(e){return document.createTextNode(e)},createComment:function(e){return document.createComment(e)},insertBefore:function(e,t,n){e.insertBefore(t,n)},removeChild:function(e,t){e.removeChild(t)},appendChild:function(e,t){e.appendChild(t)},parentNode:function(e){return e.parentNode},nextSibling:function(e){return e.nextSibling},tagName:function(e){return e.tagName},setTextContent:function(e,t){e.textContent=t},setAttribute:function(e,t,n){e.setAttribute(t,n)}}),ba={create:function(e,t){qt(t)},update:function(e,t){e.data.ref!==t.data.ref&&(qt(e,!0),qt(t))},destroy:function(e){qt(e,!0)}},$a=new lo("",{},[]),Ca=["create","activate","update","remove","destroy"],wa={create:Yt,update:Yt,destroy:function(e){Yt(e,$a)}},xa=Object.create(null),ka=[ba,wa],Aa={create:nn,update:nn},Oa={create:on,update:on},Sa=/[\w).+\-_$\]]/,Ta="__r",Ea="__c",ja={create:Nn,update:Nn},Na={create:Ln,update:Ln},La=v(function(e){var t={},n=/;(?![^(]*\))/g,r=/:(.+)/;return e.split(n).forEach(function(e){if(e){var n=e.split(r);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}),Ia=/^--/,Ma=/\s*!important$/,Da=function(e,t,n){if(Ia.test(t))e.style.setProperty(t,n);else if(Ma.test(n))e.style.setProperty(t,n.replace(Ma,""),"important");else{var r=Fa(t);if(Array.isArray(n))for(var i=0,o=n.length;i<o;i++)e.style[r]=n[i];else e.style[r]=n}},Pa=["Webkit","Moz","ms"],Fa=v(function(e){if(ra=ra||document.createElement("div").style,"filter"!==(e=Ni(e))&&e in ra)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<Pa.length;n++){var r=Pa[n]+t;if(r in ra)return r}}),Ra={create:Hn,update:Hn},Ha=v(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),Ba=Ki&&!Wi,Ua="transition",Va="animation",za="transition",Ka="transitionend",Ja="animation",qa="animationend";Ba&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(za="WebkitTransition",Ka="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Ja="WebkitAnimation",qa="webkitAnimationEnd"));var Wa=Ki?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()},Ga=/\b(transform|all)(,|$)/,Za=function(r){function o(e){return new lo(j.tagName(e).toLowerCase(),{},[],void 0,e)}function a(e,t){function n(){0==--n.listeners&&s(e)}return n.listeners=t,n}function s(e){var n=j.parentNode(e);t(n)&&j.removeChild(n,e)}function c(e,r,i,o,a){if(e.isRootInsert=!a,!u(e,r,i,o)){var s=e.data,c=e.children,l=e.tag;t(l)?(e.elm=e.ns?j.createElementNS(e.ns,l):j.createElement(l,e),y(e),v(e,c,r),t(s)&&m(e,r),p(i,e.elm,o)):n(e.isComment)?(e.elm=j.createComment(e.text),p(i,e.elm,o)):(e.elm=j.createTextNode(e.text),p(i,e.elm,o))}}function u(e,r,i,o){var a=e.data;if(t(a)){var s=t(e.componentInstance)&&a.keepAlive;if(t(a=a.hook)&&t(a=a.init)&&a(e,!1,i,o),t(e.componentInstance))return l(e,r),n(s)&&d(e,r,i,o),!0}}function l(e,n){t(e.data.pendingInsert)&&(n.push.apply(n,e.data.pendingInsert),e.data.pendingInsert=null),e.elm=e.componentInstance.$el,h(e)?(m(e,n),y(e)):(qt(e),n.push(e))}function d(e,n,r,i){for(var o,a=e;a.componentInstance;)if(a=a.componentInstance._vnode,t(o=a.data)&&t(o=o.transition)){for(o=0;o<T.activate.length;++o)T.activate[o]($a,a);n.push(a);break}p(r,e.elm,i)}function p(e,n,r){t(e)&&(t(r)?r.parentNode===e&&j.insertBefore(e,n,r):j.appendChild(e,n))}function v(e,t,n){if(Array.isArray(t))for(var r=0;r<t.length;++r)c(t[r],n,e.elm,null,!0);else i(e.text)&&j.appendChild(e.elm,j.createTextNode(e.text))}function h(e){for(;e.componentInstance;)e=e.componentInstance._vnode;return t(e.tag)}function m(e,n){for(var r=0;r<T.create.length;++r)T.create[r]($a,e);t(O=e.data.hook)&&(t(O.create)&&O.create($a,e),t(O.insert)&&n.push(e))}function y(e){var n;if(t(n=e.functionalScopeId))j.setAttribute(e.elm,n,"");else for(var r=e;r;)t(n=r.context)&&t(n=n.$options._scopeId)&&j.setAttribute(e.elm,n,""),r=r.parent;t(n=jo)&&n!==e.context&&n!==e.functionalContext&&t(n=n.$options._scopeId)&&j.setAttribute(e.elm,n,"")}function g(e,t,n,r,i,o){for(;r<=i;++r)c(n[r],o,e,t)}function _(e){var n,r,i=e.data;if(t(i))for(t(n=i.hook)&&t(n=n.destroy)&&n(e),n=0;n<T.destroy.length;++n)T.destroy[n](e);if(t(n=e.children))for(r=0;r<e.children.length;++r)_(e.children[r])}function b(e,n,r,i){for(;r<=i;++r){var o=n[r];t(o)&&(t(o.tag)?($(o),_(o)):s(o.elm))}}function $(e,n){if(t(n)||t(e.data)){var r,i=T.remove.length+1;for(t(n)?n.listeners+=i:n=a(e.elm,i),t(r=e.componentInstance)&&t(r=r._vnode)&&t(r.data)&&$(r,n),r=0;r<T.remove.length;++r)T.remove[r](e,n);t(r=e.data.hook)&&t(r=r.remove)?r(e,n):n()}else s(e.elm)}function C(n,r,i,o,a){for(var s,u,l,f=0,d=0,p=r.length-1,v=r[0],h=r[p],m=i.length-1,y=i[0],_=i[m],$=!a;f<=p&&d<=m;)e(v)?v=r[++f]:e(h)?h=r[--p]:Wt(v,y)?(x(v,y,o),v=r[++f],y=i[++d]):Wt(h,_)?(x(h,_,o),h=r[--p],_=i[--m]):Wt(v,_)?(x(v,_,o),$&&j.insertBefore(n,v.elm,j.nextSibling(h.elm)),v=r[++f],_=i[--m]):Wt(h,y)?(x(h,y,o),$&&j.insertBefore(n,h.elm,v.elm),h=r[--p],y=i[++d]):(e(s)&&(s=Zt(r,f,p)),e(u=t(y.key)?s[y.key]:w(y,r,f,p))?c(y,o,n,v.elm):Wt(l=r[u],y)?(x(l,y,o),r[u]=void 0,$&&j.insertBefore(n,l.elm,v.elm)):c(y,o,n,v.elm),y=i[++d]);f>p?g(n,e(i[m+1])?null:i[m+1].elm,i,d,m,o):d>m&&b(n,r,f,p)}function w(e,n,r,i){for(var o=r;o<i;o++){var a=n[o];if(t(a)&&Wt(e,a))return o}}function x(r,i,o,a){if(r!==i){var s=i.elm=r.elm;if(n(r.isAsyncPlaceholder))t(i.asyncFactory.resolved)?A(r.elm,i,o):i.isAsyncPlaceholder=!0;else if(n(i.isStatic)&&n(r.isStatic)&&i.key===r.key&&(n(i.isCloned)||n(i.isOnce)))i.componentInstance=r.componentInstance;else{var c,u=i.data;t(u)&&t(c=u.hook)&&t(c=c.prepatch)&&c(r,i);var l=r.children,f=i.children;if(t(u)&&h(i)){for(c=0;c<T.update.length;++c)T.update[c](r,i);t(c=u.hook)&&t(c=c.update)&&c(r,i)}e(i.text)?t(l)&&t(f)?l!==f&&C(s,l,f,o,a):t(f)?(t(r.text)&&j.setTextContent(s,""),g(s,null,f,0,f.length-1,o)):t(l)?b(s,l,0,l.length-1):t(r.text)&&j.setTextContent(s,""):r.text!==i.text&&j.setTextContent(s,i.text),t(u)&&t(c=u.hook)&&t(c=c.postpatch)&&c(r,i)}}}function k(e,r,i){if(n(i)&&t(e.parent))e.parent.data.pendingInsert=r;else for(var o=0;o<r.length;++o)r[o].data.hook.insert(r[o])}function A(e,r,i){if(n(r.isComment)&&t(r.asyncFactory))return r.elm=e,r.isAsyncPlaceholder=!0,!0;r.elm=e;var o=r.tag,a=r.data,s=r.children;if(t(a)&&(t(O=a.hook)&&t(O=O.init)&&O(r,!0),t(O=r.componentInstance)))return l(r,i),!0;if(t(o)){if(t(s))if(e.hasChildNodes())if(t(O=a)&&t(O=O.domProps)&&t(O=O.innerHTML)){if(O!==e.innerHTML)return!1}else{for(var c=!0,u=e.firstChild,f=0;f<s.length;f++){if(!u||!A(u,s[f],i)){c=!1;break}u=u.nextSibling}if(!c||u)return!1}else v(r,s,i);if(t(a))for(var d in a)if(!N(d)){m(r,i);break}}else e.data!==r.text&&(e.data=r.text);return!0}var O,S,T={},E=r.modules,j=r.nodeOps;for(O=0;O<Ca.length;++O)for(T[Ca[O]]=[],S=0;S<E.length;++S)t(E[S][Ca[O]])&&T[Ca[O]].push(E[S][Ca[O]]);var N=f("attrs,style,class,staticClass,staticStyle,key");return function(r,i,a,s,u,l){if(!e(i)){var f=!1,d=[];if(e(r))f=!0,c(i,d,u,l);else{var p=t(r.nodeType);if(!p&&Wt(r,i))x(r,i,d,s);else{if(p){if(1===r.nodeType&&r.hasAttribute(Fi)&&(r.removeAttribute(Fi),a=!0),n(a)&&A(r,i,d))return k(i,d,!0),r;r=o(r)}var v=r.elm,m=j.parentNode(v);if(c(i,d,v._leaveCb?null:m,j.nextSibling(v)),t(i.parent))for(var y=i.parent,g=h(i);y;){for(var $=0;$<T.destroy.length;++$)T.destroy[$](y);if(y.elm=i.elm,g){for(var C=0;C<T.create.length;++C)T.create[C]($a,y);var w=y.data.hook.insert;if(w.merged)for(var O=1;O<w.fns.length;O++)w.fns[O]()}else qt(y);y=y.parent}t(m)?b(m,[r],0,0):t(r.tag)&&_(r)}}return k(i,d,f),i.elm}t(r)&&_(r)}}({nodeOps:_a,modules:[Aa,Oa,ja,Na,Ra,Ki?{create:tr,activate:tr,remove:function(e,t){!0!==e.data.show?Qn(e,t):t()}}:{}].concat(ka)});Wi&&document.addEventListener("selectionchange",function(){var e=document.activeElement;e&&e.vmodel&&cr(e,"input")});var Ya={inserted:function(e,t,n,r){"select"===n.tag?(r.elm&&!r.elm._vOptions?ae(n,"postpatch",function(){Ya.componentUpdated(e,t,n)}):nr(e,t,n.context),e._vOptions=[].map.call(e.options,or)):("textarea"===n.tag||ga(e.type))&&(e._vModifiers=t.modifiers,t.modifiers.lazy||(e.addEventListener("change",sr),Zi||(e.addEventListener("compositionstart",ar),e.addEventListener("compositionend",sr)),Wi&&(e.vmodel=!0)))},componentUpdated:function(e,t,n){if("select"===n.tag){nr(e,t,n.context);var r=e._vOptions,i=e._vOptions=[].map.call(e.options,or);i.some(function(e,t){return!b(e,r[t])})&&(e.multiple?t.value.some(function(e){return ir(e,i)}):t.value!==t.oldValue&&ir(t.value,i))&&cr(e,"change")}}},Qa={model:Ya,show:{bind:function(e,t,n){var r=t.value,i=(n=ur(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;r&&i?(n.data.show=!0,Yn(n,function(){e.style.display=o})):e.style.display=r?o:"none"},update:function(e,t,n){var r=t.value;r!==t.oldValue&&((n=ur(n)).data&&n.data.transition?(n.data.show=!0,r?Yn(n,function(){e.style.display=e.__vOriginalDisplay}):Qn(n,function(){e.style.display="none"})):e.style.display=r?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}}},Xa={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]},es={name:"transition",props:Xa,abstract:!0,render:function(e){var t=this,n=this.$options._renderChildren;if(n&&(n=n.filter(function(e){return e.tag||me(e)})).length){var r=this.mode,o=n[0];if(pr(this.$vnode))return o;var a=lr(o);if(!a)return o;if(this._leaving)return dr(e,o);var s="__transition-"+this._uid+"-";a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=fr(this),u=this._vnode,l=lr(u);if(a.data.directives&&a.data.directives.some(function(e){return"show"===e.name})&&(a.data.show=!0),l&&l.data&&!vr(a,l)&&!me(l)){var f=l.data.transition=y({},c);if("out-in"===r)return this._leaving=!0,ae(f,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),dr(e,o);if("in-out"===r){if(me(a))return u;var d,p=function(){d()};ae(c,"afterEnter",p),ae(c,"enterCancelled",p),ae(f,"delayLeave",function(e){d=e})}}return o}}},ts=y({tag:String,moveClass:String},Xa);delete ts.mode;var ns={Transition:es,TransitionGroup:{props:ts,render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=fr(this),s=0;s<i.length;s++){var c=i[s];c.tag&&null!=c.key&&0!==String(c.key).indexOf("__vlist")&&(o.push(c),n[c.key]=c,(c.data||(c.data={})).transition=a)}if(r){for(var u=[],l=[],f=0;f<r.length;f++){var d=r[f];d.data.transition=a,d.data.pos=d.elm.getBoundingClientRect(),n[d.key]?u.push(d):l.push(d)}this.kept=e(t,null,u),this.removed=l}return e(t,null,o)},beforeUpdate:function(){this.__patch__(this._vnode,this.kept,!1,!0),this._vnode=this.kept},updated:function(){var e=this.prevChildren,t=this.moveClass||(this.name||"v")+"-move";e.length&&this.hasMove(e[0].elm,t)&&(e.forEach(hr),e.forEach(mr),e.forEach(yr),this._reflow=document.body.offsetHeight,e.forEach(function(e){if(e.data.moved){var n=e.elm,r=n.style;Kn(n,t),r.transform=r.WebkitTransform=r.transitionDuration="",n.addEventListener(Ka,n._moveCb=function e(r){r&&!/transform$/.test(r.propertyName)||(n.removeEventListener(Ka,e),n._moveCb=null,Jn(n,t))})}}))},methods:{hasMove:function(e,t){if(!Ba)return!1;if(this._hasMove)return this._hasMove;var n=e.cloneNode();e._transitionClasses&&e._transitionClasses.forEach(function(e){Un(n,e)}),Bn(n,t),n.style.display="none",this.$el.appendChild(n);var r=Wn(n);return this.$el.removeChild(n),this._hasMove=r.hasTransform}}}};Ot.config.mustUseProp=aa,Ot.config.isReservedTag=ma,Ot.config.isReservedAttr=ia,Ot.config.getTagNamespace=Kt,Ot.config.isUnknownElement=function(e){if(!Ki)return!0;if(ma(e))return!1;if(e=e.toLowerCase(),null!=ya[e])return ya[e];var t=document.createElement(e);return e.indexOf("-")>-1?ya[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:ya[e]=/HTMLUnknownElement/.test(t.toString())},y(Ot.options.directives,Qa),y(Ot.options.components,ns),Ot.prototype.__patch__=Ki?Za:_,Ot.prototype.$mount=function(e,t){return e=e&&Ki?Jt(e):void 0,Ae(this,e,t)},Ot.nextTick(function(){Bi.devtools&&io&&io.emit("init",Ot)},0);var rs,is=/\{\{((?:.|\n)+?)\}\}/g,os=/[-.*+?^${}()|[\]\/\\]/g,as=v(function(e){var t=e[0].replace(os,"\\$&"),n=e[1].replace(os,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")}),ss={staticKeys:["staticClass"],transformNode:function(e,t){t.warn;var n=hn(e,"class");n&&(e.staticClass=JSON.stringify(n));var r=vn(e,"class",!1);r&&(e.classBinding=r)},genData:function(e){var t="";return e.staticClass&&(t+="staticClass:"+e.staticClass+","),e.classBinding&&(t+="class:"+e.classBinding+","),t}},cs={staticKeys:["staticStyle"],transformNode:function(e,t){var n=hn(e,"style");n&&(e.staticStyle=JSON.stringify(La(n)));var r=vn(e,"style",!1);r&&(e.styleBinding=r)},genData:function(e){var t="";return e.staticStyle&&(t+="staticStyle:"+e.staticStyle+","),e.styleBinding&&(t+="style:("+e.styleBinding+"),"),t}},us={decode:function(e){return rs=rs||document.createElement("div"),rs.innerHTML=e,rs.textContent}},ls=f("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),fs=f("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),ds=f("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"),ps=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,vs="[a-zA-Z_][\\w\\-\\.]*",hs="((?:"+vs+"\\:)?"+vs+")",ms=new RegExp("^<"+hs),ys=/^\s*(\/?)>/,gs=new RegExp("^<\\/"+hs+"[^>]*>"),_s=/^<!DOCTYPE [^>]+>/i,bs=/^<!--/,$s=/^<!\[/,Cs=!1;"x".replace(/x(.)?/g,function(e,t){Cs=""===t});var ws,xs,ks,As,Os,Ss,Ts,Es,js,Ns,Ls,Is=f("script,style,textarea",!0),Ms={},Ds={"<":"<",">":">",""":'"',"&":"&"," ":"\n","	":"\t"},Ps=/&(?:lt|gt|quot|amp);/g,Fs=/&(?:lt|gt|quot|amp|#10|#9);/g,Rs=f("pre,textarea",!0),Hs=function(e,t){return e&&Rs(e)&&"\n"===t[0]},Bs=/^@|^v-on:/,Us=/^v-|^@|^:/,Vs=/(.*?)\s+(?:in|of)\s+(.*)/,zs=/\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/,Ks=/:(.*)$/,Js=/^:|^v-bind:/,qs=/\.[^.]+/g,Ws=v(us.decode),Gs=/^xmlns:NS\d+/,Zs=/^NS\d+:/,Ys=[ss,cs,{preTransformNode:function(e,t){if("input"===e.tag){var n=e.attrsMap;if(n["v-model"]&&(n["v-bind:type"]||n[":type"])){var r=vn(e,"type"),i=hn(e,"v-if",!0),o=i?"&&("+i+")":"",a=null!=hn(e,"v-else",!0),s=hn(e,"v-else-if",!0),c=Vr(e);Sr(c),zr(c,"type","checkbox"),kr(c,t),c.processed=!0,c.if="("+r+")==='checkbox'"+o,Nr(c,{exp:c.if,block:c});var u=Vr(e);hn(u,"v-for",!0),zr(u,"type","radio"),kr(u,t),Nr(c,{exp:"("+r+")==='radio'"+o,block:u});var l=Vr(e);return hn(l,"v-for",!0),zr(l,":type",r),kr(l,t),Nr(c,{exp:i,block:l}),a?c.else=!0:s&&(c.elseif=s),c}}}}],Qs={expectHTML:!0,modules:Ys,directives:{model:function(e,t,n){var r=t.value,i=t.modifiers,o=e.tag,a=e.attrsMap.type;if(e.component)return mn(e,r,i),!1;if("select"===o)An(e,r,i);else if("input"===o&&"checkbox"===a)xn(e,r,i);else if("input"===o&&"radio"===a)kn(e,r,i);else if("input"===o||"textarea"===o)On(e,r,i);else if(!Bi.isReservedTag(o))return mn(e,r,i),!1;return!0},text:function(e,t){t.value&&ln(e,"textContent","_s("+t.value+")")},html:function(e,t){t.value&&ln(e,"innerHTML","_s("+t.value+")")}},isPreTag:function(e){return"pre"===e},isUnaryTag:ls,mustUseProp:aa,canBeLeftOpenTag:fs,isReservedTag:ma,getTagNamespace:Kt,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}(Ys)},Xs=v(function(e){return f("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(e?","+e:""))}),ec=/^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/,tc=/^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/,nc={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},rc=function(e){return"if("+e+")return null;"},ic={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:rc("$event.target !== $event.currentTarget"),ctrl:rc("!$event.ctrlKey"),shift:rc("!$event.shiftKey"),alt:rc("!$event.altKey"),meta:rc("!$event.metaKey"),left:rc("'button' in $event && $event.button !== 0"),middle:rc("'button' in $event && $event.button !== 1"),right:rc("'button' in $event && $event.button !== 2")},oc={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:_},ac=function(e){this.options=e,this.warn=e.warn||cn,this.transforms=un(e.modules,"transformCode"),this.dataGenFns=un(e.modules,"genData"),this.directives=y(y({},oc),e.directives);var t=e.isReservedTag||Di;this.maybeComponent=function(e){return!t(e.tag)},this.onceId=0,this.staticRenderFns=[]},sc=(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"),new RegExp("\\b"+"delete,typeof,void".split(",").join("\\s*\\([^\\)]*\\)|\\b")+"\\s*\\([^\\)]*\\)"),function(e){return function(t){function n(n,r){var i=Object.create(t),o=[],a=[];if(i.warn=function(e,t){(t?a:o).push(e)},r){r.modules&&(i.modules=(t.modules||[]).concat(r.modules)),r.directives&&(i.directives=y(Object.create(t.directives),r.directives));for(var s in r)"modules"!==s&&"directives"!==s&&(i[s]=r[s])}var c=e(n,i);return c.errors=o,c.tips=a,c}return{compile:n,compileToFunctions:xi(n)}}}(function(e,t){var n=Cr(e.trim(),t);Kr(n,t);var r=ei(n,t);return{ast:n,render:r.render,staticRenderFns:r.staticRenderFns}})(Qs).compileToFunctions),cc=!!Ki&&ki(!1),uc=!!Ki&&ki(!0),lc=v(function(e){var t=Jt(e);return t&&t.innerHTML}),fc=Ot.prototype.$mount;return Ot.prototype.$mount=function(e,t){if((e=e&&Jt(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=lc(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=Ai(e));if(r){var i=sc(r,{shouldDecodeNewlines:cc,shouldDecodeNewlinesForHref:uc,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return fc.call(this,e,t)},Ot.compile=sc,Ot}); \ No newline at end of file diff --git a/dist/vue.runtime.common.js b/dist/vue.runtime.common.js index 23689454263..07ddaa2ccba 100644 --- a/dist/vue.runtime.common.js +++ b/dist/vue.runtime.common.js @@ -1,7794 +1,5 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 Evan You - * Released under the MIT License. - */ -'use strict'; - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real 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. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ - - -/** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid$1 = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid$1++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - 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) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else if (process.env.NODE_ENV !== 'production') { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else if (process.env.NODE_ENV !== 'production') { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if (process.env.NODE_ENV !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (process.env.NODE_ENV !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; +if (process.env.NODE_ENV === 'production') { + module.exports = require('./vue.runtime.common.prod.js') } else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -if (process.env.NODE_ENV !== 'production') { - var 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,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -var mark; -var measure; - -if (process.env.NODE_ENV !== 'production') { - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var 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; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - if (process.env.NODE_ENV !== 'production') { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - process.env.NODE_ENV !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - process.env.NODE_ENV !== 'production' - ? ("timeout (" + (res.timeout) + "ms)") - : null - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - -function getFirstComponentChild (children) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var c = children[i]; - if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { - return c - } - } - } -} - -/* */ - -/* */ - -function initEvents (vm) { - vm._events = Object.create(null); - vm._hasHookEvent = false; - // init parent attached events - var listeners = vm.$options._parentListeners; - if (listeners) { - updateComponentListeners(vm, listeners); - } -} - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -function eventsMixin (Vue) { - var hookRE = /^hook:/; - Vue.prototype.$on = function (event, fn) { - var this$1 = this; - - var vm = this; - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$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, fn) { - var vm = 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, fn) { - var this$1 = this; - - var vm = this; - // all - if (!arguments.length) { - vm._events = Object.create(null); - return vm - } - // array of events - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$off(event[i], fn); - } - return vm - } - // specific event - var cbs = vm._events[event]; - if (!cbs) { - return vm - } - if (!fn) { - vm._events[event] = null; - return vm - } - if (fn) { - // specific handler - var cb; - var i$1 = cbs.length; - while (i$1--) { - cb = cbs[i$1]; - if (cb === fn || cb.fn === fn) { - cbs.splice(i$1, 1); - break - } - } - } - return vm - }; - - Vue.prototype.$emit = function (event) { - var vm = this; - if (process.env.NODE_ENV !== 'production') { - var 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 + "\"." - ); - } - } - var cbs = vm._events[event]; - if (cbs) { - cbs = cbs.length > 1 ? toArray(cbs) : cbs; - var args = toArray(arguments, 1); - for (var i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args); - } catch (e) { - handleError(e, vm, ("event handler for \"" + event + "\"")); - } - } - } - return vm - }; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; -var isUpdatingChildComponent = false; - -function initLifecycle (vm) { - var options = vm.$options; - - // locate first non-abstract parent - var 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._watcher = null; - vm._inactive = null; - vm._directInactive = false; - vm._isMounted = false; - vm._isDestroyed = false; - vm._isBeingDestroyed = false; -} - -function lifecycleMixin (Vue) { - Vue.prototype._update = function (vnode, hydrating) { - var vm = this; - if (vm._isMounted) { - callHook(vm, 'beforeUpdate'); - } - var prevEl = vm.$el; - var prevVnode = vm._vnode; - var prevActiveInstance = activeInstance; - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ); - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null; - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode); - } - activeInstance = prevActiveInstance; - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el; - } - // updated hook is called by the scheduler to ensure that children are - // updated in a parent's updated hook. - }; - - Vue.prototype.$forceUpdate = function () { - var vm = this; - if (vm._watcher) { - vm._watcher.update(); - } - }; - - Vue.prototype.$destroy = function () { - var vm = this; - if (vm._isBeingDestroyed) { - return - } - callHook(vm, 'beforeDestroy'); - vm._isBeingDestroyed = true; - // remove self from parent - var parent = vm.$parent; - if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { - remove(parent.$children, vm); - } - // teardown watchers - if (vm._watcher) { - vm._watcher.teardown(); - } - var i = vm._watchers.length; - while (i--) { - vm._watchers[i].teardown(); - } - // 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; - } - }; -} - -function mountComponent ( - vm, - el, - hydrating -) { - vm.$el = el; - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode; - if (process.env.NODE_ENV !== 'production') { - /* 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'); - - var updateComponent; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - updateComponent = function () { - var name = vm._name; - var id = vm._uid; - var startTag = "vue-perf-start:" + id; - var endTag = "vue-perf-end:" + id; - - mark(startTag); - var 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 = function () { - vm._update(vm._render(), hydrating); - }; - } - - vm._watcher = new Watcher(vm, updateComponent, noop); - hydrating = false; - - // 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 -} - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = true; - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = false; - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index = queue.length = activatedChildren.length = 0; - has = {}; - if (process.env.NODE_ENV !== 'production') { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // 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]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var 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; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = process.env.NODE_ENV !== 'production' - ? expOrFn.toString() - : ''; - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - process.env.NODE_ENV !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var sharedPropertyDefinition = { - enumerable: true, - configurable: true, - get: noop, - set: noop -}; - -function proxy (target, sourceKey, key) { - sharedPropertyDefinition.get = function proxyGetter () { - return this[sourceKey][key] - }; - sharedPropertyDefinition.set = function proxySetter (val) { - this[sourceKey][key] = val; - }; - Object.defineProperty(target, key, sharedPropertyDefinition); -} - -function initState (vm) { - vm._watchers = []; - var opts = vm.$options; - if (opts.props) { initProps(vm, opts.props); } - if (opts.methods) { initMethods(vm, opts.methods); } - if (opts.data) { - initData(vm); - } else { - observe(vm._data = {}, true /* asRootData */); - } - if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch && opts.watch !== nativeWatch) { - initWatch(vm, opts.watch); - } -} - -function initProps (vm, propsOptions) { - var propsData = vm.$options.propsData || {}; - var props = vm._props = {}; - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - var keys = vm.$options._propKeys = []; - var isRoot = !vm.$parent; - // root instance props should be converted - observerState.shouldConvert = isRoot; - var loop = function ( key ) { - keys.push(key); - var value = validateProp(key, propsOptions, propsData, vm); - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - var 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, function () { - if (vm.$parent && !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 - ); - } - }); - } else { - defineReactive(props, key, value); - } - // 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); - } - }; - - for (var key in propsOptions) loop( key ); - observerState.shouldConvert = true; -} - -function initData (vm) { - var data = vm.$options.data; - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {}; - if (!isPlainObject(data)) { - data = {}; - process.env.NODE_ENV !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', - vm - ); - } - // proxy data on instance - var keys = Object.keys(data); - var props = vm.$options.props; - var methods = vm.$options.methods; - var i = keys.length; - while (i--) { - var key = keys[i]; - if (process.env.NODE_ENV !== 'production') { - if (methods && hasOwn(methods, key)) { - warn( - ("Method \"" + key + "\" has already been defined as a data property."), - vm - ); - } - } - if (props && hasOwn(props, key)) { - process.env.NODE_ENV !== 'production' && 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 - observe(data, true /* asRootData */); -} - -function getData (data, vm) { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, "data()"); - return {} - } -} - -var computedWatcherOptions = { lazy: true }; - -function initComputed (vm, computed) { - var watchers = vm._computedWatchers = Object.create(null); - // computed properties are just getters during SSR - var isSSR = isServerRendering(); - - for (var key in computed) { - var userDef = computed[key]; - var getter = typeof userDef === 'function' ? userDef : userDef.get; - if (process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production') { - 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); - } - } - } -} - -function defineComputed ( - target, - key, - userDef -) { - var shouldCache = !isServerRendering(); - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef; - sharedPropertyDefinition.set = noop; - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop; - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop; - } - if (process.env.NODE_ENV !== 'production' && - 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 () { - var watcher = this._computedWatchers && this._computedWatchers[key]; - if (watcher) { - if (watcher.dirty) { - watcher.evaluate(); - } - if (Dep.target) { - watcher.depend(); - } - return watcher.value - } - } -} - -function initMethods (vm, methods) { - var props = vm.$options.props; - for (var key in methods) { - if (process.env.NODE_ENV !== 'production') { - if (methods[key] == null) { - warn( - "Method \"" + key + "\" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm); - } -} - -function initWatch (vm, watch) { - for (var key in watch) { - var handler = watch[key]; - if (Array.isArray(handler)) { - for (var i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]); - } - } else { - createWatcher(vm, key, handler); - } - } -} - -function createWatcher ( - vm, - keyOrFn, - handler, - options -) { - if (isPlainObject(handler)) { - options = handler; - handler = handler.handler; - } - if (typeof handler === 'string') { - handler = vm[handler]; - } - return vm.$watch(keyOrFn, handler, options) -} - -function stateMixin (Vue) { - // flow somehow has problems with directly declared definition object - // when using Object.defineProperty, so we have to procedurally build up - // the object here. - var dataDef = {}; - dataDef.get = function () { return this._data }; - var propsDef = {}; - propsDef.get = function () { return this._props }; - if (process.env.NODE_ENV !== 'production') { - dataDef.set = function (newData) { - 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, - cb, - options - ) { - var vm = this; - if (isPlainObject(cb)) { - return createWatcher(vm, expOrFn, cb, options) - } - options = options || {}; - options.user = true; - var watcher = new Watcher(vm, expOrFn, cb, options); - if (options.immediate) { - cb.call(vm, watcher.value); - } - return function unwatchFn () { - watcher.teardown(); - } - }; -} - -/* */ - -function initProvide (vm) { - var provide = vm.$options.provide; - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide; - } -} - -function initInjections (vm) { - var result = resolveInject(vm.$options.inject, vm); - if (result) { - observerState.shouldConvert = false; - Object.keys(result).forEach(function (key) { - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, key, result[key], function () { - 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]); - } - }); - observerState.shouldConvert = true; - } -} - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else if (process.env.NODE_ENV !== 'production') { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - if (process.env.NODE_ENV !== 'production') { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -function initRender (vm) { - vm._vnode = null; // the root of the child tree - var options = vm.$options; - var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree - var renderContext = parentVnode && parentVnode.context; - vm.$slots = resolveSlots(options._renderChildren, renderContext); - vm.$scopedSlots = 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 - vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; - // normalization is always applied for the public version, used in - // user-written render functions. - vm.$createElement = function (a, b, c, d) { return 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 - var parentData = parentVnode && parentVnode.data; - - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () { - !isUpdatingChildComponent && warn("$attrs is readonly.", vm); - }, true); - defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () { - !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); - } -} - -function renderMixin (Vue) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype); - - Vue.prototype.$nextTick = function (fn) { - return nextTick(fn, this) - }; - - Vue.prototype._render = function () { - var vm = this; - var ref = vm.$options; - var render = ref.render; - var _parentVnode = ref._parentVnode; - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (var key in vm.$slots) { - var slot = vm.$slots[key]; - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */); - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode; - // render self - var vnode; - try { - vnode = render.call(vm._renderProxy, vm.$createElement); - } catch (e) { - handleError(e, vm, "render"); - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); - } catch (e) { - handleError(e, vm, "renderError"); - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if (process.env.NODE_ENV !== 'production' && Array.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 - }; -} - -/* */ - -var uid = 0; - -function initMixin (Vue) { - Vue.prototype._init = function (options) { - var vm = this; - // a uid - vm._uid = uid++; - - var startTag, endTag; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - startTag = "vue-perf-start:" + (vm._uid); - endTag = "vue-perf-end:" + (vm._uid); - mark(startTag); - } - - // a flag to avoid this being observed - vm._isVue = 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); - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ); - } - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - initProxy(vm); - } else { - vm._renderProxy = vm; - } - // expose real self - vm._self = vm; - initLifecycle(vm); - initEvents(vm); - initRender(vm); - callHook(vm, 'beforeCreate'); - initInjections(vm); // resolve injections before data/props - initState(vm); - initProvide(vm); // resolve provide after data/props - callHook(vm, 'created'); - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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); - } - }; -} - -function initInternalComponent (vm, options) { - var opts = vm.$options = Object.create(vm.constructor.options); - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent; - opts.propsData = options.propsData; - opts._parentVnode = options._parentVnode; - opts._parentListeners = options._parentListeners; - opts._renderChildren = options._renderChildren; - opts._componentTag = options._componentTag; - opts._parentElm = options._parentElm; - opts._refElm = options._refElm; - if (options.render) { - opts.render = options.render; - opts.staticRenderFns = options.staticRenderFns; - } -} - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -function Vue$3 (options) { - if (process.env.NODE_ENV !== 'production' && - !(this instanceof Vue$3) - ) { - warn('Vue is a constructor and should be called with the `new` keyword'); - } - this._init(options); -} - -initMixin(Vue$3); -stateMixin(Vue$3); -eventsMixin(Vue$3); -lifecycleMixin(Vue$3); -renderMixin(Vue$3); - -/* */ - -function initUse (Vue) { - Vue.use = function (plugin) { - var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - var args = toArray(arguments, 1); - args.unshift(this); - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args); - } else if (typeof plugin === 'function') { - plugin.apply(null, args); - } - installedPlugins.push(plugin); - return this - }; -} - -/* */ - -function initMixin$1 (Vue) { - Vue.mixin = function (mixin) { - this.options = mergeOptions(this.options, mixin); - return this - }; -} - -/* */ - -function initExtend (Vue) { - /** - * 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 - */ - Vue.extend = function (extendOptions) { - extendOptions = extendOptions || {}; - var Super = this; - var SuperId = Super.cid; - var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - 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 characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - } - - var Sub = function VueComponent (options) { - this._init(options); - }; - 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$1(Sub); - } - if (Sub.options.computed) { - initComputed$1(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$1 (Comp) { - var props = Comp.options.props; - for (var key in props) { - proxy(Comp.prototype, "_props", key); - } -} - -function initComputed$1 (Comp) { - var computed = Comp.options.computed; - for (var key in computed) { - defineComputed(Comp.prototype, key, computed[key]); - } -} - -/* */ - -function initAssetRegisters (Vue) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.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' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ); - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id; - definition = this.options._base.extend(definition); - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition }; - } - this.options[type + 's'][id] = definition; - return definition - } - }; - }); -} - -/* */ - -function getComponentName (opts) { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern, name) { - if (Array.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, filter) { - var cache = keepAliveInstance.cache; - var keys = keepAliveInstance.keys; - var _vnode = keepAliveInstance._vnode; - for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode); - } - } - } -} - -function pruneCacheEntry ( - cache, - key, - keys, - current -) { - var cached$$1 = cache[key]; - if (cached$$1 && cached$$1 !== current) { - cached$$1.componentInstance.$destroy(); - } - cache[key] = null; - remove(keys, key); -} - -var patternTypes = [String, RegExp, Array]; - -var KeepAlive = { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created: function created () { - this.cache = Object.create(null); - this.keys = []; - }, - - destroyed: function destroyed () { - var this$1 = this; - - for (var key in this$1.cache) { - pruneCacheEntry(this$1.cache, key, this$1.keys); - } - }, - - watch: { - include: function include (val) { - pruneCache(this, function (name) { return matches(val, name); }); - }, - exclude: function exclude (val) { - pruneCache(this, function (name) { return !matches(val, name); }); - } - }, - - render: function render () { - var vnode = getFirstComponentChild(this.$slots.default); - var componentOptions = vnode && vnode.componentOptions; - if (componentOptions) { - // check pattern - var name = getComponentName(componentOptions); - if (name && ( - (this.exclude && matches(this.exclude, name)) || - (this.include && !matches(this.include, name)) - )) { - return vnode - } - - var ref = this; - var cache = ref.cache; - var keys = ref.keys; - var 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 { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } - } - - vnode.data.keepAlive = true; - } - return vnode - } -}; - -var builtInComponents = { - KeepAlive: KeepAlive -}; - -/* */ - -function initGlobalAPI (Vue) { - // config - var configDef = {}; - configDef.get = function () { return config; }; - if (process.env.NODE_ENV !== 'production') { - configDef.set = function () { - 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: warn, - extend: extend, - mergeOptions: mergeOptions, - defineReactive: defineReactive - }; - - Vue.set = set; - Vue.delete = del; - Vue.nextTick = nextTick; - - Vue.options = Object.create(null); - ASSET_TYPES.forEach(function (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$1(Vue); - initExtend(Vue); - initAssetRegisters(Vue); -} - -initGlobalAPI(Vue$3); - -Object.defineProperty(Vue$3.prototype, '$isServer', { - get: isServerRendering -}); - -Object.defineProperty(Vue$3.prototype, '$ssrContext', { - get: function get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}); - -Vue$3.version = '2.5.3'; - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -var xlinkNS = 'http://www.w3.org/1999/xlink'; - -var isXlink = function (name) { - return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' -}; - -var getXlinkProp = function (name) { - return isXlink(name) ? name.slice(6, name.length) : '' -}; - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - -var namespaceMap = { - svg: 'http://www.w3.org/2000/svg', - math: 'http://www.w3.org/1998/Math/MathML' -}; - -var 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. -var 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 -); - - - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - -var unknownElementCache = Object.create(null); -function isUnknownElement (tag) { - /* 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] - } - var el = document.createElement(tag); - if (tag.indexOf('-') > -1) { - // http://stackoverflow.com/a/28210364/1070244 - return (unknownElementCache[tag] = ( - el.constructor === window.HTMLUnknownElement || - el.constructor === window.HTMLElement - )) - } else { - return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString())) - } -} - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ -function query (el) { - if (typeof el === 'string') { - var selected = document.querySelector(el); - if (!selected) { - process.env.NODE_ENV !== 'production' && warn( - 'Cannot find element: ' + el - ); - return document.createElement('div') - } - return selected - } else { - return el - } -} - -/* */ - -function createElement$1 (tagName, vnode) { - var 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 -} - -function createElementNS (namespace, tagName) { - return document.createElementNS(namespaceMap[namespace], tagName) -} - -function createTextNode (text) { - return document.createTextNode(text) -} - -function createComment (text) { - return document.createComment(text) -} - -function insertBefore (parentNode, newNode, referenceNode) { - parentNode.insertBefore(newNode, referenceNode); -} - -function removeChild (node, child) { - node.removeChild(child); -} - -function appendChild (node, child) { - node.appendChild(child); -} - -function parentNode (node) { - return node.parentNode -} - -function nextSibling (node) { - return node.nextSibling -} - -function tagName (node) { - return node.tagName -} - -function setTextContent (node, text) { - node.textContent = text; -} - -function setAttribute (node, key, val) { - node.setAttribute(key, val); -} - - -var nodeOps = Object.freeze({ - createElement: createElement$1, - createElementNS: createElementNS, - createTextNode: createTextNode, - createComment: createComment, - insertBefore: insertBefore, - removeChild: removeChild, - appendChild: appendChild, - parentNode: parentNode, - nextSibling: nextSibling, - tagName: tagName, - setTextContent: setTextContent, - setAttribute: setAttribute -}); - -/* */ - -var ref = { - create: function create (_, vnode) { - registerRef(vnode); - }, - update: function update (oldVnode, vnode) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true); - registerRef(vnode); - } - }, - destroy: function destroy (vnode) { - registerRef(vnode, true); - } -}; - -function registerRef (vnode, isRemoval) { - var key = vnode.data.ref; - if (!key) { return } - - var vm = vnode.context; - var ref = vnode.componentInstance || vnode.elm; - var refs = vm.$refs; - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref); - } else if (refs[key] === ref) { - refs[key] = undefined; - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref]; - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref); - } - } else { - refs[key] = ref; - } - } -} - -/** - * 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. - */ - -var emptyNode = new VNode('', {}, []); - -var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; - -function sameVnode (a, b) { - return ( - a.key === b.key && ( - ( - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - isUndef(b.asyncFactory.error) - ) - ) - ) -} - -function sameInputType (a, b) { - if (a.tag !== 'input') { return true } - var i; - var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type; - var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type; - return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) -} - -function createKeyToOldIdx (children, beginIdx, endIdx) { - var i, key; - var map = {}; - for (i = beginIdx; i <= endIdx; ++i) { - key = children[i].key; - if (isDef(key)) { map[key] = i; } - } - return map -} - -function createPatchFunction (backend) { - var i, j; - var cbs = {}; - - var modules = backend.modules; - var nodeOps = backend.nodeOps; - - 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) { - var parent = nodeOps.parentNode(el); - // element may have already been removed due to v-html / v-text - if (isDef(parent)) { - nodeOps.removeChild(parent, el); - } - } - - var inPre = 0; - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - vnode.isRootInsert = !nested; // for transition enter check - if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { - return - } - - var data = vnode.data; - var children = vnode.children; - var tag = vnode.tag; - if (isDef(tag)) { - if (process.env.NODE_ENV !== 'production') { - if (data && data.pre) { - inPre++; - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(function (ignore) { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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); - - /* istanbul ignore if */ - { - createChildren(vnode, children, insertedVnodeQueue); - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - - if (process.env.NODE_ENV !== 'production' && data && data.pre) { - inPre--; - } - } 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) { - var i = vnode.data; - if (isDef(i)) { - var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; - if (isDef(i = i.hook) && isDef(i = i.init)) { - i(vnode, false /* hydrating */, parentElm, refElm); - } - // 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); - 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) { - var 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. - var 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$$1) { - if (isDef(parent)) { - if (isDef(ref$$1)) { - if (ref$$1.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref$$1); - } - } else { - nodeOps.appendChild(parent, elm); - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); - } - } - - function isPatchable (vnode) { - while (vnode.componentInstance) { - vnode = vnode.componentInstance._vnode; - } - return isDef(vnode.tag) - } - - function invokeCreateHooks (vnode, insertedVnodeQueue) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](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) { - var i; - if (isDef(i = vnode.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, ''); - } else { - var ancestor = vnode; - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, ''); - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm); - } - } - - function invokeDestroyHook (vnode) { - var i, j; - var 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 (parentElm, vnodes, startIdx, endIdx) { - for (; startIdx <= endIdx; ++startIdx) { - var ch = vnodes[startIdx]; - if (isDef(ch)) { - if (isDef(ch.tag)) { - removeAndInvokeRemoveHook(ch); - invokeDestroyHook(ch); - } else { // Text node - removeNode(ch.elm); - } - } - } - } - - function removeAndInvokeRemoveHook (vnode, rm) { - if (isDef(rm) || isDef(vnode.data)) { - var i; - var 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) { - var oldStartIdx = 0; - var newStartIdx = 0; - var oldEndIdx = oldCh.length - 1; - var oldStartVnode = oldCh[0]; - var oldEndVnode = oldCh[oldEndIdx]; - var newEndIdx = newCh.length - 1; - var newStartVnode = newCh[0]; - var newEndVnode = newCh[newEndIdx]; - var 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 - var canMove = !removeOnly; - - 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); - oldStartVnode = oldCh[++oldStartIdx]; - newStartVnode = newCh[++newStartIdx]; - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); - oldEndVnode = oldCh[--oldEndIdx]; - newEndVnode = newCh[--newEndIdx]; - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); - 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); - 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); - } else { - vnodeToMove = oldCh[idxInOld]; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ); - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue); - 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); - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx); - } - } - - function findIdxInOld (node, oldCh, start, end) { - for (var i = start; i < end; i++) { - var c = oldCh[i]; - if (isDef(c) && sameVnode(node, c)) { return i } - } - } - - function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { - if (oldVnode === vnode) { - return - } - - var 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 - } - - var i; - var data = vnode.data; - if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { - i(oldVnode, vnode); - } - - var oldCh = oldVnode.children; - var 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 (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 (var i = 0; i < queue.length; ++i) { - queue[i].data.hook.insert(queue[i]); - } - } - } - - var bailed = 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 - var isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key'); - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm; - vnode.isAsyncPlaceholder = true; - return true - } - if (process.env.NODE_ENV !== 'production') { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm; - var tag = vnode.tag; - var data = vnode.data; - var children = vnode.children; - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('server innerHTML: ', i); - console.warn('client innerHTML: ', elm.innerHTML); - } - return false - } - } else { - // iterate and compare children lists - var childrenMatch = true; - var childNode = elm.firstChild; - for (var i$1 = 0; i$1 < children.length; i$1++) { - if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue)) { - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); - } - return false - } - } - } - } - if (isDef(data)) { - for (var key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text; - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } - return - } - - var isInitialPatch = false; - var insertedVnodeQueue = []; - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true; - createElm(vnode, insertedVnodeQueue, parentElm, refElm); - } else { - var isRealElement = isDef(oldVnode.nodeType); - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 (process.env.NODE_ENV !== 'production') { - 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 - var oldElm = oldVnode.elm; - var parentElm$1 = 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$1, - nodeOps.nextSibling(oldElm) - ); - - // update parent placeholder node element, recursively - if (isDef(vnode.parent)) { - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - // destroy old node - if (isDef(parentElm$1)) { - removeVnodes(parentElm$1, [oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } -} - -/* */ - -var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } -}; - -function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } -} - -function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode, 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode, 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } -} - -var emptyModifiers = Object.create(null); - -function normalizeDirectives$1 ( - dirs, - vm -) { - var res = Object.create(null); - if (!dirs) { - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - return res -} - -function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) -} - -function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } -} - -var baseModules = [ - ref, - directives -]; - -/* */ - -function updateAttrs (oldVnode, vnode) { - var opts = vnode.componentOptions; - if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { - return - } - if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(attrs.__ob__)) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - setAttr(elm, key, cur); - } - } - // #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 ((isIE9 || 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, key, value) { - 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, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'); - } else if (isXlink(key)) { - if (isFalsyAttrValue(value)) { - el.removeAttributeNS(xlinkNS, getXlinkProp(key)); - } else { - el.setAttributeNS(xlinkNS, key, value); - } - } else { - if (isFalsyAttrValue(value)) { - el.removeAttribute(key); - } else { - el.setAttribute(key, value); - } - } -} - -var attrs = { - create: updateAttrs, - update: updateAttrs -}; - -/* */ - -function updateClass (oldVnode, vnode) { - var el = vnode.elm; - var data = vnode.data; - var oldData = oldVnode.data; - if ( - isUndef(data.staticClass) && - isUndef(data.class) && ( - isUndef(oldData) || ( - isUndef(oldData.staticClass) && - isUndef(oldData.class) - ) - ) - ) { - return - } - - var cls = genClassForVnode(vnode); - - // handle transition classes - var 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; - } -} - -var klass = { - create: updateClass, - update: updateClass -}; - -/* */ - -/* */ - - - - - - - - - - - - - - - -// 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. - -/* */ - -/** - * Cross-platform code generation for component v-model - */ - - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ - -/* */ - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; -var CHECKBOX_RADIO_TOKEN = '__c'; - -/* */ - -// 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 - var 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]; - } -} - -var target$1; - -function createOnceHandler (handler, event, capture) { - var _target = target$1; // save current target element in closure - return function onceHandler () { - var res = handler.apply(null, arguments); - if (res !== null) { - remove$2(event, onceHandler, capture, _target); - } - } -} - -function add$1 ( - event, - handler, - once$$1, - capture, - passive -) { - handler = withMacroTask(handler); - if (once$$1) { handler = createOnceHandler(handler, event, capture); } - target$1.addEventListener( - event, - handler, - supportsPassive - ? { capture: capture, passive: passive } - : capture - ); -} - -function remove$2 ( - event, - handler, - capture, - _target -) { - (_target || target$1).removeEventListener( - event, - handler._withTask || handler, - capture - ); -} - -function updateDOMListeners (oldVnode, vnode) { - if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { - return - } - var on = vnode.data.on || {}; - var oldOn = oldVnode.data.on || {}; - target$1 = vnode.elm; - normalizeEvents(on); - updateListeners(on, oldOn, add$1, remove$2, vnode.context); - target$1 = undefined; -} - -var events = { - create: updateDOMListeners, - update: updateDOMListeners -}; - -/* */ - -function updateDOMProps (oldVnode, vnode) { - if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { - return - } - var key, cur; - var elm = vnode.elm; - var oldProps = oldVnode.data.domProps || {}; - var props = vnode.data.domProps || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(props.__ob__)) { - props = vnode.data.domProps = extend({}, props); - } - - for (key in oldProps) { - if (isUndef(props[key])) { - 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') { - // 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 - var strCur = isUndef(cur) ? '' : String(cur); - if (shouldUpdateValue(elm, strCur)) { - elm.value = strCur; - } - } else { - elm[key] = cur; - } - } -} - -// check platforms/web/util/attrs.js acceptValue - - -function shouldUpdateValue (elm, checkVal) { - return (!elm.composing && ( - elm.tagName === 'OPTION' || - isDirty(elm, checkVal) || - isInputChanged(elm, checkVal) - )) -} - -function isDirty (elm, checkVal) { - // return true when textbox (.number and .trim) loses focus and its value is - // not equal to the updated value - var notInFocus = true; - // #6157 - // work around IE bug when accessing document.activeElement in an iframe - try { notInFocus = document.activeElement !== elm; } catch (e) {} - return notInFocus && elm.value !== checkVal -} - -function isInputChanged (elm, newVal) { - var value = elm.value; - var modifiers = elm._vModifiers; // injected by v-model runtime - if (isDef(modifiers) && modifiers.number) { - return toNumber(value) !== toNumber(newVal) - } - if (isDef(modifiers) && modifiers.trim) { - return value.trim() !== newVal.trim() - } - return value !== newVal -} - -var domProps = { - create: updateDOMProps, - update: updateDOMProps -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -var cssVarRE = /^--/; -var importantRE = /\s*!important$/; -var setProp = function (el, name, val) { - /* istanbul ignore if */ - if (cssVarRE.test(name)) { - el.style.setProperty(name, val); - } else if (importantRE.test(val)) { - el.style.setProperty(name, val.replace(importantRE, ''), 'important'); - } else { - var 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 (var i = 0, len = val.length; i < len; i++) { - el.style[normalizedName] = val[i]; - } - } else { - el.style[normalizedName] = val; - } - } -}; - -var vendorNames = ['Webkit', 'Moz', 'ms']; - -var emptyStyle; -var normalize = cached(function (prop) { - emptyStyle = emptyStyle || document.createElement('div').style; - prop = camelize(prop); - if (prop !== 'filter' && (prop in emptyStyle)) { - return prop - } - var capName = prop.charAt(0).toUpperCase() + prop.slice(1); - for (var i = 0; i < vendorNames.length; i++) { - var name = vendorNames[i] + capName; - if (name in emptyStyle) { - return name - } - } -}); - -function updateStyle (oldVnode, vnode) { - var data = vnode.data; - var oldData = oldVnode.data; - - if (isUndef(data.staticStyle) && isUndef(data.style) && - isUndef(oldData.staticStyle) && isUndef(oldData.style) - ) { - return - } - - var cur, name; - var el = vnode.elm; - var oldStaticStyle = oldData.staticStyle; - var oldStyleBinding = oldData.normalizedStyle || oldData.style || {}; - - // if static style exists, stylebinding already merged into it when doing normalizeStyleData - var oldStyle = oldStaticStyle || oldStyleBinding; - - var 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; - - var newStyle = getStyle(vnode, true); - - for (name in oldStyle) { - if (isUndef(newStyle[name])) { - setProp(el, name, ''); - } - } - for (name in newStyle) { - cur = newStyle[name]; - if (cur !== oldStyle[name]) { - // ie9 setting to null has no effect, must use empty string - setProp(el, name, cur == null ? '' : cur); - } - } -} - -var style = { - create: updateStyle, - update: updateStyle -}; - -/* */ - -/** - * Add class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ -function addClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.add(c); }); - } else { - el.classList.add(cls); - } - } else { - var 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 - */ -function removeClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.remove(c); }); - } else { - el.classList.remove(cls); - } - if (!el.classList.length) { - el.removeAttribute('class'); - } - } else { - var cur = " " + (el.getAttribute('class') || '') + " "; - var tar = ' ' + cls + ' '; - while (cur.indexOf(tar) >= 0) { - cur = cur.replace(tar, ' '); - } - cur = cur.trim(); - if (cur) { - el.setAttribute('class', cur); - } else { - el.removeAttribute('class'); - } - } -} - -/* */ - -function resolveTransition (def) { - if (!def) { - return - } - /* istanbul ignore else */ - if (typeof def === 'object') { - var res = {}; - if (def.css !== false) { - extend(res, autoCssTransition(def.name || 'v')); - } - extend(res, def); - return res - } else if (typeof def === 'string') { - return autoCssTransition(def) - } -} - -var autoCssTransition = cached(function (name) { - return { - enterClass: (name + "-enter"), - enterToClass: (name + "-enter-to"), - enterActiveClass: (name + "-enter-active"), - leaveClass: (name + "-leave"), - leaveToClass: (name + "-leave-to"), - leaveActiveClass: (name + "-leave-active") - } -}); - -var hasTransition = inBrowser && !isIE9; -var TRANSITION = 'transition'; -var ANIMATION = 'animation'; - -// Transition property/event sniffing -var transitionProp = 'transition'; -var transitionEndEvent = 'transitionend'; -var animationProp = 'animation'; -var 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 -var raf = inBrowser - ? window.requestAnimationFrame - ? window.requestAnimationFrame.bind(window) - : setTimeout - : /* istanbul ignore next */ function (fn) { return fn(); }; - -function nextFrame (fn) { - raf(function () { - raf(fn); - }); -} - -function addTransitionClass (el, cls) { - var transitionClasses = el._transitionClasses || (el._transitionClasses = []); - if (transitionClasses.indexOf(cls) < 0) { - transitionClasses.push(cls); - addClass(el, cls); - } -} - -function removeTransitionClass (el, cls) { - if (el._transitionClasses) { - remove(el._transitionClasses, cls); - } - removeClass(el, cls); -} - -function whenTransitionEnds ( - el, - expectedType, - cb -) { - var ref = getTransitionInfo(el, expectedType); - var type = ref.type; - var timeout = ref.timeout; - var propCount = ref.propCount; - if (!type) { return cb() } - var event = type === TRANSITION ? transitionEndEvent : animationEndEvent; - var ended = 0; - var end = function () { - el.removeEventListener(event, onEnd); - cb(); - }; - var onEnd = function (e) { - if (e.target === el) { - if (++ended >= propCount) { - end(); - } - } - }; - setTimeout(function () { - if (ended < propCount) { - end(); - } - }, timeout + 1); - el.addEventListener(event, onEnd); -} - -var transformRE = /\b(transform|all)(,|$)/; - -function getTransitionInfo (el, expectedType) { - var styles = window.getComputedStyle(el); - var transitionDelays = styles[transitionProp + 'Delay'].split(', '); - var transitionDurations = styles[transitionProp + 'Duration'].split(', '); - var transitionTimeout = getTimeout(transitionDelays, transitionDurations); - var animationDelays = styles[animationProp + 'Delay'].split(', '); - var animationDurations = styles[animationProp + 'Duration'].split(', '); - var animationTimeout = getTimeout(animationDelays, animationDurations); - - var type; - var timeout = 0; - var 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; - } - var hasTransform = - type === TRANSITION && - transformRE.test(styles[transitionProp + 'Property']); - return { - type: type, - timeout: timeout, - propCount: propCount, - hasTransform: hasTransform - } -} - -function getTimeout (delays, durations) { - /* istanbul ignore next */ - while (delays.length < durations.length) { - delays = delays.concat(delays); - } - - return Math.max.apply(null, durations.map(function (d, i) { - return toMs(d) + toMs(delays[i]) - })) -} - -function toMs (s) { - return Number(s.slice(0, -1)) * 1000 -} - -/* */ - -function enter (vnode, toggleDisplay) { - var el = vnode.elm; - - // call leave callback now - if (isDef(el._leaveCb)) { - el._leaveCb.cancelled = true; - el._leaveCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return - } - - /* istanbul ignore if */ - if (isDef(el._enterCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var enterClass = data.enterClass; - var enterToClass = data.enterToClass; - var enterActiveClass = data.enterActiveClass; - var appearClass = data.appearClass; - var appearToClass = data.appearToClass; - var appearActiveClass = data.appearActiveClass; - var beforeEnter = data.beforeEnter; - var enter = data.enter; - var afterEnter = data.afterEnter; - var enterCancelled = data.enterCancelled; - var beforeAppear = data.beforeAppear; - var appear = data.appear; - var afterAppear = data.afterAppear; - var appearCancelled = data.appearCancelled; - var duration = data.duration; - - // 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. - var context = activeInstance; - var transitionNode = activeInstance.$vnode; - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent; - context = transitionNode.context; - } - - var isAppear = !context._isMounted || !vnode.isRootInsert; - - if (isAppear && !appear && appear !== '') { - return - } - - var startClass = isAppear && appearClass - ? appearClass - : enterClass; - var activeClass = isAppear && appearActiveClass - ? appearActiveClass - : enterActiveClass; - var toClass = isAppear && appearToClass - ? appearToClass - : enterToClass; - - var beforeEnterHook = isAppear - ? (beforeAppear || beforeEnter) - : beforeEnter; - var enterHook = isAppear - ? (typeof appear === 'function' ? appear : enter) - : enter; - var afterEnterHook = isAppear - ? (afterAppear || afterEnter) - : afterEnter; - var enterCancelledHook = isAppear - ? (appearCancelled || enterCancelled) - : enterCancelled; - - var explicitEnterDuration = toNumber( - isObject(duration) - ? duration.enter - : duration - ); - - if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) { - checkDuration(explicitEnterDuration, 'enter', vnode); - } - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(enterHook); - - var cb = el._enterCb = once(function () { - if (expectsCSS) { - removeTransitionClass(el, toClass); - removeTransitionClass(el, activeClass); - } - 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', function () { - var parent = el.parentNode; - var 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(function () { - addTransitionClass(el, toClass); - removeTransitionClass(el, startClass); - if (!cb.cancelled && !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(); - } -} - -function leave (vnode, rm) { - var el = vnode.elm; - - // call enter callback now - if (isDef(el._enterCb)) { - el._enterCb.cancelled = true; - el._enterCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return rm() - } - - /* istanbul ignore if */ - if (isDef(el._leaveCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var leaveClass = data.leaveClass; - var leaveToClass = data.leaveToClass; - var leaveActiveClass = data.leaveActiveClass; - var beforeLeave = data.beforeLeave; - var leave = data.leave; - var afterLeave = data.afterLeave; - var leaveCancelled = data.leaveCancelled; - var delayLeave = data.delayLeave; - var duration = data.duration; - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(leave); - - var explicitLeaveDuration = toNumber( - isObject(duration) - ? duration.leave - : duration - ); - - if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) { - checkDuration(explicitLeaveDuration, 'leave', vnode); - } - - var cb = el._leaveCb = once(function () { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null; - } - if (expectsCSS) { - removeTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveActiveClass); - } - 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 - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode; - } - beforeLeave && beforeLeave(el); - if (expectsCSS) { - addTransitionClass(el, leaveClass); - addTransitionClass(el, leaveActiveClass); - nextFrame(function () { - addTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveClass); - if (!cb.cancelled && !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) { - if (isUndef(fn)) { - return false - } - var invokerFns = fn.fns; - if (isDef(invokerFns)) { - // invoker - return getHookArgumentsLength( - Array.isArray(invokerFns) - ? invokerFns[0] - : invokerFns - ) - } else { - return (fn._length || fn.length) > 1 - } -} - -function _enter (_, vnode) { - if (vnode.data.show !== true) { - enter(vnode); - } -} - -var transition = inBrowser ? { - create: _enter, - activate: _enter, - remove: function remove$$1 (vnode, rm) { - /* istanbul ignore else */ - if (vnode.data.show !== true) { - leave(vnode, rm); - } else { - rm(); - } - } -} : {}; - -var platformModules = [ - attrs, - klass, - events, - domProps, - style, - transition -]; - -/* */ - -// the directive module should be applied last, after all -// built-in modules have been applied. -var modules = platformModules.concat(baseModules); - -var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules }); - -/** - * Not type checking this file because flow doesn't like attaching - * properties to Elements. - */ - -/* istanbul ignore if */ -if (isIE9) { - // http://www.matts411.com/post/internet-explorer-9-oninput/ - document.addEventListener('selectionchange', function () { - var el = document.activeElement; - if (el && el.vmodel) { - trigger(el, 'input'); - } - }); -} - -var directive = { - inserted: function inserted (el, binding, vnode, oldVnode) { - if (vnode.tag === 'select') { - // #6903 - if (oldVnode.elm && !oldVnode.elm._vOptions) { - mergeVNodeHook(vnode, 'postpatch', function () { - 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) { - // 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); - if (!isAndroid) { - el.addEventListener('compositionstart', onCompositionStart); - el.addEventListener('compositionend', onCompositionEnd); - } - /* istanbul ignore if */ - if (isIE9) { - el.vmodel = true; - } - } - } - }, - - componentUpdated: function 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. - var prevOptions = el._vOptions; - var curOptions = el._vOptions = [].map.call(el.options, getValue); - if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) { - // trigger change event if - // no matching option found for at least one value - var needReset = el.multiple - ? binding.value.some(function (v) { return 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(function () { - actuallySetSelected(el, binding, vm); - }, 0); - } -} - -function actuallySetSelected (el, binding, vm) { - var value = binding.value; - var isMultiple = el.multiple; - if (isMultiple && !Array.isArray(value)) { - process.env.NODE_ENV !== 'production' && 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 - } - var selected, option; - for (var 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(function (o) { return !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) { - var e = document.createEvent('HTMLEvents'); - e.initEvent(type, true, true); - el.dispatchEvent(e); -} - -/* */ - -// recursively search for possible transition defined inside the component root -function locateNode (vnode) { - return vnode.componentInstance && (!vnode.data || !vnode.data.transition) - ? locateNode(vnode.componentInstance._vnode) - : vnode -} - -var show = { - bind: function bind (el, ref, vnode) { - var value = ref.value; - - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - var originalDisplay = el.__vOriginalDisplay = - el.style.display === 'none' ? '' : el.style.display; - if (value && transition$$1) { - vnode.data.show = true; - enter(vnode, function () { - el.style.display = originalDisplay; - }); - } else { - el.style.display = value ? originalDisplay : 'none'; - } - }, - - update: function update (el, ref, vnode) { - var value = ref.value; - var oldValue = ref.oldValue; - - /* istanbul ignore if */ - if (value === oldValue) { return } - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - if (transition$$1) { - vnode.data.show = true; - if (value) { - enter(vnode, function () { - el.style.display = el.__vOriginalDisplay; - }); - } else { - leave(vnode, function () { - el.style.display = 'none'; - }); - } - } else { - el.style.display = value ? el.__vOriginalDisplay : 'none'; - } - }, - - unbind: function unbind ( - el, - binding, - vnode, - oldVnode, - isDestroy - ) { - if (!isDestroy) { - el.style.display = el.__vOriginalDisplay; - } - } -}; - -var platformDirectives = { - model: directive, - show: show -}; - -/* */ - -// Provides transition support for a single element/component. -// supports transition mode (out-in / in-out) - -var 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) { - var compOptions = vnode && vnode.componentOptions; - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -function extractTransitionData (comp) { - var data = {}; - var options = comp.$options; - // props - for (var key in options.propsData) { - data[key] = comp[key]; - } - // events. - // extract listeners and pass them directly to the transition methods - var listeners = options._parentListeners; - for (var key$1 in listeners) { - data[camelize(key$1)] = listeners[key$1]; - } - return data -} - -function placeholder (h, rawChild) { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode) { - while ((vnode = vnode.parent)) { - if (vnode.data.transition) { - return true - } - } -} - -function isSameChild (child, oldChild) { - return oldChild.key === child.key && oldChild.tag === child.tag -} - -var Transition = { - name: 'transition', - props: transitionProps, - abstract: true, - - render: function render (h) { - var this$1 = this; - - var children = this.$options._renderChildren; - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if (process.env.NODE_ENV !== 'production' && children.length > 1) { - warn( - '<transition> can only be used on a single element. Use ' + - '<transition-group> for lists.', - this.$parent - ); - } - - var mode = this.mode; - - // warn invalid mode - if (process.env.NODE_ENV !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in' - ) { - warn( - 'invalid <transition> mode: ' + mode, - this.$parent - ); - } - - var rawChild = 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 - var 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. - var id = "__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; - - var data = (child.data || (child.data = {})).transition = extractTransitionData(this); - var oldRawChild = this._vnode; - var 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(function (d) { return d.name === 'show'; })) { - child.data.show = true; - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // replace old child transition data with fresh one - // important for dynamic transitions! - var oldData = 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', function () { - this$1._leaving = false; - this$1.$forceUpdate(); - }); - return placeholder(h, rawChild) - } else if (mode === 'in-out') { - if (isAsyncPlaceholder(child)) { - return oldRawChild - } - var delayedLeave; - var performLeave = function () { delayedLeave(); }; - mergeVNodeHook(data, 'afterEnter', performLeave); - mergeVNodeHook(data, 'enterCancelled', performLeave); - mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; }); - } - } - - return rawChild - } -}; - -/* */ - -// 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. - -var props = extend({ - tag: String, - moveClass: String -}, transitionProps); - -delete props.mode; - -var TransitionGroup = { - props: props, - - render: function render (h) { - var tag = this.tag || this.$vnode.data.tag || 'span'; - var map = Object.create(null); - var prevChildren = this.prevChildren = this.children; - var rawChildren = this.$slots.default || []; - var children = this.children = []; - var transitionData = extractTransitionData(this); - - for (var i = 0; i < rawChildren.length; i++) { - var c = 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 (process.env.NODE_ENV !== 'production') { - var opts = c.componentOptions; - var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag; - warn(("<transition-group> children must be keyed: <" + name + ">")); - } - } - } - - if (prevChildren) { - var kept = []; - var removed = []; - for (var i$1 = 0; i$1 < prevChildren.length; i$1++) { - var c$1 = prevChildren[i$1]; - c$1.data.transition = transitionData; - c$1.data.pos = c$1.elm.getBoundingClientRect(); - if (map[c$1.key]) { - kept.push(c$1); - } else { - removed.push(c$1); - } - } - this.kept = h(tag, null, kept); - this.removed = removed; - } - - return h(tag, null, children) - }, - - beforeUpdate: function beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ); - this._vnode = this.kept; - }, - - updated: function updated () { - var children = this.prevChildren; - var moveClass = 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(function (c) { - if (c.data.moved) { - var el = c.elm; - var s = el.style; - addTransitionClass(el, moveClass); - s.transform = s.WebkitTransform = s.transitionDuration = ''; - el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { - if (!e || /transform$/.test(e.propertyName)) { - el.removeEventListener(transitionEndEvent, cb); - el._moveCb = null; - removeTransitionClass(el, moveClass); - } - }); - } - }); - }, - - methods: { - hasMove: function hasMove (el, moveClass) { - /* 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. - var clone = el.cloneNode(); - if (el._transitionClasses) { - el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); }); - } - addClass(clone, moveClass); - clone.style.display = 'none'; - this.$el.appendChild(clone); - var info = getTransitionInfo(clone); - this.$el.removeChild(clone); - return (this._hasMove = info.hasTransform) - } - } -}; - -function callPendingCbs (c) { - /* istanbul ignore if */ - if (c.elm._moveCb) { - c.elm._moveCb(); - } - /* istanbul ignore if */ - if (c.elm._enterCb) { - c.elm._enterCb(); - } -} - -function recordPosition (c) { - c.data.newPos = c.elm.getBoundingClientRect(); -} - -function applyTranslation (c) { - var oldPos = c.data.pos; - var newPos = c.data.newPos; - var dx = oldPos.left - newPos.left; - var dy = oldPos.top - newPos.top; - if (dx || dy) { - c.data.moved = true; - var s = c.elm.style; - s.transform = s.WebkitTransform = "translate(" + dx + "px," + dy + "px)"; - s.transitionDuration = '0s'; - } + module.exports = require('./vue.runtime.common.dev.js') } - -var platformComponents = { - Transition: Transition, - TransitionGroup: TransitionGroup -}; - -/* */ - -// install platform specific utils -Vue$3.config.mustUseProp = mustUseProp; -Vue$3.config.isReservedTag = isReservedTag; -Vue$3.config.isReservedAttr = isReservedAttr; -Vue$3.config.getTagNamespace = getTagNamespace; -Vue$3.config.isUnknownElement = isUnknownElement; - -// install platform runtime directives & components -extend(Vue$3.options.directives, platformDirectives); -extend(Vue$3.options.components, platformComponents); - -// install platform patch function -Vue$3.prototype.__patch__ = inBrowser ? patch : noop; - -// public mount method -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && inBrowser ? query(el) : undefined; - return mountComponent(this, el, hydrating) -}; - -// devtools global hook -/* istanbul ignore next */ -Vue$3.nextTick(function () { - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue$3); - } else if (process.env.NODE_ENV !== 'production' && isChrome) { - console[console.info ? 'info' : 'log']( - 'Download the Vue Devtools extension for a better development experience:\n' + - 'https://github.com/vuejs/vue-devtools' - ); - } - } - if (process.env.NODE_ENV !== 'production' && - config.productionTip !== false && - inBrowser && typeof console !== 'undefined' - ) { - 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); - -/* */ - -module.exports = Vue$3; diff --git a/dist/vue.runtime.esm.js b/dist/vue.runtime.esm.js deleted file mode 100644 index d6f7bc5a676..00000000000 --- a/dist/vue.runtime.esm.js +++ /dev/null @@ -1,7792 +0,0 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 Evan You - * Released under the MIT License. - */ -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real 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. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ - - -/** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid$1 = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid$1++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - 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) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else if (process.env.NODE_ENV !== 'production') { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else if (process.env.NODE_ENV !== 'production') { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if (process.env.NODE_ENV !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (process.env.NODE_ENV !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -if (process.env.NODE_ENV !== 'production') { - var 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,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -var mark; -var measure; - -if (process.env.NODE_ENV !== 'production') { - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var 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; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - if (process.env.NODE_ENV !== 'production') { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - process.env.NODE_ENV !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - process.env.NODE_ENV !== 'production' - ? ("timeout (" + (res.timeout) + "ms)") - : null - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - -function getFirstComponentChild (children) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var c = children[i]; - if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { - return c - } - } - } -} - -/* */ - -/* */ - -function initEvents (vm) { - vm._events = Object.create(null); - vm._hasHookEvent = false; - // init parent attached events - var listeners = vm.$options._parentListeners; - if (listeners) { - updateComponentListeners(vm, listeners); - } -} - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -function eventsMixin (Vue) { - var hookRE = /^hook:/; - Vue.prototype.$on = function (event, fn) { - var this$1 = this; - - var vm = this; - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$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, fn) { - var vm = 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, fn) { - var this$1 = this; - - var vm = this; - // all - if (!arguments.length) { - vm._events = Object.create(null); - return vm - } - // array of events - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$off(event[i], fn); - } - return vm - } - // specific event - var cbs = vm._events[event]; - if (!cbs) { - return vm - } - if (!fn) { - vm._events[event] = null; - return vm - } - if (fn) { - // specific handler - var cb; - var i$1 = cbs.length; - while (i$1--) { - cb = cbs[i$1]; - if (cb === fn || cb.fn === fn) { - cbs.splice(i$1, 1); - break - } - } - } - return vm - }; - - Vue.prototype.$emit = function (event) { - var vm = this; - if (process.env.NODE_ENV !== 'production') { - var 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 + "\"." - ); - } - } - var cbs = vm._events[event]; - if (cbs) { - cbs = cbs.length > 1 ? toArray(cbs) : cbs; - var args = toArray(arguments, 1); - for (var i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args); - } catch (e) { - handleError(e, vm, ("event handler for \"" + event + "\"")); - } - } - } - return vm - }; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; -var isUpdatingChildComponent = false; - -function initLifecycle (vm) { - var options = vm.$options; - - // locate first non-abstract parent - var 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._watcher = null; - vm._inactive = null; - vm._directInactive = false; - vm._isMounted = false; - vm._isDestroyed = false; - vm._isBeingDestroyed = false; -} - -function lifecycleMixin (Vue) { - Vue.prototype._update = function (vnode, hydrating) { - var vm = this; - if (vm._isMounted) { - callHook(vm, 'beforeUpdate'); - } - var prevEl = vm.$el; - var prevVnode = vm._vnode; - var prevActiveInstance = activeInstance; - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ); - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null; - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode); - } - activeInstance = prevActiveInstance; - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el; - } - // updated hook is called by the scheduler to ensure that children are - // updated in a parent's updated hook. - }; - - Vue.prototype.$forceUpdate = function () { - var vm = this; - if (vm._watcher) { - vm._watcher.update(); - } - }; - - Vue.prototype.$destroy = function () { - var vm = this; - if (vm._isBeingDestroyed) { - return - } - callHook(vm, 'beforeDestroy'); - vm._isBeingDestroyed = true; - // remove self from parent - var parent = vm.$parent; - if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { - remove(parent.$children, vm); - } - // teardown watchers - if (vm._watcher) { - vm._watcher.teardown(); - } - var i = vm._watchers.length; - while (i--) { - vm._watchers[i].teardown(); - } - // 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; - } - }; -} - -function mountComponent ( - vm, - el, - hydrating -) { - vm.$el = el; - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode; - if (process.env.NODE_ENV !== 'production') { - /* 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'); - - var updateComponent; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - updateComponent = function () { - var name = vm._name; - var id = vm._uid; - var startTag = "vue-perf-start:" + id; - var endTag = "vue-perf-end:" + id; - - mark(startTag); - var 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 = function () { - vm._update(vm._render(), hydrating); - }; - } - - vm._watcher = new Watcher(vm, updateComponent, noop); - hydrating = false; - - // 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 -} - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = true; - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = false; - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index = queue.length = activatedChildren.length = 0; - has = {}; - if (process.env.NODE_ENV !== 'production') { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // 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]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var 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; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = process.env.NODE_ENV !== 'production' - ? expOrFn.toString() - : ''; - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - process.env.NODE_ENV !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var sharedPropertyDefinition = { - enumerable: true, - configurable: true, - get: noop, - set: noop -}; - -function proxy (target, sourceKey, key) { - sharedPropertyDefinition.get = function proxyGetter () { - return this[sourceKey][key] - }; - sharedPropertyDefinition.set = function proxySetter (val) { - this[sourceKey][key] = val; - }; - Object.defineProperty(target, key, sharedPropertyDefinition); -} - -function initState (vm) { - vm._watchers = []; - var opts = vm.$options; - if (opts.props) { initProps(vm, opts.props); } - if (opts.methods) { initMethods(vm, opts.methods); } - if (opts.data) { - initData(vm); - } else { - observe(vm._data = {}, true /* asRootData */); - } - if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch && opts.watch !== nativeWatch) { - initWatch(vm, opts.watch); - } -} - -function initProps (vm, propsOptions) { - var propsData = vm.$options.propsData || {}; - var props = vm._props = {}; - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - var keys = vm.$options._propKeys = []; - var isRoot = !vm.$parent; - // root instance props should be converted - observerState.shouldConvert = isRoot; - var loop = function ( key ) { - keys.push(key); - var value = validateProp(key, propsOptions, propsData, vm); - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - var 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, function () { - if (vm.$parent && !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 - ); - } - }); - } else { - defineReactive(props, key, value); - } - // 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); - } - }; - - for (var key in propsOptions) loop( key ); - observerState.shouldConvert = true; -} - -function initData (vm) { - var data = vm.$options.data; - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {}; - if (!isPlainObject(data)) { - data = {}; - process.env.NODE_ENV !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', - vm - ); - } - // proxy data on instance - var keys = Object.keys(data); - var props = vm.$options.props; - var methods = vm.$options.methods; - var i = keys.length; - while (i--) { - var key = keys[i]; - if (process.env.NODE_ENV !== 'production') { - if (methods && hasOwn(methods, key)) { - warn( - ("Method \"" + key + "\" has already been defined as a data property."), - vm - ); - } - } - if (props && hasOwn(props, key)) { - process.env.NODE_ENV !== 'production' && 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 - observe(data, true /* asRootData */); -} - -function getData (data, vm) { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, "data()"); - return {} - } -} - -var computedWatcherOptions = { lazy: true }; - -function initComputed (vm, computed) { - var watchers = vm._computedWatchers = Object.create(null); - // computed properties are just getters during SSR - var isSSR = isServerRendering(); - - for (var key in computed) { - var userDef = computed[key]; - var getter = typeof userDef === 'function' ? userDef : userDef.get; - if (process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production') { - 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); - } - } - } -} - -function defineComputed ( - target, - key, - userDef -) { - var shouldCache = !isServerRendering(); - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef; - sharedPropertyDefinition.set = noop; - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop; - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop; - } - if (process.env.NODE_ENV !== 'production' && - 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 () { - var watcher = this._computedWatchers && this._computedWatchers[key]; - if (watcher) { - if (watcher.dirty) { - watcher.evaluate(); - } - if (Dep.target) { - watcher.depend(); - } - return watcher.value - } - } -} - -function initMethods (vm, methods) { - var props = vm.$options.props; - for (var key in methods) { - if (process.env.NODE_ENV !== 'production') { - if (methods[key] == null) { - warn( - "Method \"" + key + "\" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm); - } -} - -function initWatch (vm, watch) { - for (var key in watch) { - var handler = watch[key]; - if (Array.isArray(handler)) { - for (var i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]); - } - } else { - createWatcher(vm, key, handler); - } - } -} - -function createWatcher ( - vm, - keyOrFn, - handler, - options -) { - if (isPlainObject(handler)) { - options = handler; - handler = handler.handler; - } - if (typeof handler === 'string') { - handler = vm[handler]; - } - return vm.$watch(keyOrFn, handler, options) -} - -function stateMixin (Vue) { - // flow somehow has problems with directly declared definition object - // when using Object.defineProperty, so we have to procedurally build up - // the object here. - var dataDef = {}; - dataDef.get = function () { return this._data }; - var propsDef = {}; - propsDef.get = function () { return this._props }; - if (process.env.NODE_ENV !== 'production') { - dataDef.set = function (newData) { - 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, - cb, - options - ) { - var vm = this; - if (isPlainObject(cb)) { - return createWatcher(vm, expOrFn, cb, options) - } - options = options || {}; - options.user = true; - var watcher = new Watcher(vm, expOrFn, cb, options); - if (options.immediate) { - cb.call(vm, watcher.value); - } - return function unwatchFn () { - watcher.teardown(); - } - }; -} - -/* */ - -function initProvide (vm) { - var provide = vm.$options.provide; - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide; - } -} - -function initInjections (vm) { - var result = resolveInject(vm.$options.inject, vm); - if (result) { - observerState.shouldConvert = false; - Object.keys(result).forEach(function (key) { - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, key, result[key], function () { - 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]); - } - }); - observerState.shouldConvert = true; - } -} - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else if (process.env.NODE_ENV !== 'production') { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - if (process.env.NODE_ENV !== 'production') { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -function initRender (vm) { - vm._vnode = null; // the root of the child tree - var options = vm.$options; - var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree - var renderContext = parentVnode && parentVnode.context; - vm.$slots = resolveSlots(options._renderChildren, renderContext); - vm.$scopedSlots = 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 - vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; - // normalization is always applied for the public version, used in - // user-written render functions. - vm.$createElement = function (a, b, c, d) { return 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 - var parentData = parentVnode && parentVnode.data; - - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () { - !isUpdatingChildComponent && warn("$attrs is readonly.", vm); - }, true); - defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () { - !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); - } -} - -function renderMixin (Vue) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype); - - Vue.prototype.$nextTick = function (fn) { - return nextTick(fn, this) - }; - - Vue.prototype._render = function () { - var vm = this; - var ref = vm.$options; - var render = ref.render; - var _parentVnode = ref._parentVnode; - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (var key in vm.$slots) { - var slot = vm.$slots[key]; - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */); - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode; - // render self - var vnode; - try { - vnode = render.call(vm._renderProxy, vm.$createElement); - } catch (e) { - handleError(e, vm, "render"); - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); - } catch (e) { - handleError(e, vm, "renderError"); - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if (process.env.NODE_ENV !== 'production' && Array.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 - }; -} - -/* */ - -var uid = 0; - -function initMixin (Vue) { - Vue.prototype._init = function (options) { - var vm = this; - // a uid - vm._uid = uid++; - - var startTag, endTag; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - startTag = "vue-perf-start:" + (vm._uid); - endTag = "vue-perf-end:" + (vm._uid); - mark(startTag); - } - - // a flag to avoid this being observed - vm._isVue = 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); - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ); - } - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - initProxy(vm); - } else { - vm._renderProxy = vm; - } - // expose real self - vm._self = vm; - initLifecycle(vm); - initEvents(vm); - initRender(vm); - callHook(vm, 'beforeCreate'); - initInjections(vm); // resolve injections before data/props - initState(vm); - initProvide(vm); // resolve provide after data/props - callHook(vm, 'created'); - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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); - } - }; -} - -function initInternalComponent (vm, options) { - var opts = vm.$options = Object.create(vm.constructor.options); - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent; - opts.propsData = options.propsData; - opts._parentVnode = options._parentVnode; - opts._parentListeners = options._parentListeners; - opts._renderChildren = options._renderChildren; - opts._componentTag = options._componentTag; - opts._parentElm = options._parentElm; - opts._refElm = options._refElm; - if (options.render) { - opts.render = options.render; - opts.staticRenderFns = options.staticRenderFns; - } -} - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -function Vue$3 (options) { - if (process.env.NODE_ENV !== 'production' && - !(this instanceof Vue$3) - ) { - warn('Vue is a constructor and should be called with the `new` keyword'); - } - this._init(options); -} - -initMixin(Vue$3); -stateMixin(Vue$3); -eventsMixin(Vue$3); -lifecycleMixin(Vue$3); -renderMixin(Vue$3); - -/* */ - -function initUse (Vue) { - Vue.use = function (plugin) { - var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - var args = toArray(arguments, 1); - args.unshift(this); - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args); - } else if (typeof plugin === 'function') { - plugin.apply(null, args); - } - installedPlugins.push(plugin); - return this - }; -} - -/* */ - -function initMixin$1 (Vue) { - Vue.mixin = function (mixin) { - this.options = mergeOptions(this.options, mixin); - return this - }; -} - -/* */ - -function initExtend (Vue) { - /** - * 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 - */ - Vue.extend = function (extendOptions) { - extendOptions = extendOptions || {}; - var Super = this; - var SuperId = Super.cid; - var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - 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 characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - } - - var Sub = function VueComponent (options) { - this._init(options); - }; - 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$1(Sub); - } - if (Sub.options.computed) { - initComputed$1(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$1 (Comp) { - var props = Comp.options.props; - for (var key in props) { - proxy(Comp.prototype, "_props", key); - } -} - -function initComputed$1 (Comp) { - var computed = Comp.options.computed; - for (var key in computed) { - defineComputed(Comp.prototype, key, computed[key]); - } -} - -/* */ - -function initAssetRegisters (Vue) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.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' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ); - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id; - definition = this.options._base.extend(definition); - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition }; - } - this.options[type + 's'][id] = definition; - return definition - } - }; - }); -} - -/* */ - -function getComponentName (opts) { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern, name) { - if (Array.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, filter) { - var cache = keepAliveInstance.cache; - var keys = keepAliveInstance.keys; - var _vnode = keepAliveInstance._vnode; - for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode); - } - } - } -} - -function pruneCacheEntry ( - cache, - key, - keys, - current -) { - var cached$$1 = cache[key]; - if (cached$$1 && cached$$1 !== current) { - cached$$1.componentInstance.$destroy(); - } - cache[key] = null; - remove(keys, key); -} - -var patternTypes = [String, RegExp, Array]; - -var KeepAlive = { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created: function created () { - this.cache = Object.create(null); - this.keys = []; - }, - - destroyed: function destroyed () { - var this$1 = this; - - for (var key in this$1.cache) { - pruneCacheEntry(this$1.cache, key, this$1.keys); - } - }, - - watch: { - include: function include (val) { - pruneCache(this, function (name) { return matches(val, name); }); - }, - exclude: function exclude (val) { - pruneCache(this, function (name) { return !matches(val, name); }); - } - }, - - render: function render () { - var vnode = getFirstComponentChild(this.$slots.default); - var componentOptions = vnode && vnode.componentOptions; - if (componentOptions) { - // check pattern - var name = getComponentName(componentOptions); - if (name && ( - (this.exclude && matches(this.exclude, name)) || - (this.include && !matches(this.include, name)) - )) { - return vnode - } - - var ref = this; - var cache = ref.cache; - var keys = ref.keys; - var 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 { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } - } - - vnode.data.keepAlive = true; - } - return vnode - } -}; - -var builtInComponents = { - KeepAlive: KeepAlive -}; - -/* */ - -function initGlobalAPI (Vue) { - // config - var configDef = {}; - configDef.get = function () { return config; }; - if (process.env.NODE_ENV !== 'production') { - configDef.set = function () { - 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: warn, - extend: extend, - mergeOptions: mergeOptions, - defineReactive: defineReactive - }; - - Vue.set = set; - Vue.delete = del; - Vue.nextTick = nextTick; - - Vue.options = Object.create(null); - ASSET_TYPES.forEach(function (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$1(Vue); - initExtend(Vue); - initAssetRegisters(Vue); -} - -initGlobalAPI(Vue$3); - -Object.defineProperty(Vue$3.prototype, '$isServer', { - get: isServerRendering -}); - -Object.defineProperty(Vue$3.prototype, '$ssrContext', { - get: function get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}); - -Vue$3.version = '2.5.3'; - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -var xlinkNS = 'http://www.w3.org/1999/xlink'; - -var isXlink = function (name) { - return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' -}; - -var getXlinkProp = function (name) { - return isXlink(name) ? name.slice(6, name.length) : '' -}; - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - -var namespaceMap = { - svg: 'http://www.w3.org/2000/svg', - math: 'http://www.w3.org/1998/Math/MathML' -}; - -var 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. -var 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 -); - - - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - -var unknownElementCache = Object.create(null); -function isUnknownElement (tag) { - /* 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] - } - var el = document.createElement(tag); - if (tag.indexOf('-') > -1) { - // http://stackoverflow.com/a/28210364/1070244 - return (unknownElementCache[tag] = ( - el.constructor === window.HTMLUnknownElement || - el.constructor === window.HTMLElement - )) - } else { - return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString())) - } -} - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ -function query (el) { - if (typeof el === 'string') { - var selected = document.querySelector(el); - if (!selected) { - process.env.NODE_ENV !== 'production' && warn( - 'Cannot find element: ' + el - ); - return document.createElement('div') - } - return selected - } else { - return el - } -} - -/* */ - -function createElement$1 (tagName, vnode) { - var 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 -} - -function createElementNS (namespace, tagName) { - return document.createElementNS(namespaceMap[namespace], tagName) -} - -function createTextNode (text) { - return document.createTextNode(text) -} - -function createComment (text) { - return document.createComment(text) -} - -function insertBefore (parentNode, newNode, referenceNode) { - parentNode.insertBefore(newNode, referenceNode); -} - -function removeChild (node, child) { - node.removeChild(child); -} - -function appendChild (node, child) { - node.appendChild(child); -} - -function parentNode (node) { - return node.parentNode -} - -function nextSibling (node) { - return node.nextSibling -} - -function tagName (node) { - return node.tagName -} - -function setTextContent (node, text) { - node.textContent = text; -} - -function setAttribute (node, key, val) { - node.setAttribute(key, val); -} - - -var nodeOps = Object.freeze({ - createElement: createElement$1, - createElementNS: createElementNS, - createTextNode: createTextNode, - createComment: createComment, - insertBefore: insertBefore, - removeChild: removeChild, - appendChild: appendChild, - parentNode: parentNode, - nextSibling: nextSibling, - tagName: tagName, - setTextContent: setTextContent, - setAttribute: setAttribute -}); - -/* */ - -var ref = { - create: function create (_, vnode) { - registerRef(vnode); - }, - update: function update (oldVnode, vnode) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true); - registerRef(vnode); - } - }, - destroy: function destroy (vnode) { - registerRef(vnode, true); - } -}; - -function registerRef (vnode, isRemoval) { - var key = vnode.data.ref; - if (!key) { return } - - var vm = vnode.context; - var ref = vnode.componentInstance || vnode.elm; - var refs = vm.$refs; - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref); - } else if (refs[key] === ref) { - refs[key] = undefined; - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref]; - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref); - } - } else { - refs[key] = ref; - } - } -} - -/** - * 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. - */ - -var emptyNode = new VNode('', {}, []); - -var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; - -function sameVnode (a, b) { - return ( - a.key === b.key && ( - ( - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - isUndef(b.asyncFactory.error) - ) - ) - ) -} - -function sameInputType (a, b) { - if (a.tag !== 'input') { return true } - var i; - var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type; - var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type; - return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) -} - -function createKeyToOldIdx (children, beginIdx, endIdx) { - var i, key; - var map = {}; - for (i = beginIdx; i <= endIdx; ++i) { - key = children[i].key; - if (isDef(key)) { map[key] = i; } - } - return map -} - -function createPatchFunction (backend) { - var i, j; - var cbs = {}; - - var modules = backend.modules; - var nodeOps = backend.nodeOps; - - 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) { - var parent = nodeOps.parentNode(el); - // element may have already been removed due to v-html / v-text - if (isDef(parent)) { - nodeOps.removeChild(parent, el); - } - } - - var inPre = 0; - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - vnode.isRootInsert = !nested; // for transition enter check - if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { - return - } - - var data = vnode.data; - var children = vnode.children; - var tag = vnode.tag; - if (isDef(tag)) { - if (process.env.NODE_ENV !== 'production') { - if (data && data.pre) { - inPre++; - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(function (ignore) { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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); - - /* istanbul ignore if */ - { - createChildren(vnode, children, insertedVnodeQueue); - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - - if (process.env.NODE_ENV !== 'production' && data && data.pre) { - inPre--; - } - } 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) { - var i = vnode.data; - if (isDef(i)) { - var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; - if (isDef(i = i.hook) && isDef(i = i.init)) { - i(vnode, false /* hydrating */, parentElm, refElm); - } - // 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); - 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) { - var 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. - var 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$$1) { - if (isDef(parent)) { - if (isDef(ref$$1)) { - if (ref$$1.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref$$1); - } - } else { - nodeOps.appendChild(parent, elm); - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); - } - } - - function isPatchable (vnode) { - while (vnode.componentInstance) { - vnode = vnode.componentInstance._vnode; - } - return isDef(vnode.tag) - } - - function invokeCreateHooks (vnode, insertedVnodeQueue) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](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) { - var i; - if (isDef(i = vnode.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, ''); - } else { - var ancestor = vnode; - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, ''); - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm); - } - } - - function invokeDestroyHook (vnode) { - var i, j; - var 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 (parentElm, vnodes, startIdx, endIdx) { - for (; startIdx <= endIdx; ++startIdx) { - var ch = vnodes[startIdx]; - if (isDef(ch)) { - if (isDef(ch.tag)) { - removeAndInvokeRemoveHook(ch); - invokeDestroyHook(ch); - } else { // Text node - removeNode(ch.elm); - } - } - } - } - - function removeAndInvokeRemoveHook (vnode, rm) { - if (isDef(rm) || isDef(vnode.data)) { - var i; - var 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) { - var oldStartIdx = 0; - var newStartIdx = 0; - var oldEndIdx = oldCh.length - 1; - var oldStartVnode = oldCh[0]; - var oldEndVnode = oldCh[oldEndIdx]; - var newEndIdx = newCh.length - 1; - var newStartVnode = newCh[0]; - var newEndVnode = newCh[newEndIdx]; - var 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 - var canMove = !removeOnly; - - 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); - oldStartVnode = oldCh[++oldStartIdx]; - newStartVnode = newCh[++newStartIdx]; - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); - oldEndVnode = oldCh[--oldEndIdx]; - newEndVnode = newCh[--newEndIdx]; - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); - 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); - 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); - } else { - vnodeToMove = oldCh[idxInOld]; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ); - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue); - 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); - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx); - } - } - - function findIdxInOld (node, oldCh, start, end) { - for (var i = start; i < end; i++) { - var c = oldCh[i]; - if (isDef(c) && sameVnode(node, c)) { return i } - } - } - - function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { - if (oldVnode === vnode) { - return - } - - var 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 - } - - var i; - var data = vnode.data; - if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { - i(oldVnode, vnode); - } - - var oldCh = oldVnode.children; - var 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 (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 (var i = 0; i < queue.length; ++i) { - queue[i].data.hook.insert(queue[i]); - } - } - } - - var bailed = 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 - var isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key'); - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm; - vnode.isAsyncPlaceholder = true; - return true - } - if (process.env.NODE_ENV !== 'production') { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm; - var tag = vnode.tag; - var data = vnode.data; - var children = vnode.children; - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('server innerHTML: ', i); - console.warn('client innerHTML: ', elm.innerHTML); - } - return false - } - } else { - // iterate and compare children lists - var childrenMatch = true; - var childNode = elm.firstChild; - for (var i$1 = 0; i$1 < children.length; i$1++) { - if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue)) { - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); - } - return false - } - } - } - } - if (isDef(data)) { - for (var key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text; - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } - return - } - - var isInitialPatch = false; - var insertedVnodeQueue = []; - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true; - createElm(vnode, insertedVnodeQueue, parentElm, refElm); - } else { - var isRealElement = isDef(oldVnode.nodeType); - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 (process.env.NODE_ENV !== 'production') { - 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 - var oldElm = oldVnode.elm; - var parentElm$1 = 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$1, - nodeOps.nextSibling(oldElm) - ); - - // update parent placeholder node element, recursively - if (isDef(vnode.parent)) { - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - // destroy old node - if (isDef(parentElm$1)) { - removeVnodes(parentElm$1, [oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } -} - -/* */ - -var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } -}; - -function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } -} - -function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode, 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode, 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } -} - -var emptyModifiers = Object.create(null); - -function normalizeDirectives$1 ( - dirs, - vm -) { - var res = Object.create(null); - if (!dirs) { - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - return res -} - -function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) -} - -function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } -} - -var baseModules = [ - ref, - directives -]; - -/* */ - -function updateAttrs (oldVnode, vnode) { - var opts = vnode.componentOptions; - if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { - return - } - if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(attrs.__ob__)) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - setAttr(elm, key, cur); - } - } - // #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 ((isIE9 || 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, key, value) { - 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, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'); - } else if (isXlink(key)) { - if (isFalsyAttrValue(value)) { - el.removeAttributeNS(xlinkNS, getXlinkProp(key)); - } else { - el.setAttributeNS(xlinkNS, key, value); - } - } else { - if (isFalsyAttrValue(value)) { - el.removeAttribute(key); - } else { - el.setAttribute(key, value); - } - } -} - -var attrs = { - create: updateAttrs, - update: updateAttrs -}; - -/* */ - -function updateClass (oldVnode, vnode) { - var el = vnode.elm; - var data = vnode.data; - var oldData = oldVnode.data; - if ( - isUndef(data.staticClass) && - isUndef(data.class) && ( - isUndef(oldData) || ( - isUndef(oldData.staticClass) && - isUndef(oldData.class) - ) - ) - ) { - return - } - - var cls = genClassForVnode(vnode); - - // handle transition classes - var 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; - } -} - -var klass = { - create: updateClass, - update: updateClass -}; - -/* */ - -/* */ - - - - - - - - - - - - - - - -// 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. - -/* */ - -/** - * Cross-platform code generation for component v-model - */ - - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ - -/* */ - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; -var CHECKBOX_RADIO_TOKEN = '__c'; - -/* */ - -// 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 - var 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]; - } -} - -var target$1; - -function createOnceHandler (handler, event, capture) { - var _target = target$1; // save current target element in closure - return function onceHandler () { - var res = handler.apply(null, arguments); - if (res !== null) { - remove$2(event, onceHandler, capture, _target); - } - } -} - -function add$1 ( - event, - handler, - once$$1, - capture, - passive -) { - handler = withMacroTask(handler); - if (once$$1) { handler = createOnceHandler(handler, event, capture); } - target$1.addEventListener( - event, - handler, - supportsPassive - ? { capture: capture, passive: passive } - : capture - ); -} - -function remove$2 ( - event, - handler, - capture, - _target -) { - (_target || target$1).removeEventListener( - event, - handler._withTask || handler, - capture - ); -} - -function updateDOMListeners (oldVnode, vnode) { - if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { - return - } - var on = vnode.data.on || {}; - var oldOn = oldVnode.data.on || {}; - target$1 = vnode.elm; - normalizeEvents(on); - updateListeners(on, oldOn, add$1, remove$2, vnode.context); - target$1 = undefined; -} - -var events = { - create: updateDOMListeners, - update: updateDOMListeners -}; - -/* */ - -function updateDOMProps (oldVnode, vnode) { - if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { - return - } - var key, cur; - var elm = vnode.elm; - var oldProps = oldVnode.data.domProps || {}; - var props = vnode.data.domProps || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(props.__ob__)) { - props = vnode.data.domProps = extend({}, props); - } - - for (key in oldProps) { - if (isUndef(props[key])) { - 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') { - // 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 - var strCur = isUndef(cur) ? '' : String(cur); - if (shouldUpdateValue(elm, strCur)) { - elm.value = strCur; - } - } else { - elm[key] = cur; - } - } -} - -// check platforms/web/util/attrs.js acceptValue - - -function shouldUpdateValue (elm, checkVal) { - return (!elm.composing && ( - elm.tagName === 'OPTION' || - isDirty(elm, checkVal) || - isInputChanged(elm, checkVal) - )) -} - -function isDirty (elm, checkVal) { - // return true when textbox (.number and .trim) loses focus and its value is - // not equal to the updated value - var notInFocus = true; - // #6157 - // work around IE bug when accessing document.activeElement in an iframe - try { notInFocus = document.activeElement !== elm; } catch (e) {} - return notInFocus && elm.value !== checkVal -} - -function isInputChanged (elm, newVal) { - var value = elm.value; - var modifiers = elm._vModifiers; // injected by v-model runtime - if (isDef(modifiers) && modifiers.number) { - return toNumber(value) !== toNumber(newVal) - } - if (isDef(modifiers) && modifiers.trim) { - return value.trim() !== newVal.trim() - } - return value !== newVal -} - -var domProps = { - create: updateDOMProps, - update: updateDOMProps -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -var cssVarRE = /^--/; -var importantRE = /\s*!important$/; -var setProp = function (el, name, val) { - /* istanbul ignore if */ - if (cssVarRE.test(name)) { - el.style.setProperty(name, val); - } else if (importantRE.test(val)) { - el.style.setProperty(name, val.replace(importantRE, ''), 'important'); - } else { - var 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 (var i = 0, len = val.length; i < len; i++) { - el.style[normalizedName] = val[i]; - } - } else { - el.style[normalizedName] = val; - } - } -}; - -var vendorNames = ['Webkit', 'Moz', 'ms']; - -var emptyStyle; -var normalize = cached(function (prop) { - emptyStyle = emptyStyle || document.createElement('div').style; - prop = camelize(prop); - if (prop !== 'filter' && (prop in emptyStyle)) { - return prop - } - var capName = prop.charAt(0).toUpperCase() + prop.slice(1); - for (var i = 0; i < vendorNames.length; i++) { - var name = vendorNames[i] + capName; - if (name in emptyStyle) { - return name - } - } -}); - -function updateStyle (oldVnode, vnode) { - var data = vnode.data; - var oldData = oldVnode.data; - - if (isUndef(data.staticStyle) && isUndef(data.style) && - isUndef(oldData.staticStyle) && isUndef(oldData.style) - ) { - return - } - - var cur, name; - var el = vnode.elm; - var oldStaticStyle = oldData.staticStyle; - var oldStyleBinding = oldData.normalizedStyle || oldData.style || {}; - - // if static style exists, stylebinding already merged into it when doing normalizeStyleData - var oldStyle = oldStaticStyle || oldStyleBinding; - - var 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; - - var newStyle = getStyle(vnode, true); - - for (name in oldStyle) { - if (isUndef(newStyle[name])) { - setProp(el, name, ''); - } - } - for (name in newStyle) { - cur = newStyle[name]; - if (cur !== oldStyle[name]) { - // ie9 setting to null has no effect, must use empty string - setProp(el, name, cur == null ? '' : cur); - } - } -} - -var style = { - create: updateStyle, - update: updateStyle -}; - -/* */ - -/** - * Add class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ -function addClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.add(c); }); - } else { - el.classList.add(cls); - } - } else { - var 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 - */ -function removeClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.remove(c); }); - } else { - el.classList.remove(cls); - } - if (!el.classList.length) { - el.removeAttribute('class'); - } - } else { - var cur = " " + (el.getAttribute('class') || '') + " "; - var tar = ' ' + cls + ' '; - while (cur.indexOf(tar) >= 0) { - cur = cur.replace(tar, ' '); - } - cur = cur.trim(); - if (cur) { - el.setAttribute('class', cur); - } else { - el.removeAttribute('class'); - } - } -} - -/* */ - -function resolveTransition (def) { - if (!def) { - return - } - /* istanbul ignore else */ - if (typeof def === 'object') { - var res = {}; - if (def.css !== false) { - extend(res, autoCssTransition(def.name || 'v')); - } - extend(res, def); - return res - } else if (typeof def === 'string') { - return autoCssTransition(def) - } -} - -var autoCssTransition = cached(function (name) { - return { - enterClass: (name + "-enter"), - enterToClass: (name + "-enter-to"), - enterActiveClass: (name + "-enter-active"), - leaveClass: (name + "-leave"), - leaveToClass: (name + "-leave-to"), - leaveActiveClass: (name + "-leave-active") - } -}); - -var hasTransition = inBrowser && !isIE9; -var TRANSITION = 'transition'; -var ANIMATION = 'animation'; - -// Transition property/event sniffing -var transitionProp = 'transition'; -var transitionEndEvent = 'transitionend'; -var animationProp = 'animation'; -var 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 -var raf = inBrowser - ? window.requestAnimationFrame - ? window.requestAnimationFrame.bind(window) - : setTimeout - : /* istanbul ignore next */ function (fn) { return fn(); }; - -function nextFrame (fn) { - raf(function () { - raf(fn); - }); -} - -function addTransitionClass (el, cls) { - var transitionClasses = el._transitionClasses || (el._transitionClasses = []); - if (transitionClasses.indexOf(cls) < 0) { - transitionClasses.push(cls); - addClass(el, cls); - } -} - -function removeTransitionClass (el, cls) { - if (el._transitionClasses) { - remove(el._transitionClasses, cls); - } - removeClass(el, cls); -} - -function whenTransitionEnds ( - el, - expectedType, - cb -) { - var ref = getTransitionInfo(el, expectedType); - var type = ref.type; - var timeout = ref.timeout; - var propCount = ref.propCount; - if (!type) { return cb() } - var event = type === TRANSITION ? transitionEndEvent : animationEndEvent; - var ended = 0; - var end = function () { - el.removeEventListener(event, onEnd); - cb(); - }; - var onEnd = function (e) { - if (e.target === el) { - if (++ended >= propCount) { - end(); - } - } - }; - setTimeout(function () { - if (ended < propCount) { - end(); - } - }, timeout + 1); - el.addEventListener(event, onEnd); -} - -var transformRE = /\b(transform|all)(,|$)/; - -function getTransitionInfo (el, expectedType) { - var styles = window.getComputedStyle(el); - var transitionDelays = styles[transitionProp + 'Delay'].split(', '); - var transitionDurations = styles[transitionProp + 'Duration'].split(', '); - var transitionTimeout = getTimeout(transitionDelays, transitionDurations); - var animationDelays = styles[animationProp + 'Delay'].split(', '); - var animationDurations = styles[animationProp + 'Duration'].split(', '); - var animationTimeout = getTimeout(animationDelays, animationDurations); - - var type; - var timeout = 0; - var 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; - } - var hasTransform = - type === TRANSITION && - transformRE.test(styles[transitionProp + 'Property']); - return { - type: type, - timeout: timeout, - propCount: propCount, - hasTransform: hasTransform - } -} - -function getTimeout (delays, durations) { - /* istanbul ignore next */ - while (delays.length < durations.length) { - delays = delays.concat(delays); - } - - return Math.max.apply(null, durations.map(function (d, i) { - return toMs(d) + toMs(delays[i]) - })) -} - -function toMs (s) { - return Number(s.slice(0, -1)) * 1000 -} - -/* */ - -function enter (vnode, toggleDisplay) { - var el = vnode.elm; - - // call leave callback now - if (isDef(el._leaveCb)) { - el._leaveCb.cancelled = true; - el._leaveCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return - } - - /* istanbul ignore if */ - if (isDef(el._enterCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var enterClass = data.enterClass; - var enterToClass = data.enterToClass; - var enterActiveClass = data.enterActiveClass; - var appearClass = data.appearClass; - var appearToClass = data.appearToClass; - var appearActiveClass = data.appearActiveClass; - var beforeEnter = data.beforeEnter; - var enter = data.enter; - var afterEnter = data.afterEnter; - var enterCancelled = data.enterCancelled; - var beforeAppear = data.beforeAppear; - var appear = data.appear; - var afterAppear = data.afterAppear; - var appearCancelled = data.appearCancelled; - var duration = data.duration; - - // 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. - var context = activeInstance; - var transitionNode = activeInstance.$vnode; - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent; - context = transitionNode.context; - } - - var isAppear = !context._isMounted || !vnode.isRootInsert; - - if (isAppear && !appear && appear !== '') { - return - } - - var startClass = isAppear && appearClass - ? appearClass - : enterClass; - var activeClass = isAppear && appearActiveClass - ? appearActiveClass - : enterActiveClass; - var toClass = isAppear && appearToClass - ? appearToClass - : enterToClass; - - var beforeEnterHook = isAppear - ? (beforeAppear || beforeEnter) - : beforeEnter; - var enterHook = isAppear - ? (typeof appear === 'function' ? appear : enter) - : enter; - var afterEnterHook = isAppear - ? (afterAppear || afterEnter) - : afterEnter; - var enterCancelledHook = isAppear - ? (appearCancelled || enterCancelled) - : enterCancelled; - - var explicitEnterDuration = toNumber( - isObject(duration) - ? duration.enter - : duration - ); - - if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) { - checkDuration(explicitEnterDuration, 'enter', vnode); - } - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(enterHook); - - var cb = el._enterCb = once(function () { - if (expectsCSS) { - removeTransitionClass(el, toClass); - removeTransitionClass(el, activeClass); - } - 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', function () { - var parent = el.parentNode; - var 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(function () { - addTransitionClass(el, toClass); - removeTransitionClass(el, startClass); - if (!cb.cancelled && !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(); - } -} - -function leave (vnode, rm) { - var el = vnode.elm; - - // call enter callback now - if (isDef(el._enterCb)) { - el._enterCb.cancelled = true; - el._enterCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return rm() - } - - /* istanbul ignore if */ - if (isDef(el._leaveCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var leaveClass = data.leaveClass; - var leaveToClass = data.leaveToClass; - var leaveActiveClass = data.leaveActiveClass; - var beforeLeave = data.beforeLeave; - var leave = data.leave; - var afterLeave = data.afterLeave; - var leaveCancelled = data.leaveCancelled; - var delayLeave = data.delayLeave; - var duration = data.duration; - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(leave); - - var explicitLeaveDuration = toNumber( - isObject(duration) - ? duration.leave - : duration - ); - - if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) { - checkDuration(explicitLeaveDuration, 'leave', vnode); - } - - var cb = el._leaveCb = once(function () { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null; - } - if (expectsCSS) { - removeTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveActiveClass); - } - 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 - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode; - } - beforeLeave && beforeLeave(el); - if (expectsCSS) { - addTransitionClass(el, leaveClass); - addTransitionClass(el, leaveActiveClass); - nextFrame(function () { - addTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveClass); - if (!cb.cancelled && !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) { - if (isUndef(fn)) { - return false - } - var invokerFns = fn.fns; - if (isDef(invokerFns)) { - // invoker - return getHookArgumentsLength( - Array.isArray(invokerFns) - ? invokerFns[0] - : invokerFns - ) - } else { - return (fn._length || fn.length) > 1 - } -} - -function _enter (_, vnode) { - if (vnode.data.show !== true) { - enter(vnode); - } -} - -var transition = inBrowser ? { - create: _enter, - activate: _enter, - remove: function remove$$1 (vnode, rm) { - /* istanbul ignore else */ - if (vnode.data.show !== true) { - leave(vnode, rm); - } else { - rm(); - } - } -} : {}; - -var platformModules = [ - attrs, - klass, - events, - domProps, - style, - transition -]; - -/* */ - -// the directive module should be applied last, after all -// built-in modules have been applied. -var modules = platformModules.concat(baseModules); - -var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules }); - -/** - * Not type checking this file because flow doesn't like attaching - * properties to Elements. - */ - -/* istanbul ignore if */ -if (isIE9) { - // http://www.matts411.com/post/internet-explorer-9-oninput/ - document.addEventListener('selectionchange', function () { - var el = document.activeElement; - if (el && el.vmodel) { - trigger(el, 'input'); - } - }); -} - -var directive = { - inserted: function inserted (el, binding, vnode, oldVnode) { - if (vnode.tag === 'select') { - // #6903 - if (oldVnode.elm && !oldVnode.elm._vOptions) { - mergeVNodeHook(vnode, 'postpatch', function () { - 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) { - // 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); - if (!isAndroid) { - el.addEventListener('compositionstart', onCompositionStart); - el.addEventListener('compositionend', onCompositionEnd); - } - /* istanbul ignore if */ - if (isIE9) { - el.vmodel = true; - } - } - } - }, - - componentUpdated: function 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. - var prevOptions = el._vOptions; - var curOptions = el._vOptions = [].map.call(el.options, getValue); - if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) { - // trigger change event if - // no matching option found for at least one value - var needReset = el.multiple - ? binding.value.some(function (v) { return 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(function () { - actuallySetSelected(el, binding, vm); - }, 0); - } -} - -function actuallySetSelected (el, binding, vm) { - var value = binding.value; - var isMultiple = el.multiple; - if (isMultiple && !Array.isArray(value)) { - process.env.NODE_ENV !== 'production' && 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 - } - var selected, option; - for (var 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(function (o) { return !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) { - var e = document.createEvent('HTMLEvents'); - e.initEvent(type, true, true); - el.dispatchEvent(e); -} - -/* */ - -// recursively search for possible transition defined inside the component root -function locateNode (vnode) { - return vnode.componentInstance && (!vnode.data || !vnode.data.transition) - ? locateNode(vnode.componentInstance._vnode) - : vnode -} - -var show = { - bind: function bind (el, ref, vnode) { - var value = ref.value; - - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - var originalDisplay = el.__vOriginalDisplay = - el.style.display === 'none' ? '' : el.style.display; - if (value && transition$$1) { - vnode.data.show = true; - enter(vnode, function () { - el.style.display = originalDisplay; - }); - } else { - el.style.display = value ? originalDisplay : 'none'; - } - }, - - update: function update (el, ref, vnode) { - var value = ref.value; - var oldValue = ref.oldValue; - - /* istanbul ignore if */ - if (value === oldValue) { return } - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - if (transition$$1) { - vnode.data.show = true; - if (value) { - enter(vnode, function () { - el.style.display = el.__vOriginalDisplay; - }); - } else { - leave(vnode, function () { - el.style.display = 'none'; - }); - } - } else { - el.style.display = value ? el.__vOriginalDisplay : 'none'; - } - }, - - unbind: function unbind ( - el, - binding, - vnode, - oldVnode, - isDestroy - ) { - if (!isDestroy) { - el.style.display = el.__vOriginalDisplay; - } - } -}; - -var platformDirectives = { - model: directive, - show: show -}; - -/* */ - -// Provides transition support for a single element/component. -// supports transition mode (out-in / in-out) - -var 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) { - var compOptions = vnode && vnode.componentOptions; - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -function extractTransitionData (comp) { - var data = {}; - var options = comp.$options; - // props - for (var key in options.propsData) { - data[key] = comp[key]; - } - // events. - // extract listeners and pass them directly to the transition methods - var listeners = options._parentListeners; - for (var key$1 in listeners) { - data[camelize(key$1)] = listeners[key$1]; - } - return data -} - -function placeholder (h, rawChild) { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode) { - while ((vnode = vnode.parent)) { - if (vnode.data.transition) { - return true - } - } -} - -function isSameChild (child, oldChild) { - return oldChild.key === child.key && oldChild.tag === child.tag -} - -var Transition = { - name: 'transition', - props: transitionProps, - abstract: true, - - render: function render (h) { - var this$1 = this; - - var children = this.$options._renderChildren; - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if (process.env.NODE_ENV !== 'production' && children.length > 1) { - warn( - '<transition> can only be used on a single element. Use ' + - '<transition-group> for lists.', - this.$parent - ); - } - - var mode = this.mode; - - // warn invalid mode - if (process.env.NODE_ENV !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in' - ) { - warn( - 'invalid <transition> mode: ' + mode, - this.$parent - ); - } - - var rawChild = 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 - var 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. - var id = "__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; - - var data = (child.data || (child.data = {})).transition = extractTransitionData(this); - var oldRawChild = this._vnode; - var 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(function (d) { return d.name === 'show'; })) { - child.data.show = true; - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // replace old child transition data with fresh one - // important for dynamic transitions! - var oldData = 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', function () { - this$1._leaving = false; - this$1.$forceUpdate(); - }); - return placeholder(h, rawChild) - } else if (mode === 'in-out') { - if (isAsyncPlaceholder(child)) { - return oldRawChild - } - var delayedLeave; - var performLeave = function () { delayedLeave(); }; - mergeVNodeHook(data, 'afterEnter', performLeave); - mergeVNodeHook(data, 'enterCancelled', performLeave); - mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; }); - } - } - - return rawChild - } -}; - -/* */ - -// 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. - -var props = extend({ - tag: String, - moveClass: String -}, transitionProps); - -delete props.mode; - -var TransitionGroup = { - props: props, - - render: function render (h) { - var tag = this.tag || this.$vnode.data.tag || 'span'; - var map = Object.create(null); - var prevChildren = this.prevChildren = this.children; - var rawChildren = this.$slots.default || []; - var children = this.children = []; - var transitionData = extractTransitionData(this); - - for (var i = 0; i < rawChildren.length; i++) { - var c = 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 (process.env.NODE_ENV !== 'production') { - var opts = c.componentOptions; - var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag; - warn(("<transition-group> children must be keyed: <" + name + ">")); - } - } - } - - if (prevChildren) { - var kept = []; - var removed = []; - for (var i$1 = 0; i$1 < prevChildren.length; i$1++) { - var c$1 = prevChildren[i$1]; - c$1.data.transition = transitionData; - c$1.data.pos = c$1.elm.getBoundingClientRect(); - if (map[c$1.key]) { - kept.push(c$1); - } else { - removed.push(c$1); - } - } - this.kept = h(tag, null, kept); - this.removed = removed; - } - - return h(tag, null, children) - }, - - beforeUpdate: function beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ); - this._vnode = this.kept; - }, - - updated: function updated () { - var children = this.prevChildren; - var moveClass = 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(function (c) { - if (c.data.moved) { - var el = c.elm; - var s = el.style; - addTransitionClass(el, moveClass); - s.transform = s.WebkitTransform = s.transitionDuration = ''; - el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { - if (!e || /transform$/.test(e.propertyName)) { - el.removeEventListener(transitionEndEvent, cb); - el._moveCb = null; - removeTransitionClass(el, moveClass); - } - }); - } - }); - }, - - methods: { - hasMove: function hasMove (el, moveClass) { - /* 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. - var clone = el.cloneNode(); - if (el._transitionClasses) { - el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); }); - } - addClass(clone, moveClass); - clone.style.display = 'none'; - this.$el.appendChild(clone); - var info = getTransitionInfo(clone); - this.$el.removeChild(clone); - return (this._hasMove = info.hasTransform) - } - } -}; - -function callPendingCbs (c) { - /* istanbul ignore if */ - if (c.elm._moveCb) { - c.elm._moveCb(); - } - /* istanbul ignore if */ - if (c.elm._enterCb) { - c.elm._enterCb(); - } -} - -function recordPosition (c) { - c.data.newPos = c.elm.getBoundingClientRect(); -} - -function applyTranslation (c) { - var oldPos = c.data.pos; - var newPos = c.data.newPos; - var dx = oldPos.left - newPos.left; - var dy = oldPos.top - newPos.top; - if (dx || dy) { - c.data.moved = true; - var s = c.elm.style; - s.transform = s.WebkitTransform = "translate(" + dx + "px," + dy + "px)"; - s.transitionDuration = '0s'; - } -} - -var platformComponents = { - Transition: Transition, - TransitionGroup: TransitionGroup -}; - -/* */ - -// install platform specific utils -Vue$3.config.mustUseProp = mustUseProp; -Vue$3.config.isReservedTag = isReservedTag; -Vue$3.config.isReservedAttr = isReservedAttr; -Vue$3.config.getTagNamespace = getTagNamespace; -Vue$3.config.isUnknownElement = isUnknownElement; - -// install platform runtime directives & components -extend(Vue$3.options.directives, platformDirectives); -extend(Vue$3.options.components, platformComponents); - -// install platform patch function -Vue$3.prototype.__patch__ = inBrowser ? patch : noop; - -// public mount method -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && inBrowser ? query(el) : undefined; - return mountComponent(this, el, hydrating) -}; - -// devtools global hook -/* istanbul ignore next */ -Vue$3.nextTick(function () { - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue$3); - } else if (process.env.NODE_ENV !== 'production' && isChrome) { - console[console.info ? 'info' : 'log']( - 'Download the Vue Devtools extension for a better development experience:\n' + - 'https://github.com/vuejs/vue-devtools' - ); - } - } - if (process.env.NODE_ENV !== 'production' && - config.productionTip !== false && - inBrowser && typeof console !== 'undefined' - ) { - 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$3; diff --git a/dist/vue.runtime.js b/dist/vue.runtime.js deleted file mode 100644 index 10f03be4bb1..00000000000 --- a/dist/vue.runtime.js +++ /dev/null @@ -1,7785 +0,0 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 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'; - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real 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. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ - - -/** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: "development" !== 'production', - - /** - * Whether to enable devtools - */ - devtools: "development" !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -{ - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid$1 = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid$1++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if ("development" !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - 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) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -{ - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - "development" !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && "development" !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if ("development" !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if ("development" !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if ("development" !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -{ - var 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,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -var mark; -var measure; - -{ - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - "development" !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var 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; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - "development" !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - "timeout (" + (res.timeout) + "ms)" - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - -function getFirstComponentChild (children) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var c = children[i]; - if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { - return c - } - } - } -} - -/* */ - -/* */ - -function initEvents (vm) { - vm._events = Object.create(null); - vm._hasHookEvent = false; - // init parent attached events - var listeners = vm.$options._parentListeners; - if (listeners) { - updateComponentListeners(vm, listeners); - } -} - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -function eventsMixin (Vue) { - var hookRE = /^hook:/; - Vue.prototype.$on = function (event, fn) { - var this$1 = this; - - var vm = this; - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$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, fn) { - var vm = 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, fn) { - var this$1 = this; - - var vm = this; - // all - if (!arguments.length) { - vm._events = Object.create(null); - return vm - } - // array of events - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$off(event[i], fn); - } - return vm - } - // specific event - var cbs = vm._events[event]; - if (!cbs) { - return vm - } - if (!fn) { - vm._events[event] = null; - return vm - } - if (fn) { - // specific handler - var cb; - var i$1 = cbs.length; - while (i$1--) { - cb = cbs[i$1]; - if (cb === fn || cb.fn === fn) { - cbs.splice(i$1, 1); - break - } - } - } - return vm - }; - - Vue.prototype.$emit = function (event) { - var vm = this; - { - var 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 + "\"." - ); - } - } - var cbs = vm._events[event]; - if (cbs) { - cbs = cbs.length > 1 ? toArray(cbs) : cbs; - var args = toArray(arguments, 1); - for (var i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args); - } catch (e) { - handleError(e, vm, ("event handler for \"" + event + "\"")); - } - } - } - return vm - }; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; -var isUpdatingChildComponent = false; - -function initLifecycle (vm) { - var options = vm.$options; - - // locate first non-abstract parent - var 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._watcher = null; - vm._inactive = null; - vm._directInactive = false; - vm._isMounted = false; - vm._isDestroyed = false; - vm._isBeingDestroyed = false; -} - -function lifecycleMixin (Vue) { - Vue.prototype._update = function (vnode, hydrating) { - var vm = this; - if (vm._isMounted) { - callHook(vm, 'beforeUpdate'); - } - var prevEl = vm.$el; - var prevVnode = vm._vnode; - var prevActiveInstance = activeInstance; - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ); - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null; - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode); - } - activeInstance = prevActiveInstance; - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el; - } - // updated hook is called by the scheduler to ensure that children are - // updated in a parent's updated hook. - }; - - Vue.prototype.$forceUpdate = function () { - var vm = this; - if (vm._watcher) { - vm._watcher.update(); - } - }; - - Vue.prototype.$destroy = function () { - var vm = this; - if (vm._isBeingDestroyed) { - return - } - callHook(vm, 'beforeDestroy'); - vm._isBeingDestroyed = true; - // remove self from parent - var parent = vm.$parent; - if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { - remove(parent.$children, vm); - } - // teardown watchers - if (vm._watcher) { - vm._watcher.teardown(); - } - var i = vm._watchers.length; - while (i--) { - vm._watchers[i].teardown(); - } - // 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; - } - }; -} - -function mountComponent ( - vm, - el, - hydrating -) { - vm.$el = el; - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode; - { - /* 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'); - - var updateComponent; - /* istanbul ignore if */ - if ("development" !== 'production' && config.performance && mark) { - updateComponent = function () { - var name = vm._name; - var id = vm._uid; - var startTag = "vue-perf-start:" + id; - var endTag = "vue-perf-end:" + id; - - mark(startTag); - var 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 = function () { - vm._update(vm._render(), hydrating); - }; - } - - vm._watcher = new Watcher(vm, updateComponent, noop); - hydrating = false; - - // 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 -} - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - { - isUpdatingChildComponent = true; - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - { - isUpdatingChildComponent = false; - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index = queue.length = activatedChildren.length = 0; - has = {}; - { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // 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]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var 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; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = expOrFn.toString(); - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - "development" !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var sharedPropertyDefinition = { - enumerable: true, - configurable: true, - get: noop, - set: noop -}; - -function proxy (target, sourceKey, key) { - sharedPropertyDefinition.get = function proxyGetter () { - return this[sourceKey][key] - }; - sharedPropertyDefinition.set = function proxySetter (val) { - this[sourceKey][key] = val; - }; - Object.defineProperty(target, key, sharedPropertyDefinition); -} - -function initState (vm) { - vm._watchers = []; - var opts = vm.$options; - if (opts.props) { initProps(vm, opts.props); } - if (opts.methods) { initMethods(vm, opts.methods); } - if (opts.data) { - initData(vm); - } else { - observe(vm._data = {}, true /* asRootData */); - } - if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch && opts.watch !== nativeWatch) { - initWatch(vm, opts.watch); - } -} - -function initProps (vm, propsOptions) { - var propsData = vm.$options.propsData || {}; - var props = vm._props = {}; - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - var keys = vm.$options._propKeys = []; - var isRoot = !vm.$parent; - // root instance props should be converted - observerState.shouldConvert = isRoot; - var loop = function ( key ) { - keys.push(key); - var value = validateProp(key, propsOptions, propsData, vm); - /* istanbul ignore else */ - { - var 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, function () { - if (vm.$parent && !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 - ); - } - }); - } - // 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); - } - }; - - for (var key in propsOptions) loop( key ); - observerState.shouldConvert = true; -} - -function initData (vm) { - var data = vm.$options.data; - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {}; - if (!isPlainObject(data)) { - data = {}; - "development" !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', - vm - ); - } - // proxy data on instance - var keys = Object.keys(data); - var props = vm.$options.props; - var methods = vm.$options.methods; - var i = keys.length; - while (i--) { - var key = keys[i]; - { - if (methods && hasOwn(methods, key)) { - warn( - ("Method \"" + key + "\" has already been defined as a data property."), - vm - ); - } - } - if (props && hasOwn(props, key)) { - "development" !== 'production' && 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 - observe(data, true /* asRootData */); -} - -function getData (data, vm) { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, "data()"); - return {} - } -} - -var computedWatcherOptions = { lazy: true }; - -function initComputed (vm, computed) { - var watchers = vm._computedWatchers = Object.create(null); - // computed properties are just getters during SSR - var isSSR = isServerRendering(); - - for (var key in computed) { - var userDef = computed[key]; - var getter = typeof userDef === 'function' ? userDef : userDef.get; - if ("development" !== 'production' && 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 (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); - } - } - } -} - -function defineComputed ( - target, - key, - userDef -) { - var shouldCache = !isServerRendering(); - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef; - sharedPropertyDefinition.set = noop; - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop; - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop; - } - if ("development" !== 'production' && - 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 () { - var watcher = this._computedWatchers && this._computedWatchers[key]; - if (watcher) { - if (watcher.dirty) { - watcher.evaluate(); - } - if (Dep.target) { - watcher.depend(); - } - return watcher.value - } - } -} - -function initMethods (vm, methods) { - var props = vm.$options.props; - for (var key in methods) { - { - if (methods[key] == null) { - warn( - "Method \"" + key + "\" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm); - } -} - -function initWatch (vm, watch) { - for (var key in watch) { - var handler = watch[key]; - if (Array.isArray(handler)) { - for (var i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]); - } - } else { - createWatcher(vm, key, handler); - } - } -} - -function createWatcher ( - vm, - keyOrFn, - handler, - options -) { - if (isPlainObject(handler)) { - options = handler; - handler = handler.handler; - } - if (typeof handler === 'string') { - handler = vm[handler]; - } - return vm.$watch(keyOrFn, handler, options) -} - -function stateMixin (Vue) { - // flow somehow has problems with directly declared definition object - // when using Object.defineProperty, so we have to procedurally build up - // the object here. - var dataDef = {}; - dataDef.get = function () { return this._data }; - var propsDef = {}; - propsDef.get = function () { return this._props }; - { - dataDef.set = function (newData) { - 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, - cb, - options - ) { - var vm = this; - if (isPlainObject(cb)) { - return createWatcher(vm, expOrFn, cb, options) - } - options = options || {}; - options.user = true; - var watcher = new Watcher(vm, expOrFn, cb, options); - if (options.immediate) { - cb.call(vm, watcher.value); - } - return function unwatchFn () { - watcher.teardown(); - } - }; -} - -/* */ - -function initProvide (vm) { - var provide = vm.$options.provide; - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide; - } -} - -function initInjections (vm) { - var result = resolveInject(vm.$options.inject, vm); - if (result) { - observerState.shouldConvert = false; - Object.keys(result).forEach(function (key) { - /* istanbul ignore else */ - { - defineReactive(vm, key, result[key], function () { - warn( - "Avoid mutating an injected value directly since the changes will be " + - "overwritten whenever the provided component re-renders. " + - "injection being mutated: \"" + key + "\"", - vm - ); - }); - } - }); - observerState.shouldConvert = true; - } -} - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if ("development" !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if ("development" !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - "development" !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - "development" !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - "development" !== 'production' && 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 ("development" !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -function initRender (vm) { - vm._vnode = null; // the root of the child tree - var options = vm.$options; - var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree - var renderContext = parentVnode && parentVnode.context; - vm.$slots = resolveSlots(options._renderChildren, renderContext); - vm.$scopedSlots = 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 - vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; - // normalization is always applied for the public version, used in - // user-written render functions. - vm.$createElement = function (a, b, c, d) { return 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 - var parentData = parentVnode && parentVnode.data; - - /* istanbul ignore else */ - { - defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () { - !isUpdatingChildComponent && warn("$attrs is readonly.", vm); - }, true); - defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () { - !isUpdatingChildComponent && warn("$listeners is readonly.", vm); - }, true); - } -} - -function renderMixin (Vue) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype); - - Vue.prototype.$nextTick = function (fn) { - return nextTick(fn, this) - }; - - Vue.prototype._render = function () { - var vm = this; - var ref = vm.$options; - var render = ref.render; - var _parentVnode = ref._parentVnode; - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (var key in vm.$slots) { - var slot = vm.$slots[key]; - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */); - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode; - // render self - var vnode; - try { - vnode = render.call(vm._renderProxy, vm.$createElement); - } catch (e) { - handleError(e, vm, "render"); - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); - } catch (e) { - handleError(e, vm, "renderError"); - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if ("development" !== 'production' && Array.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 - }; -} - -/* */ - -var uid = 0; - -function initMixin (Vue) { - Vue.prototype._init = function (options) { - var vm = this; - // a uid - vm._uid = uid++; - - var startTag, endTag; - /* istanbul ignore if */ - if ("development" !== 'production' && config.performance && mark) { - startTag = "vue-perf-start:" + (vm._uid); - endTag = "vue-perf-end:" + (vm._uid); - mark(startTag); - } - - // a flag to avoid this being observed - vm._isVue = 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); - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ); - } - /* istanbul ignore else */ - { - initProxy(vm); - } - // expose real self - vm._self = vm; - initLifecycle(vm); - initEvents(vm); - initRender(vm); - callHook(vm, 'beforeCreate'); - initInjections(vm); // resolve injections before data/props - initState(vm); - initProvide(vm); // resolve provide after data/props - callHook(vm, 'created'); - - /* istanbul ignore if */ - if ("development" !== 'production' && 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); - } - }; -} - -function initInternalComponent (vm, options) { - var opts = vm.$options = Object.create(vm.constructor.options); - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent; - opts.propsData = options.propsData; - opts._parentVnode = options._parentVnode; - opts._parentListeners = options._parentListeners; - opts._renderChildren = options._renderChildren; - opts._componentTag = options._componentTag; - opts._parentElm = options._parentElm; - opts._refElm = options._refElm; - if (options.render) { - opts.render = options.render; - opts.staticRenderFns = options.staticRenderFns; - } -} - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -function Vue$3 (options) { - if ("development" !== 'production' && - !(this instanceof Vue$3) - ) { - warn('Vue is a constructor and should be called with the `new` keyword'); - } - this._init(options); -} - -initMixin(Vue$3); -stateMixin(Vue$3); -eventsMixin(Vue$3); -lifecycleMixin(Vue$3); -renderMixin(Vue$3); - -/* */ - -function initUse (Vue) { - Vue.use = function (plugin) { - var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - var args = toArray(arguments, 1); - args.unshift(this); - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args); - } else if (typeof plugin === 'function') { - plugin.apply(null, args); - } - installedPlugins.push(plugin); - return this - }; -} - -/* */ - -function initMixin$1 (Vue) { - Vue.mixin = function (mixin) { - this.options = mergeOptions(this.options, mixin); - return this - }; -} - -/* */ - -function initExtend (Vue) { - /** - * 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 - */ - Vue.extend = function (extendOptions) { - extendOptions = extendOptions || {}; - var Super = this; - var SuperId = Super.cid; - var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - var name = extendOptions.name || Super.options.name; - { - if (!/^[a-zA-Z][\w-]*$/.test(name)) { - warn( - 'Invalid component name: "' + name + '". Component names ' + - 'can only contain alphanumeric characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - } - - var Sub = function VueComponent (options) { - this._init(options); - }; - 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$1(Sub); - } - if (Sub.options.computed) { - initComputed$1(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$1 (Comp) { - var props = Comp.options.props; - for (var key in props) { - proxy(Comp.prototype, "_props", key); - } -} - -function initComputed$1 (Comp) { - var computed = Comp.options.computed; - for (var key in computed) { - defineComputed(Comp.prototype, key, computed[key]); - } -} - -/* */ - -function initAssetRegisters (Vue) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.forEach(function (type) { - Vue[type] = function ( - id, - definition - ) { - if (!definition) { - return this.options[type + 's'][id] - } else { - /* istanbul ignore if */ - { - if (type === 'component' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ); - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id; - definition = this.options._base.extend(definition); - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition }; - } - this.options[type + 's'][id] = definition; - return definition - } - }; - }); -} - -/* */ - -function getComponentName (opts) { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern, name) { - if (Array.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, filter) { - var cache = keepAliveInstance.cache; - var keys = keepAliveInstance.keys; - var _vnode = keepAliveInstance._vnode; - for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode); - } - } - } -} - -function pruneCacheEntry ( - cache, - key, - keys, - current -) { - var cached$$1 = cache[key]; - if (cached$$1 && cached$$1 !== current) { - cached$$1.componentInstance.$destroy(); - } - cache[key] = null; - remove(keys, key); -} - -var patternTypes = [String, RegExp, Array]; - -var KeepAlive = { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created: function created () { - this.cache = Object.create(null); - this.keys = []; - }, - - destroyed: function destroyed () { - var this$1 = this; - - for (var key in this$1.cache) { - pruneCacheEntry(this$1.cache, key, this$1.keys); - } - }, - - watch: { - include: function include (val) { - pruneCache(this, function (name) { return matches(val, name); }); - }, - exclude: function exclude (val) { - pruneCache(this, function (name) { return !matches(val, name); }); - } - }, - - render: function render () { - var vnode = getFirstComponentChild(this.$slots.default); - var componentOptions = vnode && vnode.componentOptions; - if (componentOptions) { - // check pattern - var name = getComponentName(componentOptions); - if (name && ( - (this.exclude && matches(this.exclude, name)) || - (this.include && !matches(this.include, name)) - )) { - return vnode - } - - var ref = this; - var cache = ref.cache; - var keys = ref.keys; - var 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 { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } - } - - vnode.data.keepAlive = true; - } - return vnode - } -}; - -var builtInComponents = { - KeepAlive: KeepAlive -}; - -/* */ - -function initGlobalAPI (Vue) { - // config - var configDef = {}; - configDef.get = function () { return config; }; - { - configDef.set = function () { - 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: warn, - extend: extend, - mergeOptions: mergeOptions, - defineReactive: defineReactive - }; - - Vue.set = set; - Vue.delete = del; - Vue.nextTick = nextTick; - - Vue.options = Object.create(null); - ASSET_TYPES.forEach(function (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$1(Vue); - initExtend(Vue); - initAssetRegisters(Vue); -} - -initGlobalAPI(Vue$3); - -Object.defineProperty(Vue$3.prototype, '$isServer', { - get: isServerRendering -}); - -Object.defineProperty(Vue$3.prototype, '$ssrContext', { - get: function get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}); - -Vue$3.version = '2.5.3'; - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -var xlinkNS = 'http://www.w3.org/1999/xlink'; - -var isXlink = function (name) { - return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' -}; - -var getXlinkProp = function (name) { - return isXlink(name) ? name.slice(6, name.length) : '' -}; - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - -var namespaceMap = { - svg: 'http://www.w3.org/2000/svg', - math: 'http://www.w3.org/1998/Math/MathML' -}; - -var 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. -var 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 -); - - - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - -var unknownElementCache = Object.create(null); -function isUnknownElement (tag) { - /* 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] - } - var el = document.createElement(tag); - if (tag.indexOf('-') > -1) { - // http://stackoverflow.com/a/28210364/1070244 - return (unknownElementCache[tag] = ( - el.constructor === window.HTMLUnknownElement || - el.constructor === window.HTMLElement - )) - } else { - return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString())) - } -} - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ -function query (el) { - if (typeof el === 'string') { - var selected = document.querySelector(el); - if (!selected) { - "development" !== 'production' && warn( - 'Cannot find element: ' + el - ); - return document.createElement('div') - } - return selected - } else { - return el - } -} - -/* */ - -function createElement$1 (tagName, vnode) { - var 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 -} - -function createElementNS (namespace, tagName) { - return document.createElementNS(namespaceMap[namespace], tagName) -} - -function createTextNode (text) { - return document.createTextNode(text) -} - -function createComment (text) { - return document.createComment(text) -} - -function insertBefore (parentNode, newNode, referenceNode) { - parentNode.insertBefore(newNode, referenceNode); -} - -function removeChild (node, child) { - node.removeChild(child); -} - -function appendChild (node, child) { - node.appendChild(child); -} - -function parentNode (node) { - return node.parentNode -} - -function nextSibling (node) { - return node.nextSibling -} - -function tagName (node) { - return node.tagName -} - -function setTextContent (node, text) { - node.textContent = text; -} - -function setAttribute (node, key, val) { - node.setAttribute(key, val); -} - - -var nodeOps = Object.freeze({ - createElement: createElement$1, - createElementNS: createElementNS, - createTextNode: createTextNode, - createComment: createComment, - insertBefore: insertBefore, - removeChild: removeChild, - appendChild: appendChild, - parentNode: parentNode, - nextSibling: nextSibling, - tagName: tagName, - setTextContent: setTextContent, - setAttribute: setAttribute -}); - -/* */ - -var ref = { - create: function create (_, vnode) { - registerRef(vnode); - }, - update: function update (oldVnode, vnode) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true); - registerRef(vnode); - } - }, - destroy: function destroy (vnode) { - registerRef(vnode, true); - } -}; - -function registerRef (vnode, isRemoval) { - var key = vnode.data.ref; - if (!key) { return } - - var vm = vnode.context; - var ref = vnode.componentInstance || vnode.elm; - var refs = vm.$refs; - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref); - } else if (refs[key] === ref) { - refs[key] = undefined; - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref]; - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref); - } - } else { - refs[key] = ref; - } - } -} - -/** - * 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. - */ - -var emptyNode = new VNode('', {}, []); - -var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; - -function sameVnode (a, b) { - return ( - a.key === b.key && ( - ( - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - isUndef(b.asyncFactory.error) - ) - ) - ) -} - -function sameInputType (a, b) { - if (a.tag !== 'input') { return true } - var i; - var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type; - var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type; - return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) -} - -function createKeyToOldIdx (children, beginIdx, endIdx) { - var i, key; - var map = {}; - for (i = beginIdx; i <= endIdx; ++i) { - key = children[i].key; - if (isDef(key)) { map[key] = i; } - } - return map -} - -function createPatchFunction (backend) { - var i, j; - var cbs = {}; - - var modules = backend.modules; - var nodeOps = backend.nodeOps; - - 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) { - var parent = nodeOps.parentNode(el); - // element may have already been removed due to v-html / v-text - if (isDef(parent)) { - nodeOps.removeChild(parent, el); - } - } - - var inPre = 0; - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - vnode.isRootInsert = !nested; // for transition enter check - if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { - return - } - - var data = vnode.data; - var children = vnode.children; - var tag = vnode.tag; - if (isDef(tag)) { - { - if (data && data.pre) { - inPre++; - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(function (ignore) { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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); - - /* istanbul ignore if */ - { - createChildren(vnode, children, insertedVnodeQueue); - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - - if ("development" !== 'production' && data && data.pre) { - inPre--; - } - } 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) { - var i = vnode.data; - if (isDef(i)) { - var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; - if (isDef(i = i.hook) && isDef(i = i.init)) { - i(vnode, false /* hydrating */, parentElm, refElm); - } - // 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); - 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) { - var 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. - var 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$$1) { - if (isDef(parent)) { - if (isDef(ref$$1)) { - if (ref$$1.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref$$1); - } - } else { - nodeOps.appendChild(parent, elm); - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); - } - } - - function isPatchable (vnode) { - while (vnode.componentInstance) { - vnode = vnode.componentInstance._vnode; - } - return isDef(vnode.tag) - } - - function invokeCreateHooks (vnode, insertedVnodeQueue) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](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) { - var i; - if (isDef(i = vnode.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, ''); - } else { - var ancestor = vnode; - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, ''); - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm); - } - } - - function invokeDestroyHook (vnode) { - var i, j; - var 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 (parentElm, vnodes, startIdx, endIdx) { - for (; startIdx <= endIdx; ++startIdx) { - var ch = vnodes[startIdx]; - if (isDef(ch)) { - if (isDef(ch.tag)) { - removeAndInvokeRemoveHook(ch); - invokeDestroyHook(ch); - } else { // Text node - removeNode(ch.elm); - } - } - } - } - - function removeAndInvokeRemoveHook (vnode, rm) { - if (isDef(rm) || isDef(vnode.data)) { - var i; - var 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) { - var oldStartIdx = 0; - var newStartIdx = 0; - var oldEndIdx = oldCh.length - 1; - var oldStartVnode = oldCh[0]; - var oldEndVnode = oldCh[oldEndIdx]; - var newEndIdx = newCh.length - 1; - var newStartVnode = newCh[0]; - var newEndVnode = newCh[newEndIdx]; - var 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 - var canMove = !removeOnly; - - 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); - oldStartVnode = oldCh[++oldStartIdx]; - newStartVnode = newCh[++newStartIdx]; - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); - oldEndVnode = oldCh[--oldEndIdx]; - newEndVnode = newCh[--newEndIdx]; - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); - 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); - 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); - } else { - vnodeToMove = oldCh[idxInOld]; - /* istanbul ignore if */ - if ("development" !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ); - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue); - 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); - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx); - } - } - - function findIdxInOld (node, oldCh, start, end) { - for (var i = start; i < end; i++) { - var c = oldCh[i]; - if (isDef(c) && sameVnode(node, c)) { return i } - } - } - - function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { - if (oldVnode === vnode) { - return - } - - var 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 - } - - var i; - var data = vnode.data; - if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { - i(oldVnode, vnode); - } - - var oldCh = oldVnode.children; - var 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 (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 (var i = 0; i < queue.length; ++i) { - queue[i].data.hook.insert(queue[i]); - } - } - } - - var bailed = 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 - var isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key'); - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm; - vnode.isAsyncPlaceholder = true; - return true - } - { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm; - var tag = vnode.tag; - var data = vnode.data; - var children = vnode.children; - 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 ("development" !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('server innerHTML: ', i); - console.warn('client innerHTML: ', elm.innerHTML); - } - return false - } - } else { - // iterate and compare children lists - var childrenMatch = true; - var childNode = elm.firstChild; - for (var i$1 = 0; i$1 < children.length; i$1++) { - if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue)) { - 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 ("development" !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); - } - return false - } - } - } - } - if (isDef(data)) { - for (var key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text; - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } - return - } - - var isInitialPatch = false; - var insertedVnodeQueue = []; - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true; - createElm(vnode, insertedVnodeQueue, parentElm, refElm); - } else { - var isRealElement = isDef(oldVnode.nodeType); - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 { - 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 - var oldElm = oldVnode.elm; - var parentElm$1 = 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$1, - nodeOps.nextSibling(oldElm) - ); - - // update parent placeholder node element, recursively - if (isDef(vnode.parent)) { - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - // destroy old node - if (isDef(parentElm$1)) { - removeVnodes(parentElm$1, [oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } -} - -/* */ - -var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } -}; - -function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } -} - -function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode, 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode, 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } -} - -var emptyModifiers = Object.create(null); - -function normalizeDirectives$1 ( - dirs, - vm -) { - var res = Object.create(null); - if (!dirs) { - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - return res -} - -function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) -} - -function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } -} - -var baseModules = [ - ref, - directives -]; - -/* */ - -function updateAttrs (oldVnode, vnode) { - var opts = vnode.componentOptions; - if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { - return - } - if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(attrs.__ob__)) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - setAttr(elm, key, cur); - } - } - // #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 ((isIE9 || 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, key, value) { - 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, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'); - } else if (isXlink(key)) { - if (isFalsyAttrValue(value)) { - el.removeAttributeNS(xlinkNS, getXlinkProp(key)); - } else { - el.setAttributeNS(xlinkNS, key, value); - } - } else { - if (isFalsyAttrValue(value)) { - el.removeAttribute(key); - } else { - el.setAttribute(key, value); - } - } -} - -var attrs = { - create: updateAttrs, - update: updateAttrs -}; - -/* */ - -function updateClass (oldVnode, vnode) { - var el = vnode.elm; - var data = vnode.data; - var oldData = oldVnode.data; - if ( - isUndef(data.staticClass) && - isUndef(data.class) && ( - isUndef(oldData) || ( - isUndef(oldData.staticClass) && - isUndef(oldData.class) - ) - ) - ) { - return - } - - var cls = genClassForVnode(vnode); - - // handle transition classes - var 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; - } -} - -var klass = { - create: updateClass, - update: updateClass -}; - -/* */ - -/* */ - - - - - - - - - - - - - - - -// 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. - -/* */ - -/** - * Cross-platform code generation for component v-model - */ - - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ - -/* */ - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; -var CHECKBOX_RADIO_TOKEN = '__c'; - -/* */ - -// 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 - var 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]; - } -} - -var target$1; - -function createOnceHandler (handler, event, capture) { - var _target = target$1; // save current target element in closure - return function onceHandler () { - var res = handler.apply(null, arguments); - if (res !== null) { - remove$2(event, onceHandler, capture, _target); - } - } -} - -function add$1 ( - event, - handler, - once$$1, - capture, - passive -) { - handler = withMacroTask(handler); - if (once$$1) { handler = createOnceHandler(handler, event, capture); } - target$1.addEventListener( - event, - handler, - supportsPassive - ? { capture: capture, passive: passive } - : capture - ); -} - -function remove$2 ( - event, - handler, - capture, - _target -) { - (_target || target$1).removeEventListener( - event, - handler._withTask || handler, - capture - ); -} - -function updateDOMListeners (oldVnode, vnode) { - if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { - return - } - var on = vnode.data.on || {}; - var oldOn = oldVnode.data.on || {}; - target$1 = vnode.elm; - normalizeEvents(on); - updateListeners(on, oldOn, add$1, remove$2, vnode.context); - target$1 = undefined; -} - -var events = { - create: updateDOMListeners, - update: updateDOMListeners -}; - -/* */ - -function updateDOMProps (oldVnode, vnode) { - if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { - return - } - var key, cur; - var elm = vnode.elm; - var oldProps = oldVnode.data.domProps || {}; - var props = vnode.data.domProps || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(props.__ob__)) { - props = vnode.data.domProps = extend({}, props); - } - - for (key in oldProps) { - if (isUndef(props[key])) { - 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') { - // 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 - var strCur = isUndef(cur) ? '' : String(cur); - if (shouldUpdateValue(elm, strCur)) { - elm.value = strCur; - } - } else { - elm[key] = cur; - } - } -} - -// check platforms/web/util/attrs.js acceptValue - - -function shouldUpdateValue (elm, checkVal) { - return (!elm.composing && ( - elm.tagName === 'OPTION' || - isDirty(elm, checkVal) || - isInputChanged(elm, checkVal) - )) -} - -function isDirty (elm, checkVal) { - // return true when textbox (.number and .trim) loses focus and its value is - // not equal to the updated value - var notInFocus = true; - // #6157 - // work around IE bug when accessing document.activeElement in an iframe - try { notInFocus = document.activeElement !== elm; } catch (e) {} - return notInFocus && elm.value !== checkVal -} - -function isInputChanged (elm, newVal) { - var value = elm.value; - var modifiers = elm._vModifiers; // injected by v-model runtime - if (isDef(modifiers) && modifiers.number) { - return toNumber(value) !== toNumber(newVal) - } - if (isDef(modifiers) && modifiers.trim) { - return value.trim() !== newVal.trim() - } - return value !== newVal -} - -var domProps = { - create: updateDOMProps, - update: updateDOMProps -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -var cssVarRE = /^--/; -var importantRE = /\s*!important$/; -var setProp = function (el, name, val) { - /* istanbul ignore if */ - if (cssVarRE.test(name)) { - el.style.setProperty(name, val); - } else if (importantRE.test(val)) { - el.style.setProperty(name, val.replace(importantRE, ''), 'important'); - } else { - var 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 (var i = 0, len = val.length; i < len; i++) { - el.style[normalizedName] = val[i]; - } - } else { - el.style[normalizedName] = val; - } - } -}; - -var vendorNames = ['Webkit', 'Moz', 'ms']; - -var emptyStyle; -var normalize = cached(function (prop) { - emptyStyle = emptyStyle || document.createElement('div').style; - prop = camelize(prop); - if (prop !== 'filter' && (prop in emptyStyle)) { - return prop - } - var capName = prop.charAt(0).toUpperCase() + prop.slice(1); - for (var i = 0; i < vendorNames.length; i++) { - var name = vendorNames[i] + capName; - if (name in emptyStyle) { - return name - } - } -}); - -function updateStyle (oldVnode, vnode) { - var data = vnode.data; - var oldData = oldVnode.data; - - if (isUndef(data.staticStyle) && isUndef(data.style) && - isUndef(oldData.staticStyle) && isUndef(oldData.style) - ) { - return - } - - var cur, name; - var el = vnode.elm; - var oldStaticStyle = oldData.staticStyle; - var oldStyleBinding = oldData.normalizedStyle || oldData.style || {}; - - // if static style exists, stylebinding already merged into it when doing normalizeStyleData - var oldStyle = oldStaticStyle || oldStyleBinding; - - var 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; - - var newStyle = getStyle(vnode, true); - - for (name in oldStyle) { - if (isUndef(newStyle[name])) { - setProp(el, name, ''); - } - } - for (name in newStyle) { - cur = newStyle[name]; - if (cur !== oldStyle[name]) { - // ie9 setting to null has no effect, must use empty string - setProp(el, name, cur == null ? '' : cur); - } - } -} - -var style = { - create: updateStyle, - update: updateStyle -}; - -/* */ - -/** - * Add class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ -function addClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.add(c); }); - } else { - el.classList.add(cls); - } - } else { - var 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 - */ -function removeClass (el, cls) { - /* istanbul ignore if */ - if (!cls || !(cls = cls.trim())) { - return - } - - /* istanbul ignore else */ - if (el.classList) { - if (cls.indexOf(' ') > -1) { - cls.split(/\s+/).forEach(function (c) { return el.classList.remove(c); }); - } else { - el.classList.remove(cls); - } - if (!el.classList.length) { - el.removeAttribute('class'); - } - } else { - var cur = " " + (el.getAttribute('class') || '') + " "; - var tar = ' ' + cls + ' '; - while (cur.indexOf(tar) >= 0) { - cur = cur.replace(tar, ' '); - } - cur = cur.trim(); - if (cur) { - el.setAttribute('class', cur); - } else { - el.removeAttribute('class'); - } - } -} - -/* */ - -function resolveTransition (def) { - if (!def) { - return - } - /* istanbul ignore else */ - if (typeof def === 'object') { - var res = {}; - if (def.css !== false) { - extend(res, autoCssTransition(def.name || 'v')); - } - extend(res, def); - return res - } else if (typeof def === 'string') { - return autoCssTransition(def) - } -} - -var autoCssTransition = cached(function (name) { - return { - enterClass: (name + "-enter"), - enterToClass: (name + "-enter-to"), - enterActiveClass: (name + "-enter-active"), - leaveClass: (name + "-leave"), - leaveToClass: (name + "-leave-to"), - leaveActiveClass: (name + "-leave-active") - } -}); - -var hasTransition = inBrowser && !isIE9; -var TRANSITION = 'transition'; -var ANIMATION = 'animation'; - -// Transition property/event sniffing -var transitionProp = 'transition'; -var transitionEndEvent = 'transitionend'; -var animationProp = 'animation'; -var 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 -var raf = inBrowser - ? window.requestAnimationFrame - ? window.requestAnimationFrame.bind(window) - : setTimeout - : /* istanbul ignore next */ function (fn) { return fn(); }; - -function nextFrame (fn) { - raf(function () { - raf(fn); - }); -} - -function addTransitionClass (el, cls) { - var transitionClasses = el._transitionClasses || (el._transitionClasses = []); - if (transitionClasses.indexOf(cls) < 0) { - transitionClasses.push(cls); - addClass(el, cls); - } -} - -function removeTransitionClass (el, cls) { - if (el._transitionClasses) { - remove(el._transitionClasses, cls); - } - removeClass(el, cls); -} - -function whenTransitionEnds ( - el, - expectedType, - cb -) { - var ref = getTransitionInfo(el, expectedType); - var type = ref.type; - var timeout = ref.timeout; - var propCount = ref.propCount; - if (!type) { return cb() } - var event = type === TRANSITION ? transitionEndEvent : animationEndEvent; - var ended = 0; - var end = function () { - el.removeEventListener(event, onEnd); - cb(); - }; - var onEnd = function (e) { - if (e.target === el) { - if (++ended >= propCount) { - end(); - } - } - }; - setTimeout(function () { - if (ended < propCount) { - end(); - } - }, timeout + 1); - el.addEventListener(event, onEnd); -} - -var transformRE = /\b(transform|all)(,|$)/; - -function getTransitionInfo (el, expectedType) { - var styles = window.getComputedStyle(el); - var transitionDelays = styles[transitionProp + 'Delay'].split(', '); - var transitionDurations = styles[transitionProp + 'Duration'].split(', '); - var transitionTimeout = getTimeout(transitionDelays, transitionDurations); - var animationDelays = styles[animationProp + 'Delay'].split(', '); - var animationDurations = styles[animationProp + 'Duration'].split(', '); - var animationTimeout = getTimeout(animationDelays, animationDurations); - - var type; - var timeout = 0; - var 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; - } - var hasTransform = - type === TRANSITION && - transformRE.test(styles[transitionProp + 'Property']); - return { - type: type, - timeout: timeout, - propCount: propCount, - hasTransform: hasTransform - } -} - -function getTimeout (delays, durations) { - /* istanbul ignore next */ - while (delays.length < durations.length) { - delays = delays.concat(delays); - } - - return Math.max.apply(null, durations.map(function (d, i) { - return toMs(d) + toMs(delays[i]) - })) -} - -function toMs (s) { - return Number(s.slice(0, -1)) * 1000 -} - -/* */ - -function enter (vnode, toggleDisplay) { - var el = vnode.elm; - - // call leave callback now - if (isDef(el._leaveCb)) { - el._leaveCb.cancelled = true; - el._leaveCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return - } - - /* istanbul ignore if */ - if (isDef(el._enterCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var enterClass = data.enterClass; - var enterToClass = data.enterToClass; - var enterActiveClass = data.enterActiveClass; - var appearClass = data.appearClass; - var appearToClass = data.appearToClass; - var appearActiveClass = data.appearActiveClass; - var beforeEnter = data.beforeEnter; - var enter = data.enter; - var afterEnter = data.afterEnter; - var enterCancelled = data.enterCancelled; - var beforeAppear = data.beforeAppear; - var appear = data.appear; - var afterAppear = data.afterAppear; - var appearCancelled = data.appearCancelled; - var duration = data.duration; - - // 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. - var context = activeInstance; - var transitionNode = activeInstance.$vnode; - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent; - context = transitionNode.context; - } - - var isAppear = !context._isMounted || !vnode.isRootInsert; - - if (isAppear && !appear && appear !== '') { - return - } - - var startClass = isAppear && appearClass - ? appearClass - : enterClass; - var activeClass = isAppear && appearActiveClass - ? appearActiveClass - : enterActiveClass; - var toClass = isAppear && appearToClass - ? appearToClass - : enterToClass; - - var beforeEnterHook = isAppear - ? (beforeAppear || beforeEnter) - : beforeEnter; - var enterHook = isAppear - ? (typeof appear === 'function' ? appear : enter) - : enter; - var afterEnterHook = isAppear - ? (afterAppear || afterEnter) - : afterEnter; - var enterCancelledHook = isAppear - ? (appearCancelled || enterCancelled) - : enterCancelled; - - var explicitEnterDuration = toNumber( - isObject(duration) - ? duration.enter - : duration - ); - - if ("development" !== 'production' && explicitEnterDuration != null) { - checkDuration(explicitEnterDuration, 'enter', vnode); - } - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(enterHook); - - var cb = el._enterCb = once(function () { - if (expectsCSS) { - removeTransitionClass(el, toClass); - removeTransitionClass(el, activeClass); - } - 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', function () { - var parent = el.parentNode; - var 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(function () { - addTransitionClass(el, toClass); - removeTransitionClass(el, startClass); - if (!cb.cancelled && !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(); - } -} - -function leave (vnode, rm) { - var el = vnode.elm; - - // call enter callback now - if (isDef(el._enterCb)) { - el._enterCb.cancelled = true; - el._enterCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (isUndef(data)) { - return rm() - } - - /* istanbul ignore if */ - if (isDef(el._leaveCb) || el.nodeType !== 1) { - return - } - - var css = data.css; - var type = data.type; - var leaveClass = data.leaveClass; - var leaveToClass = data.leaveToClass; - var leaveActiveClass = data.leaveActiveClass; - var beforeLeave = data.beforeLeave; - var leave = data.leave; - var afterLeave = data.afterLeave; - var leaveCancelled = data.leaveCancelled; - var delayLeave = data.delayLeave; - var duration = data.duration; - - var expectsCSS = css !== false && !isIE9; - var userWantsControl = getHookArgumentsLength(leave); - - var explicitLeaveDuration = toNumber( - isObject(duration) - ? duration.leave - : duration - ); - - if ("development" !== 'production' && isDef(explicitLeaveDuration)) { - checkDuration(explicitLeaveDuration, 'leave', vnode); - } - - var cb = el._leaveCb = once(function () { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null; - } - if (expectsCSS) { - removeTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveActiveClass); - } - 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 - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode; - } - beforeLeave && beforeLeave(el); - if (expectsCSS) { - addTransitionClass(el, leaveClass); - addTransitionClass(el, leaveActiveClass); - nextFrame(function () { - addTransitionClass(el, leaveToClass); - removeTransitionClass(el, leaveClass); - if (!cb.cancelled && !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) { - if (isUndef(fn)) { - return false - } - var invokerFns = fn.fns; - if (isDef(invokerFns)) { - // invoker - return getHookArgumentsLength( - Array.isArray(invokerFns) - ? invokerFns[0] - : invokerFns - ) - } else { - return (fn._length || fn.length) > 1 - } -} - -function _enter (_, vnode) { - if (vnode.data.show !== true) { - enter(vnode); - } -} - -var transition = inBrowser ? { - create: _enter, - activate: _enter, - remove: function remove$$1 (vnode, rm) { - /* istanbul ignore else */ - if (vnode.data.show !== true) { - leave(vnode, rm); - } else { - rm(); - } - } -} : {}; - -var platformModules = [ - attrs, - klass, - events, - domProps, - style, - transition -]; - -/* */ - -// the directive module should be applied last, after all -// built-in modules have been applied. -var modules = platformModules.concat(baseModules); - -var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules }); - -/** - * Not type checking this file because flow doesn't like attaching - * properties to Elements. - */ - -/* istanbul ignore if */ -if (isIE9) { - // http://www.matts411.com/post/internet-explorer-9-oninput/ - document.addEventListener('selectionchange', function () { - var el = document.activeElement; - if (el && el.vmodel) { - trigger(el, 'input'); - } - }); -} - -var directive = { - inserted: function inserted (el, binding, vnode, oldVnode) { - if (vnode.tag === 'select') { - // #6903 - if (oldVnode.elm && !oldVnode.elm._vOptions) { - mergeVNodeHook(vnode, 'postpatch', function () { - 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) { - // 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); - if (!isAndroid) { - el.addEventListener('compositionstart', onCompositionStart); - el.addEventListener('compositionend', onCompositionEnd); - } - /* istanbul ignore if */ - if (isIE9) { - el.vmodel = true; - } - } - } - }, - - componentUpdated: function 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. - var prevOptions = el._vOptions; - var curOptions = el._vOptions = [].map.call(el.options, getValue); - if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) { - // trigger change event if - // no matching option found for at least one value - var needReset = el.multiple - ? binding.value.some(function (v) { return 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(function () { - actuallySetSelected(el, binding, vm); - }, 0); - } -} - -function actuallySetSelected (el, binding, vm) { - var value = binding.value; - var isMultiple = el.multiple; - if (isMultiple && !Array.isArray(value)) { - "development" !== 'production' && 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 - } - var selected, option; - for (var 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(function (o) { return !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) { - var e = document.createEvent('HTMLEvents'); - e.initEvent(type, true, true); - el.dispatchEvent(e); -} - -/* */ - -// recursively search for possible transition defined inside the component root -function locateNode (vnode) { - return vnode.componentInstance && (!vnode.data || !vnode.data.transition) - ? locateNode(vnode.componentInstance._vnode) - : vnode -} - -var show = { - bind: function bind (el, ref, vnode) { - var value = ref.value; - - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - var originalDisplay = el.__vOriginalDisplay = - el.style.display === 'none' ? '' : el.style.display; - if (value && transition$$1) { - vnode.data.show = true; - enter(vnode, function () { - el.style.display = originalDisplay; - }); - } else { - el.style.display = value ? originalDisplay : 'none'; - } - }, - - update: function update (el, ref, vnode) { - var value = ref.value; - var oldValue = ref.oldValue; - - /* istanbul ignore if */ - if (value === oldValue) { return } - vnode = locateNode(vnode); - var transition$$1 = vnode.data && vnode.data.transition; - if (transition$$1) { - vnode.data.show = true; - if (value) { - enter(vnode, function () { - el.style.display = el.__vOriginalDisplay; - }); - } else { - leave(vnode, function () { - el.style.display = 'none'; - }); - } - } else { - el.style.display = value ? el.__vOriginalDisplay : 'none'; - } - }, - - unbind: function unbind ( - el, - binding, - vnode, - oldVnode, - isDestroy - ) { - if (!isDestroy) { - el.style.display = el.__vOriginalDisplay; - } - } -}; - -var platformDirectives = { - model: directive, - show: show -}; - -/* */ - -// Provides transition support for a single element/component. -// supports transition mode (out-in / in-out) - -var 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) { - var compOptions = vnode && vnode.componentOptions; - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -function extractTransitionData (comp) { - var data = {}; - var options = comp.$options; - // props - for (var key in options.propsData) { - data[key] = comp[key]; - } - // events. - // extract listeners and pass them directly to the transition methods - var listeners = options._parentListeners; - for (var key$1 in listeners) { - data[camelize(key$1)] = listeners[key$1]; - } - return data -} - -function placeholder (h, rawChild) { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode) { - while ((vnode = vnode.parent)) { - if (vnode.data.transition) { - return true - } - } -} - -function isSameChild (child, oldChild) { - return oldChild.key === child.key && oldChild.tag === child.tag -} - -var Transition = { - name: 'transition', - props: transitionProps, - abstract: true, - - render: function render (h) { - var this$1 = this; - - var children = this.$options._renderChildren; - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if ("development" !== 'production' && children.length > 1) { - warn( - '<transition> can only be used on a single element. Use ' + - '<transition-group> for lists.', - this.$parent - ); - } - - var mode = this.mode; - - // warn invalid mode - if ("development" !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in' - ) { - warn( - 'invalid <transition> mode: ' + mode, - this.$parent - ); - } - - var rawChild = 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 - var 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. - var id = "__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; - - var data = (child.data || (child.data = {})).transition = extractTransitionData(this); - var oldRawChild = this._vnode; - var 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(function (d) { return d.name === 'show'; })) { - child.data.show = true; - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // replace old child transition data with fresh one - // important for dynamic transitions! - var oldData = 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', function () { - this$1._leaving = false; - this$1.$forceUpdate(); - }); - return placeholder(h, rawChild) - } else if (mode === 'in-out') { - if (isAsyncPlaceholder(child)) { - return oldRawChild - } - var delayedLeave; - var performLeave = function () { delayedLeave(); }; - mergeVNodeHook(data, 'afterEnter', performLeave); - mergeVNodeHook(data, 'enterCancelled', performLeave); - mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; }); - } - } - - return rawChild - } -}; - -/* */ - -// 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. - -var props = extend({ - tag: String, - moveClass: String -}, transitionProps); - -delete props.mode; - -var TransitionGroup = { - props: props, - - render: function render (h) { - var tag = this.tag || this.$vnode.data.tag || 'span'; - var map = Object.create(null); - var prevChildren = this.prevChildren = this.children; - var rawChildren = this.$slots.default || []; - var children = this.children = []; - var transitionData = extractTransitionData(this); - - for (var i = 0; i < rawChildren.length; i++) { - var c = 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 { - var opts = c.componentOptions; - var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag; - warn(("<transition-group> children must be keyed: <" + name + ">")); - } - } - } - - if (prevChildren) { - var kept = []; - var removed = []; - for (var i$1 = 0; i$1 < prevChildren.length; i$1++) { - var c$1 = prevChildren[i$1]; - c$1.data.transition = transitionData; - c$1.data.pos = c$1.elm.getBoundingClientRect(); - if (map[c$1.key]) { - kept.push(c$1); - } else { - removed.push(c$1); - } - } - this.kept = h(tag, null, kept); - this.removed = removed; - } - - return h(tag, null, children) - }, - - beforeUpdate: function beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ); - this._vnode = this.kept; - }, - - updated: function updated () { - var children = this.prevChildren; - var moveClass = 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(function (c) { - if (c.data.moved) { - var el = c.elm; - var s = el.style; - addTransitionClass(el, moveClass); - s.transform = s.WebkitTransform = s.transitionDuration = ''; - el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { - if (!e || /transform$/.test(e.propertyName)) { - el.removeEventListener(transitionEndEvent, cb); - el._moveCb = null; - removeTransitionClass(el, moveClass); - } - }); - } - }); - }, - - methods: { - hasMove: function hasMove (el, moveClass) { - /* 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. - var clone = el.cloneNode(); - if (el._transitionClasses) { - el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); }); - } - addClass(clone, moveClass); - clone.style.display = 'none'; - this.$el.appendChild(clone); - var info = getTransitionInfo(clone); - this.$el.removeChild(clone); - return (this._hasMove = info.hasTransform) - } - } -}; - -function callPendingCbs (c) { - /* istanbul ignore if */ - if (c.elm._moveCb) { - c.elm._moveCb(); - } - /* istanbul ignore if */ - if (c.elm._enterCb) { - c.elm._enterCb(); - } -} - -function recordPosition (c) { - c.data.newPos = c.elm.getBoundingClientRect(); -} - -function applyTranslation (c) { - var oldPos = c.data.pos; - var newPos = c.data.newPos; - var dx = oldPos.left - newPos.left; - var dy = oldPos.top - newPos.top; - if (dx || dy) { - c.data.moved = true; - var s = c.elm.style; - s.transform = s.WebkitTransform = "translate(" + dx + "px," + dy + "px)"; - s.transitionDuration = '0s'; - } -} - -var platformComponents = { - Transition: Transition, - TransitionGroup: TransitionGroup -}; - -/* */ - -// install platform specific utils -Vue$3.config.mustUseProp = mustUseProp; -Vue$3.config.isReservedTag = isReservedTag; -Vue$3.config.isReservedAttr = isReservedAttr; -Vue$3.config.getTagNamespace = getTagNamespace; -Vue$3.config.isUnknownElement = isUnknownElement; - -// install platform runtime directives & components -extend(Vue$3.options.directives, platformDirectives); -extend(Vue$3.options.components, platformComponents); - -// install platform patch function -Vue$3.prototype.__patch__ = inBrowser ? patch : noop; - -// public mount method -Vue$3.prototype.$mount = function ( - el, - hydrating -) { - el = el && inBrowser ? query(el) : undefined; - return mountComponent(this, el, hydrating) -}; - -// devtools global hook -/* istanbul ignore next */ -Vue$3.nextTick(function () { - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue$3); - } else if ("development" !== 'production' && isChrome) { - console[console.info ? 'info' : 'log']( - 'Download the Vue Devtools extension for a better development experience:\n' + - 'https://github.com/vuejs/vue-devtools' - ); - } - } - if ("development" !== 'production' && - config.productionTip !== false && - inBrowser && typeof console !== 'undefined' - ) { - 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); - -/* */ - -return Vue$3; - -}))); diff --git a/dist/vue.runtime.min.js b/dist/vue.runtime.min.js deleted file mode 100644 index 3693fa071b3..00000000000 --- a/dist/vue.runtime.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Vue.js v2.5.3 - * (c) 2014-2017 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(t){return void 0===t||null===t}function e(t){return void 0!==t&&null!==t}function n(t){return!0===t}function r(t){return!1===t}function o(t){return"string"==typeof t||"number"==typeof t||"boolean"==typeof t}function i(t){return null!==t&&"object"==typeof t}function a(t){return"[object Object]"===Jn.call(t)}function s(t){return"[object RegExp]"===Jn.call(t)}function c(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function u(t){return null==t?"":"object"==typeof t?JSON.stringify(t,null,2):String(t)}function l(t){var e=parseFloat(t);return isNaN(e)?t:e}function f(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o<r.length;o++)n[r[o]]=!0;return e?function(t){return n[t.toLowerCase()]}:function(t){return n[t]}}function d(t,e){if(t.length){var n=t.indexOf(e);if(n>-1)return t.splice(n,1)}}function p(t,e){return Qn.call(t,e)}function v(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}function h(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function m(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function y(t,e){for(var n in e)t[n]=e[n];return t}function _(t){for(var e={},n=0;n<t.length;n++)t[n]&&y(e,t[n]);return e}function g(t,e,n){}function b(t,e){if(t===e)return!0;var n=i(t),r=i(e);if(!n||!r)return!n&&!r&&String(t)===String(e);try{var o=Array.isArray(t),a=Array.isArray(e);if(o&&a)return t.length===e.length&&t.every(function(t,n){return b(t,e[n])});if(o||a)return!1;var s=Object.keys(t),c=Object.keys(e);return s.length===c.length&&s.every(function(n){return b(t[n],e[n])})}catch(t){return!1}}function C(t,e){for(var n=0;n<t.length;n++)if(b(t[n],e))return n;return-1}function w(t){var e=!1;return function(){e||(e=!0,t.apply(this,arguments))}}function A(t){var e=(t+"").charCodeAt(0);return 36===e||95===e}function $(t,e,n,r){Object.defineProperty(t,e,{value:n,enumerable:!!r,writable:!0,configurable:!0})}function x(t){if(!lr.test(t)){var e=t.split(".");return function(t){for(var n=0;n<e.length;n++){if(!t)return;t=t[e[n]]}return t}}}function k(t){return"function"==typeof t&&/native code/.test(t.toString())}function O(t){Er.target&&jr.push(Er.target),Er.target=t}function S(){Er.target=jr.pop()}function E(t){return new Ir(void 0,void 0,void 0,String(t))}function j(t,e){var n=t.componentOptions,r=new Ir(t.tag,t.data,t.children,t.text,t.elm,t.context,n,t.asyncFactory);return r.ns=t.ns,r.isStatic=t.isStatic,r.key=t.key,r.isComment=t.isComment,r.isCloned=!0,e&&(t.children&&(r.children=I(t.children,!0)),n&&n.children&&(n.children=I(n.children,!0))),r}function I(t,e){for(var n=t.length,r=new Array(n),o=0;o<n;o++)r[o]=j(t[o],e);return r}function T(t,e,n){t.__proto__=e}function D(t,e,n){for(var r=0,o=n.length;r<o;r++){var i=n[r];$(t,i,e[i])}}function L(t,e){if(i(t)&&!(t instanceof Ir)){var n;return p(t,"__ob__")&&t.__ob__ instanceof Ur?n=t.__ob__:Mr.shouldConvert&&!$r()&&(Array.isArray(t)||a(t))&&Object.isExtensible(t)&&!t._isVue&&(n=new Ur(t)),e&&n&&n.vmCount++,n}}function P(t,e,n,r,o){var i=new Er,a=Object.getOwnPropertyDescriptor(t,e);if(!a||!1!==a.configurable){var s=a&&a.get,c=a&&a.set,u=!o&&L(n);Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){var e=s?s.call(t):n;return Er.target&&(i.depend(),u&&(u.dep.depend(),Array.isArray(e)&&U(e))),e},set:function(e){var r=s?s.call(t):n;e===r||e!==e&&r!==r||(c?c.call(t,e):n=e,u=!o&&L(e),i.notify())}})}}function N(t,e,n){if(Array.isArray(t)&&c(e))return t.length=Math.max(t.length,e),t.splice(e,1,n),n;if(e in t&&!(e in Object.prototype))return t[e]=n,n;var r=t.__ob__;return t._isVue||r&&r.vmCount?n:r?(P(r.value,e,n),r.dep.notify(),n):(t[e]=n,n)}function M(t,e){if(Array.isArray(t)&&c(e))t.splice(e,1);else{var n=t.__ob__;t._isVue||n&&n.vmCount||p(t,e)&&(delete t[e],n&&n.dep.notify())}}function U(t){for(var e=void 0,n=0,r=t.length;n<r;n++)(e=t[n])&&e.__ob__&&e.__ob__.dep.depend(),Array.isArray(e)&&U(e)}function F(t,e){if(!e)return t;for(var n,r,o,i=Object.keys(e),s=0;s<i.length;s++)r=t[n=i[s]],o=e[n],p(t,n)?a(r)&&a(o)&&F(r,o):N(t,n,o);return t}function R(t,e,n){return n?function(){var r="function"==typeof e?e.call(n):e,o="function"==typeof t?t.call(n):t;return r?F(r,o):o}:e?t?function(){return F("function"==typeof e?e.call(this):e,"function"==typeof t?t.call(this):t)}:e:t}function V(t,e){return e?t?t.concat(e):Array.isArray(e)?e:[e]:t}function B(t,e,n,r){var o=Object.create(t||null);return e?y(o,e):o}function H(t,e){var n=t.props;if(n){var r,o,i={};if(Array.isArray(n))for(r=n.length;r--;)"string"==typeof(o=n[r])&&(i[Yn(o)]={type:null});else if(a(n))for(var s in n)o=n[s],i[Yn(s)]=a(o)?o:{type:o};t.props=i}}function z(t,e){var n=t.inject,r=t.inject={};if(Array.isArray(n))for(var o=0;o<n.length;o++)r[n[o]]={from:n[o]};else if(a(n))for(var i in n){var s=n[i];r[i]=a(s)?y({from:i},s):{from:s}}}function W(t){var e=t.directives;if(e)for(var n in e){var r=e[n];"function"==typeof r&&(e[n]={bind:r,update:r})}}function q(t,e,n){function r(r){var o=Fr[r]||Br;c[r]=o(t[r],e[r],n,r)}"function"==typeof e&&(e=e.options),H(e,n),z(e,n),W(e);var o=e.extends;if(o&&(t=q(t,o,n)),e.mixins)for(var i=0,a=e.mixins.length;i<a;i++)t=q(t,e.mixins[i],n);var s,c={};for(s in t)r(s);for(s in e)p(t,s)||r(s);return c}function K(t,e,n,r){if("string"==typeof n){var o=t[e];if(p(o,n))return o[n];var i=Yn(n);if(p(o,i))return o[i];var a=tr(i);if(p(o,a))return o[a];var s=o[n]||o[i]||o[a];return s}}function G(t,e,n,r){var o=e[t],i=!p(n,t),a=n[t];if(Q(Boolean,o.type)&&(i&&!p(o,"default")?a=!1:Q(String,o.type)||""!==a&&a!==nr(t)||(a=!0)),void 0===a){a=J(r,o,t);var s=Mr.shouldConvert;Mr.shouldConvert=!0,L(a),Mr.shouldConvert=s}return a}function J(t,e,n){if(p(e,"default")){var r=e.default;return t&&t.$options.propsData&&void 0===t.$options.propsData[n]&&void 0!==t._props[n]?t._props[n]:"function"==typeof r&&"Function"!==Z(e.type)?r.call(t):r}}function Z(t){var e=t&&t.toString().match(/^\s*function (\w+)/);return e?e[1]:""}function Q(t,e){if(!Array.isArray(e))return Z(e)===Z(t);for(var n=0,r=e.length;n<r;n++)if(Z(e[n])===Z(t))return!0;return!1}function X(t,e,n){if(e)for(var r=e;r=r.$parent;){var o=r.$options.errorCaptured;if(o)for(var i=0;i<o.length;i++)try{if(!1===o[i].call(r,t,e,n))return}catch(t){Y(t,r,"errorCaptured hook")}}Y(t,e,n)}function Y(t,e,n){if(cr.errorHandler)try{return cr.errorHandler.call(null,t,e,n)}catch(t){tt(t,null,"config.errorHandler")}tt(t,e,n)}function tt(t,e,n){if(!dr||"undefined"==typeof console)throw t;console.error(t)}function et(){zr=!1;var t=Hr.slice(0);Hr.length=0;for(var e=0;e<t.length;e++)t[e]()}function nt(t){return t._withTask||(t._withTask=function(){Wr=!0;var e=t.apply(null,arguments);return Wr=!1,e})}function rt(t,e){var n;if(Hr.push(function(){if(t)try{t.call(e)}catch(t){X(t,e,"nextTick")}else n&&n(e)}),zr||(zr=!0,Wr?Vr():Rr()),!t&&"undefined"!=typeof Promise)return new Promise(function(t){n=t})}function ot(t){function e(){var t=arguments,n=e.fns;if(!Array.isArray(n))return n.apply(null,arguments);for(var r=n.slice(),o=0;o<r.length;o++)r[o].apply(null,t)}return e.fns=t,e}function it(e,n,r,o,i){var a,s,c,u;for(a in e)s=e[a],c=n[a],u=Zr(a),t(s)||(t(c)?(t(s.fns)&&(s=e[a]=ot(s)),r(u.name,s,u.once,u.capture,u.passive)):s!==c&&(c.fns=s,e[a]=c));for(a in n)t(e[a])&&o((u=Zr(a)).name,n[a],u.capture)}function at(r,o,i){function a(){i.apply(this,arguments),d(s.fns,a)}r instanceof Ir&&(r=r.data.hook||(r.data.hook={}));var s,c=r[o];t(c)?s=ot([a]):e(c.fns)&&n(c.merged)?(s=c).fns.push(a):s=ot([c,a]),s.merged=!0,r[o]=s}function st(n,r,o){var i=r.options.props;if(!t(i)){var a={},s=n.attrs,c=n.props;if(e(s)||e(c))for(var u in i){var l=nr(u);ct(a,c,u,l,!0)||ct(a,s,u,l,!1)}return a}}function ct(t,n,r,o,i){if(e(n)){if(p(n,r))return t[r]=n[r],i||delete n[r],!0;if(p(n,o))return t[r]=n[o],i||delete n[o],!0}return!1}function ut(t){for(var e=0;e<t.length;e++)if(Array.isArray(t[e]))return Array.prototype.concat.apply([],t);return t}function lt(t){return o(t)?[E(t)]:Array.isArray(t)?dt(t):void 0}function ft(t){return e(t)&&e(t.text)&&r(t.isComment)}function dt(r,i){var a,s,c,u,l=[];for(a=0;a<r.length;a++)t(s=r[a])||"boolean"==typeof s||(u=l[c=l.length-1],Array.isArray(s)?s.length>0&&(ft((s=dt(s,(i||"")+"_"+a))[0])&&ft(u)&&(l[c]=E(u.text+s[0].text),s.shift()),l.push.apply(l,s)):o(s)?ft(u)?l[c]=E(u.text+s):""!==s&&l.push(E(s)):ft(s)&&ft(u)?l[c]=E(u.text+s.text):(n(r._isVList)&&e(s.tag)&&t(s.key)&&e(i)&&(s.key="__vlist"+i+"_"+a+"__"),l.push(s)));return l}function pt(t,e){return(t.__esModule||kr&&"Module"===t[Symbol.toStringTag])&&(t=t.default),i(t)?e.extend(t):t}function vt(t,e,n,r,o){var i=Dr();return i.asyncFactory=t,i.asyncMeta={data:e,context:n,children:r,tag:o},i}function ht(r,o,a){if(n(r.error)&&e(r.errorComp))return r.errorComp;if(e(r.resolved))return r.resolved;if(n(r.loading)&&e(r.loadingComp))return r.loadingComp;if(!e(r.contexts)){var s=r.contexts=[a],c=!0,u=function(){for(var t=0,e=s.length;t<e;t++)s[t].$forceUpdate()},l=w(function(t){r.resolved=pt(t,o),c||u()}),f=w(function(t){e(r.errorComp)&&(r.error=!0,u())}),d=r(l,f);return i(d)&&("function"==typeof d.then?t(r.resolved)&&d.then(l,f):e(d.component)&&"function"==typeof d.component.then&&(d.component.then(l,f),e(d.error)&&(r.errorComp=pt(d.error,o)),e(d.loading)&&(r.loadingComp=pt(d.loading,o),0===d.delay?r.loading=!0:setTimeout(function(){t(r.resolved)&&t(r.error)&&(r.loading=!0,u())},d.delay||200)),e(d.timeout)&&setTimeout(function(){t(r.resolved)&&f(null)},d.timeout))),c=!1,r.loading?r.loadingComp:r.resolved}r.contexts.push(a)}function mt(t){return t.isComment&&t.asyncFactory}function yt(t){if(Array.isArray(t))for(var n=0;n<t.length;n++){var r=t[n];if(e(r)&&(e(r.componentOptions)||mt(r)))return r}}function _t(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Ct(t,e)}function gt(t,e,n){n?Jr.$once(t,e):Jr.$on(t,e)}function bt(t,e){Jr.$off(t,e)}function Ct(t,e,n){Jr=t,it(e,n||{},gt,bt,t),Jr=void 0}function wt(t,e){var n={};if(!t)return n;for(var r=0,o=t.length;r<o;r++){var i=t[r],a=i.data;if(a&&a.attrs&&a.attrs.slot&&delete a.attrs.slot,i.context!==e&&i.functionalContext!==e||!a||null==a.slot)(n.default||(n.default=[])).push(i);else{var s=i.data.slot,c=n[s]||(n[s]=[]);"template"===i.tag?c.push.apply(c,i.children):c.push(i)}}for(var u in n)n[u].every(At)&&delete n[u];return n}function At(t){return t.isComment||" "===t.text}function $t(t,e){e=e||{};for(var n=0;n<t.length;n++)Array.isArray(t[n])?$t(t[n],e):e[t[n].key]=t[n].fn;return e}function xt(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}function kt(t,e,n){t.$el=e,t.$options.render||(t.$options.render=Dr),It(t,"beforeMount");var r;return r=function(){t._update(t._render(),n)},t._watcher=new io(t,r,g),n=!1,null==t.$vnode&&(t._isMounted=!0,It(t,"mounted")),t}function Ot(t,e,n,r,o){var i=!!(o||t.$options._renderChildren||r.data.scopedSlots||t.$scopedSlots!==ur);if(t.$options._parentVnode=r,t.$vnode=r,t._vnode&&(t._vnode.parent=r),t.$options._renderChildren=o,t.$attrs=r.data&&r.data.attrs||ur,t.$listeners=n||ur,e&&t.$options.props){Mr.shouldConvert=!1;for(var a=t._props,s=t.$options._propKeys||[],c=0;c<s.length;c++){var u=s[c];a[u]=G(u,t.$options.props,e,t)}Mr.shouldConvert=!0,t.$options.propsData=e}if(n){var l=t.$options._parentListeners;t.$options._parentListeners=n,Ct(t,n,l)}i&&(t.$slots=wt(o,r.context),t.$forceUpdate())}function St(t){for(;t&&(t=t.$parent);)if(t._inactive)return!0;return!1}function Et(t,e){if(e){if(t._directInactive=!1,St(t))return}else if(t._directInactive)return;if(t._inactive||null===t._inactive){t._inactive=!1;for(var n=0;n<t.$children.length;n++)Et(t.$children[n]);It(t,"activated")}}function jt(t,e){if(!(e&&(t._directInactive=!0,St(t))||t._inactive)){t._inactive=!0;for(var n=0;n<t.$children.length;n++)jt(t.$children[n]);It(t,"deactivated")}}function It(t,e){var n=t.$options[e];if(n)for(var r=0,o=n.length;r<o;r++)try{n[r].call(t)}catch(n){X(n,t,e+" hook")}t._hasHookEvent&&t.$emit("hook:"+e)}function Tt(){ro=Xr.length=Yr.length=0,to={},eo=no=!1}function Dt(){no=!0;var t,e;for(Xr.sort(function(t,e){return t.id-e.id}),ro=0;ro<Xr.length;ro++)e=(t=Xr[ro]).id,to[e]=null,t.run();var n=Yr.slice(),r=Xr.slice();Tt(),Nt(n),Lt(r),xr&&cr.devtools&&xr.emit("flush")}function Lt(t){for(var e=t.length;e--;){var n=t[e],r=n.vm;r._watcher===n&&r._isMounted&&It(r,"updated")}}function Pt(t){t._inactive=!1,Yr.push(t)}function Nt(t){for(var e=0;e<t.length;e++)t[e]._inactive=!0,Et(t[e],!0)}function Mt(t){var e=t.id;if(null==to[e]){if(to[e]=!0,no){for(var n=Xr.length-1;n>ro&&Xr[n].id>t.id;)n--;Xr.splice(n+1,0,t)}else Xr.push(t);eo||(eo=!0,rt(Dt))}}function Ut(t){ao.clear(),Ft(t,ao)}function Ft(t,e){var n,r,o=Array.isArray(t);if((o||i(t))&&Object.isExtensible(t)){if(t.__ob__){var a=t.__ob__.dep.id;if(e.has(a))return;e.add(a)}if(o)for(n=t.length;n--;)Ft(t[n],e);else for(n=(r=Object.keys(t)).length;n--;)Ft(t[r[n]],e)}}function Rt(t,e,n){so.get=function(){return this[e][n]},so.set=function(t){this[e][n]=t},Object.defineProperty(t,n,so)}function Vt(t){t._watchers=[];var e=t.$options;e.props&&Bt(t,e.props),e.methods&&Gt(t,e.methods),e.data?Ht(t):L(t._data={},!0),e.computed&&Wt(t,e.computed),e.watch&&e.watch!==gr&&Jt(t,e.watch)}function Bt(t,e){var n=t.$options.propsData||{},r=t._props={},o=t.$options._propKeys=[],i=!t.$parent;Mr.shouldConvert=i;for(var a in e)!function(i){o.push(i);var a=G(i,e,n,t);P(r,i,a),i in t||Rt(t,"_props",i)}(a);Mr.shouldConvert=!0}function Ht(t){var e=t.$options.data;a(e=t._data="function"==typeof e?zt(e,t):e||{})||(e={});for(var n=Object.keys(e),r=t.$options.props,o=n.length;o--;){var i=n[o];r&&p(r,i)||A(i)||Rt(t,"_data",i)}L(e,!0)}function zt(t,e){try{return t.call(e,e)}catch(t){return X(t,e,"data()"),{}}}function Wt(t,e){var n=t._computedWatchers=Object.create(null),r=$r();for(var o in e){var i=e[o],a="function"==typeof i?i:i.get;r||(n[o]=new io(t,a||g,g,co)),o in t||qt(t,o,i)}}function qt(t,e,n){var r=!$r();"function"==typeof n?(so.get=r?Kt(e):n,so.set=g):(so.get=n.get?r&&!1!==n.cache?Kt(e):n.get:g,so.set=n.set?n.set:g),Object.defineProperty(t,e,so)}function Kt(t){return function(){var e=this._computedWatchers&&this._computedWatchers[t];if(e)return e.dirty&&e.evaluate(),Er.target&&e.depend(),e.value}}function Gt(t,e){for(var n in e)t[n]=null==e[n]?g:h(e[n],t)}function Jt(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var o=0;o<r.length;o++)Zt(t,n,r[o]);else Zt(t,n,r)}}function Zt(t,e,n,r){return a(n)&&(r=n,n=n.handler),"string"==typeof n&&(n=t[n]),t.$watch(e,n,r)}function Qt(t){var e=t.$options.provide;e&&(t._provided="function"==typeof e?e.call(t):e)}function Xt(t){var e=Yt(t.$options.inject,t);e&&(Mr.shouldConvert=!1,Object.keys(e).forEach(function(n){P(t,n,e[n])}),Mr.shouldConvert=!0)}function Yt(t,e){if(t){for(var n=Object.create(null),r=kr?Reflect.ownKeys(t).filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}):Object.keys(t),o=0;o<r.length;o++){for(var i=r[o],a=t[i].from,s=e;s;){if(s._provided&&a in s._provided){n[i]=s._provided[a];break}s=s.$parent}if(!s&&"default"in t[i]){var c=t[i].default;n[i]="function"==typeof c?c.call(e):c}}return n}}function te(t,n){var r,o,a,s,c;if(Array.isArray(t)||"string"==typeof t)for(r=new Array(t.length),o=0,a=t.length;o<a;o++)r[o]=n(t[o],o);else if("number"==typeof t)for(r=new Array(t),o=0;o<t;o++)r[o]=n(o+1,o);else if(i(t))for(s=Object.keys(t),r=new Array(s.length),o=0,a=s.length;o<a;o++)c=s[o],r[o]=n(t[c],c,o);return e(r)&&(r._isVList=!0),r}function ee(t,e,n,r){var o,i=this.$scopedSlots[t];if(i)n=n||{},r&&(n=y(y({},r),n)),o=i(n)||e;else{var a=this.$slots[t];a&&(a._rendered=!0),o=a||e}var s=n&&n.slot;return s?this.$createElement("template",{slot:s},o):o}function ne(t){return K(this.$options,"filters",t,!0)||or}function re(t,e,n,r){var o=cr.keyCodes[e]||n;return o?Array.isArray(o)?-1===o.indexOf(t):o!==t:r?nr(r)!==e:void 0}function oe(t,e,n,r,o){if(n)if(i(n)){Array.isArray(n)&&(n=_(n));var a;for(var s in n)!function(i){if("class"===i||"style"===i||Zn(i))a=t;else{var s=t.attrs&&t.attrs.type;a=r||cr.mustUseProp(e,s,i)?t.domProps||(t.domProps={}):t.attrs||(t.attrs={})}i in a||(a[i]=n[i],o&&((t.on||(t.on={}))["update:"+i]=function(t){n[i]=t}))}(s)}else;return t}function ie(t,e){var n=this.$options,r=n.cached||(n.cached=[]),o=r[t];return o&&!e?Array.isArray(o)?I(o):j(o):(o=r[t]=n.staticRenderFns[t].call(this._renderProxy,null,this),se(o,"__static__"+t,!1),o)}function ae(t,e,n){return se(t,"__once__"+e+(n?"_"+n:""),!0),t}function se(t,e,n){if(Array.isArray(t))for(var r=0;r<t.length;r++)t[r]&&"string"!=typeof t[r]&&ce(t[r],e+"_"+r,n);else ce(t,e,n)}function ce(t,e,n){t.isStatic=!0,t.key=e,t.isOnce=n}function ue(t,e){if(e)if(a(e)){var n=t.on=t.on?y({},t.on):{};for(var r in e){var o=n[r],i=e[r];n[r]=o?[].concat(o,i):i}}else;return t}function le(t){t._o=ae,t._n=l,t._s=u,t._l=te,t._t=ee,t._q=b,t._i=C,t._m=ie,t._f=ne,t._k=re,t._b=oe,t._v=E,t._e=Dr,t._u=$t,t._g=ue}function fe(t,e,r,o,i){var a=i.options;this.data=t,this.props=e,this.children=r,this.parent=o,this.listeners=t.on||ur,this.injections=Yt(a.inject,o),this.slots=function(){return wt(r,o)};var s=Object.create(o),c=n(a._compiled),u=!c;c&&(this.$options=a,this.$slots=this.slots(),this.$scopedSlots=t.scopedSlots||ur),a._scopeId?this._c=function(t,e,n,r){var i=ge(s,t,e,n,r,u);return i&&(i.functionalScopeId=a._scopeId,i.functionalContext=o),i}:this._c=function(t,e,n,r){return ge(s,t,e,n,r,u)}}function de(t,n,r,o,i){var a=t.options,s={},c=a.props;if(e(c))for(var u in c)s[u]=G(u,c,n||ur);else e(r.attrs)&&pe(s,r.attrs),e(r.props)&&pe(s,r.props);var l=new fe(r,s,i,o,t),f=a.render.call(null,l._c,l);return f instanceof Ir&&(f.functionalContext=o,f.functionalOptions=a,r.slot&&((f.data||(f.data={})).slot=r.slot)),f}function pe(t,e){for(var n in e)t[Yn(n)]=e[n]}function ve(r,o,a,s,c){if(!t(r)){var u=a.$options._base;if(i(r)&&(r=u.extend(r)),"function"==typeof r){var l;if(t(r.cid)&&(l=r,void 0===(r=ht(l,u,a))))return vt(l,o,a,s,c);o=o||{},$e(r),e(o.model)&&_e(r.options,o);var f=st(o,r,c);if(n(r.options.functional))return de(r,f,o,a,s);var d=o.on;if(o.on=o.nativeOn,n(r.options.abstract)){var p=o.slot;o={},p&&(o.slot=p)}me(o);var v=r.options.name||c;return new Ir("vue-component-"+r.cid+(v?"-"+v:""),o,void 0,void 0,void 0,a,{Ctor:r,propsData:f,listeners:d,tag:c,children:s},l)}}}function he(t,n,r,o){var i=t.componentOptions,a={_isComponent:!0,parent:n,propsData:i.propsData,_componentTag:i.tag,_parentVnode:t,_parentListeners:i.listeners,_renderChildren:i.children,_parentElm:r||null,_refElm:o||null},s=t.data.inlineTemplate;return e(s)&&(a.render=s.render,a.staticRenderFns=s.staticRenderFns),new i.Ctor(a)}function me(t){t.hook||(t.hook={});for(var e=0;e<lo.length;e++){var n=lo[e],r=t.hook[n],o=uo[n];t.hook[n]=r?ye(o,r):o}}function ye(t,e){return function(n,r,o,i){t(n,r,o,i),e(n,r,o,i)}}function _e(t,n){var r=t.model&&t.model.prop||"value",o=t.model&&t.model.event||"input";(n.props||(n.props={}))[r]=n.model.value;var i=n.on||(n.on={});e(i[o])?i[o]=[n.model.callback].concat(i[o]):i[o]=n.model.callback}function ge(t,e,r,i,a,s){return(Array.isArray(r)||o(r))&&(a=i,i=r,r=void 0),n(s)&&(a=po),be(t,e,r,i,a)}function be(t,n,r,o,i){if(e(r)&&e(r.__ob__))return Dr();if(e(r)&&e(r.is)&&(n=r.is),!n)return Dr();Array.isArray(o)&&"function"==typeof o[0]&&((r=r||{}).scopedSlots={default:o[0]},o.length=0),i===po?o=lt(o):i===fo&&(o=ut(o));var a,s;if("string"==typeof n){var c;s=t.$vnode&&t.$vnode.ns||cr.getTagNamespace(n),a=cr.isReservedTag(n)?new Ir(cr.parsePlatformTagName(n),r,o,void 0,void 0,t):e(c=K(t.$options,"components",n))?ve(c,r,t,o,n):new Ir(n,r,o,void 0,void 0,t)}else a=ve(n,r,t,o);return e(a)?(s&&Ce(a,s),a):Dr()}function Ce(r,o,i){if(r.ns=o,"foreignObject"===r.tag&&(o=void 0,i=!0),e(r.children))for(var a=0,s=r.children.length;a<s;a++){var c=r.children[a];e(c.tag)&&(t(c.ns)||n(i))&&Ce(c,o,i)}}function we(t){t._vnode=null;var e=t.$options,n=t.$vnode=e._parentVnode,r=n&&n.context;t.$slots=wt(e._renderChildren,r),t.$scopedSlots=ur,t._c=function(e,n,r,o){return ge(t,e,n,r,o,!1)},t.$createElement=function(e,n,r,o){return ge(t,e,n,r,o,!0)};var o=n&&n.data;P(t,"$attrs",o&&o.attrs||ur,null,!0),P(t,"$listeners",e._parentListeners||ur,null,!0)}function Ae(t,e){var n=t.$options=Object.create(t.constructor.options);n.parent=e.parent,n.propsData=e.propsData,n._parentVnode=e._parentVnode,n._parentListeners=e._parentListeners,n._renderChildren=e._renderChildren,n._componentTag=e._componentTag,n._parentElm=e._parentElm,n._refElm=e._refElm,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}function $e(t){var e=t.options;if(t.super){var n=$e(t.super);if(n!==t.superOptions){t.superOptions=n;var r=xe(t);r&&y(t.extendOptions,r),(e=t.options=q(n,t.extendOptions)).name&&(e.components[e.name]=t)}}return e}function xe(t){var e,n=t.options,r=t.extendOptions,o=t.sealedOptions;for(var i in n)n[i]!==o[i]&&(e||(e={}),e[i]=ke(n[i],r[i],o[i]));return e}function ke(t,e,n){if(Array.isArray(t)){var r=[];n=Array.isArray(n)?n:[n],e=Array.isArray(e)?e:[e];for(var o=0;o<t.length;o++)(e.indexOf(t[o])>=0||n.indexOf(t[o])<0)&&r.push(t[o]);return r}return t}function Oe(t){this._init(t)}function Se(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=m(arguments,1);return n.unshift(this),"function"==typeof t.install?t.install.apply(t,n):"function"==typeof t&&t.apply(null,n),e.push(t),this}}function Ee(t){t.mixin=function(t){return this.options=q(this.options,t),this}}function je(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,o=t._Ctor||(t._Ctor={});if(o[r])return o[r];var i=t.name||n.options.name,a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=q(n.options,t),a.super=n,a.options.props&&Ie(a),a.options.computed&&Te(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,ar.forEach(function(t){a[t]=n[t]}),i&&(a.options.components[i]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=y({},a.options),o[r]=a,a}}function Ie(t){var e=t.options.props;for(var n in e)Rt(t.prototype,"_props",n)}function Te(t){var e=t.options.computed;for(var n in e)qt(t.prototype,n,e[n])}function De(t){ar.forEach(function(e){t[e]=function(t,n){return n?("component"===e&&a(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&"function"==typeof n&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}})}function Le(t){return t&&(t.Ctor.options.name||t.tag)}function Pe(t,e){return Array.isArray(t)?t.indexOf(e)>-1:"string"==typeof t?t.split(",").indexOf(e)>-1:!!s(t)&&t.test(e)}function Ne(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=Le(a.componentOptions);s&&!e(s)&&Me(n,i,r,o)}}}function Me(t,e,n,r){var o=t[e];o&&o!==r&&o.componentInstance.$destroy(),t[e]=null,d(n,e)}function Ue(t){for(var n=t.data,r=t,o=t;e(o.componentInstance);)(o=o.componentInstance._vnode).data&&(n=Fe(o.data,n));for(;e(r=r.parent);)r.data&&(n=Fe(n,r.data));return Re(n.staticClass,n.class)}function Fe(t,n){return{staticClass:Ve(t.staticClass,n.staticClass),class:e(t.class)?[t.class,n.class]:n.class}}function Re(t,n){return e(t)||e(n)?Ve(t,Be(n)):""}function Ve(t,e){return t?e?t+" "+e:t:e||""}function Be(t){return Array.isArray(t)?He(t):i(t)?ze(t):"string"==typeof t?t:""}function He(t){for(var n,r="",o=0,i=t.length;o<i;o++)e(n=Be(t[o]))&&""!==n&&(r&&(r+=" "),r+=n);return r}function ze(t){var e="";for(var n in t)t[n]&&(e&&(e+=" "),e+=n);return e}function We(t){if("string"==typeof t){var e=document.querySelector(t);return e||document.createElement("div")}return t}function qe(t,e){var n=t.data.ref;if(n){var r=t.context,o=t.componentInstance||t.elm,i=r.$refs;e?Array.isArray(i[n])?d(i[n],o):i[n]===o&&(i[n]=void 0):t.data.refInFor?Array.isArray(i[n])?i[n].indexOf(o)<0&&i[n].push(o):i[n]=[o]:i[n]=o}}function Ke(r,o){return r.key===o.key&&(r.tag===o.tag&&r.isComment===o.isComment&&e(r.data)===e(o.data)&&Ge(r,o)||n(r.isAsyncPlaceholder)&&r.asyncFactory===o.asyncFactory&&t(o.asyncFactory.error))}function Ge(t,n){if("input"!==t.tag)return!0;var r,o=e(r=t.data)&&e(r=r.attrs)&&r.type,i=e(r=n.data)&&e(r=r.attrs)&&r.type;return o===i||To(o)&&To(i)}function Je(t,n,r){var o,i,a={};for(o=n;o<=r;++o)e(i=t[o].key)&&(a[i]=o);return a}function Ze(t,e){(t.data.directives||e.data.directives)&&Qe(t,e)}function Qe(t,e){var n,r,o,i=t===Po,a=e===Po,s=Xe(t.data.directives,t.context),c=Xe(e.data.directives,e.context),u=[],l=[];for(n in c)r=s[n],o=c[n],r?(o.oldValue=r.value,tn(o,"update",e,t),o.def&&o.def.componentUpdated&&l.push(o)):(tn(o,"bind",e,t),o.def&&o.def.inserted&&u.push(o));if(u.length){var f=function(){for(var n=0;n<u.length;n++)tn(u[n],"inserted",e,t)};i?at(e,"insert",f):f()}if(l.length&&at(e,"postpatch",function(){for(var n=0;n<l.length;n++)tn(l[n],"componentUpdated",e,t)}),!i)for(n in s)c[n]||tn(s[n],"unbind",t,t,a)}function Xe(t,e){var n=Object.create(null);if(!t)return n;var r,o;for(r=0;r<t.length;r++)(o=t[r]).modifiers||(o.modifiers=Uo),n[Ye(o)]=o,o.def=K(e.$options,"directives",o.name,!0);return n}function Ye(t){return t.rawName||t.name+"."+Object.keys(t.modifiers||{}).join(".")}function tn(t,e,n,r,o){var i=t.def&&t.def[e];if(i)try{i(n.elm,t,n,r,o)}catch(r){X(r,n.context,"directive "+t.name+" "+e+" hook")}}function en(n,r){var o=r.componentOptions;if(!(e(o)&&!1===o.Ctor.options.inheritAttrs||t(n.data.attrs)&&t(r.data.attrs))){var i,a,s=r.elm,c=n.data.attrs||{},u=r.data.attrs||{};e(u.__ob__)&&(u=r.data.attrs=y({},u));for(i in u)a=u[i],c[i]!==a&&nn(s,i,a);(hr||mr)&&u.value!==c.value&&nn(s,"value",u.value);for(i in c)t(u[i])&&($o(i)?s.removeAttributeNS(Ao,xo(i)):Co(i)||s.removeAttribute(i))}}function nn(t,e,n){wo(e)?ko(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):Co(e)?t.setAttribute(e,ko(n)||"false"===n?"false":"true"):$o(e)?ko(n)?t.removeAttributeNS(Ao,xo(e)):t.setAttributeNS(Ao,e,n):ko(n)?t.removeAttribute(e):t.setAttribute(e,n)}function rn(n,r){var o=r.elm,i=r.data,a=n.data;if(!(t(i.staticClass)&&t(i.class)&&(t(a)||t(a.staticClass)&&t(a.class)))){var s=Ue(r),c=o._transitionClasses;e(c)&&(s=Ve(s,Be(c))),s!==o._prevClass&&(o.setAttribute("class",s),o._prevClass=s)}}function on(t){if(e(t[Bo])){var n=vr?"change":"input";t[n]=[].concat(t[Bo],t[n]||[]),delete t[Bo]}e(t[Ho])&&(t.change=[].concat(t[Ho],t.change||[]),delete t[Ho])}function an(t,e,n){var r=yo;return function o(){null!==t.apply(null,arguments)&&cn(e,o,n,r)}}function sn(t,e,n,r,o){e=nt(e),n&&(e=an(e,t,r)),yo.addEventListener(t,e,br?{capture:r,passive:o}:r)}function cn(t,e,n,r){(r||yo).removeEventListener(t,e._withTask||e,n)}function un(e,n){if(!t(e.data.on)||!t(n.data.on)){var r=n.data.on||{},o=e.data.on||{};yo=n.elm,on(r),it(r,o,sn,cn,n.context),yo=void 0}}function ln(n,r){if(!t(n.data.domProps)||!t(r.data.domProps)){var o,i,a=r.elm,s=n.data.domProps||{},c=r.data.domProps||{};e(c.__ob__)&&(c=r.data.domProps=y({},c));for(o in s)t(c[o])&&(a[o]="");for(o in c){if(i=c[o],"textContent"===o||"innerHTML"===o){if(r.children&&(r.children.length=0),i===s[o])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===o){a._value=i;var u=t(i)?"":String(i);fn(a,u)&&(a.value=u)}else a[o]=i}}}function fn(t,e){return!t.composing&&("OPTION"===t.tagName||dn(t,e)||pn(t,e))}function dn(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}function pn(t,n){var r=t.value,o=t._vModifiers;return e(o)&&o.number?l(r)!==l(n):e(o)&&o.trim?r.trim()!==n.trim():r!==n}function vn(t){var e=hn(t.style);return t.staticStyle?y(t.staticStyle,e):e}function hn(t){return Array.isArray(t)?_(t):"string"==typeof t?qo(t):t}function mn(t,e){var n,r={};if(e)for(var o=t;o.componentInstance;)(o=o.componentInstance._vnode).data&&(n=vn(o.data))&&y(r,n);(n=vn(t.data))&&y(r,n);for(var i=t;i=i.parent;)i.data&&(n=vn(i.data))&&y(r,n);return r}function yn(n,r){var o=r.data,i=n.data;if(!(t(o.staticStyle)&&t(o.style)&&t(i.staticStyle)&&t(i.style))){var a,s,c=r.elm,u=i.staticStyle,l=i.normalizedStyle||i.style||{},f=u||l,d=hn(r.data.style)||{};r.data.normalizedStyle=e(d.__ob__)?y({},d):d;var p=mn(r,!0);for(s in f)t(p[s])&&Jo(c,s,"");for(s in p)(a=p[s])!==f[s]&&Jo(c,s,null==a?"":a)}}function _n(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(/\s+/).forEach(function(e){return t.classList.add(e)}):t.classList.add(e);else{var n=" "+(t.getAttribute("class")||"")+" ";n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function gn(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(/\s+/).forEach(function(e){return t.classList.remove(e)}):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" "+(t.getAttribute("class")||"")+" ",r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function bn(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&y(e,Yo(t.name||"v")),y(e,t),e}return"string"==typeof t?Yo(t):void 0}}function Cn(t){si(function(){si(t)})}function wn(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),_n(t,e))}function An(t,e){t._transitionClasses&&d(t._transitionClasses,e),gn(t,e)}function $n(t,e,n){var r=xn(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===ei?oi:ai,c=0,u=function(){t.removeEventListener(s,l),n()},l=function(e){e.target===t&&++c>=a&&u()};setTimeout(function(){c<a&&u()},i+1),t.addEventListener(s,l)}function xn(t,e){var n,r=window.getComputedStyle(t),o=r[ri+"Delay"].split(", "),i=r[ri+"Duration"].split(", "),a=kn(o,i),s=r[ii+"Delay"].split(", "),c=r[ii+"Duration"].split(", "),u=kn(s,c),l=0,f=0;return e===ei?a>0&&(n=ei,l=a,f=i.length):e===ni?u>0&&(n=ni,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?ei:ni:null)?n===ei?i.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===ei&&ci.test(r[ri+"Property"])}}function kn(t,e){for(;t.length<e.length;)t=t.concat(t);return Math.max.apply(null,e.map(function(e,n){return On(e)+On(t[n])}))}function On(t){return 1e3*Number(t.slice(0,-1))}function Sn(n,r){var o=n.elm;e(o._leaveCb)&&(o._leaveCb.cancelled=!0,o._leaveCb());var a=bn(n.data.transition);if(!t(a)&&!e(o._enterCb)&&1===o.nodeType){for(var s=a.css,c=a.type,u=a.enterClass,f=a.enterToClass,d=a.enterActiveClass,p=a.appearClass,v=a.appearToClass,h=a.appearActiveClass,m=a.beforeEnter,y=a.enter,_=a.afterEnter,g=a.enterCancelled,b=a.beforeAppear,C=a.appear,A=a.afterAppear,$=a.appearCancelled,x=a.duration,k=Qr,O=Qr.$vnode;O&&O.parent;)k=(O=O.parent).context;var S=!k._isMounted||!n.isRootInsert;if(!S||C||""===C){var E=S&&p?p:u,j=S&&h?h:d,I=S&&v?v:f,T=S?b||m:m,D=S&&"function"==typeof C?C:y,L=S?A||_:_,P=S?$||g:g,N=l(i(x)?x.enter:x),M=!1!==s&&!hr,U=In(D),F=o._enterCb=w(function(){M&&(An(o,I),An(o,j)),F.cancelled?(M&&An(o,E),P&&P(o)):L&&L(o),o._enterCb=null});n.data.show||at(n,"insert",function(){var t=o.parentNode,e=t&&t._pending&&t._pending[n.key];e&&e.tag===n.tag&&e.elm._leaveCb&&e.elm._leaveCb(),D&&D(o,F)}),T&&T(o),M&&(wn(o,E),wn(o,j),Cn(function(){wn(o,I),An(o,E),F.cancelled||U||(jn(N)?setTimeout(F,N):$n(o,c,F))})),n.data.show&&(r&&r(),D&&D(o,F)),M||U||F()}}}function En(n,r){function o(){$.cancelled||(n.data.show||((a.parentNode._pending||(a.parentNode._pending={}))[n.key]=n),v&&v(a),b&&(wn(a,f),wn(a,p),Cn(function(){wn(a,d),An(a,f),$.cancelled||C||(jn(A)?setTimeout($,A):$n(a,u,$))})),h&&h(a,$),b||C||$())}var a=n.elm;e(a._enterCb)&&(a._enterCb.cancelled=!0,a._enterCb());var s=bn(n.data.transition);if(t(s))return r();if(!e(a._leaveCb)&&1===a.nodeType){var c=s.css,u=s.type,f=s.leaveClass,d=s.leaveToClass,p=s.leaveActiveClass,v=s.beforeLeave,h=s.leave,m=s.afterLeave,y=s.leaveCancelled,_=s.delayLeave,g=s.duration,b=!1!==c&&!hr,C=In(h),A=l(i(g)?g.leave:g),$=a._leaveCb=w(function(){a.parentNode&&a.parentNode._pending&&(a.parentNode._pending[n.key]=null),b&&(An(a,d),An(a,p)),$.cancelled?(b&&An(a,f),y&&y(a)):(r(),m&&m(a)),a._leaveCb=null});_?_(o):o()}}function jn(t){return"number"==typeof t&&!isNaN(t)}function In(n){if(t(n))return!1;var r=n.fns;return e(r)?In(Array.isArray(r)?r[0]:r):(n._length||n.length)>1}function Tn(t,e){!0!==e.data.show&&Sn(e)}function Dn(t,e,n){Ln(t,e,n),(vr||mr)&&setTimeout(function(){Ln(t,e,n)},0)}function Ln(t,e,n){var r=e.value,o=t.multiple;if(!o||Array.isArray(r)){for(var i,a,s=0,c=t.options.length;s<c;s++)if(a=t.options[s],o)i=C(r,Nn(a))>-1,a.selected!==i&&(a.selected=i);else if(b(Nn(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function Pn(t,e){return e.every(function(e){return!b(e,t)})}function Nn(t){return"_value"in t?t._value:t.value}function Mn(t){t.target.composing=!0}function Un(t){t.target.composing&&(t.target.composing=!1,Fn(t.target,"input"))}function Fn(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function Rn(t){return!t.componentInstance||t.data&&t.data.transition?t:Rn(t.componentInstance._vnode)}function Vn(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Vn(yt(e.children)):t}function Bn(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var i in o)e[Yn(i)]=o[i];return e}function Hn(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}function zn(t){for(;t=t.parent;)if(t.data.transition)return!0}function Wn(t,e){return e.key===t.key&&e.tag===t.tag}function qn(t){t.elm._moveCb&&t.elm._moveCb(),t.elm._enterCb&&t.elm._enterCb()}function Kn(t){t.data.newPos=t.elm.getBoundingClientRect()}function Gn(t){var e=t.data.pos,n=t.data.newPos,r=e.left-n.left,o=e.top-n.top;if(r||o){t.data.moved=!0;var i=t.elm.style;i.transform=i.WebkitTransform="translate("+r+"px,"+o+"px)",i.transitionDuration="0s"}}var Jn=Object.prototype.toString,Zn=f("key,ref,slot,slot-scope,is"),Qn=Object.prototype.hasOwnProperty,Xn=/-(\w)/g,Yn=v(function(t){return t.replace(Xn,function(t,e){return e?e.toUpperCase():""})}),tr=v(function(t){return t.charAt(0).toUpperCase()+t.slice(1)}),er=/\B([A-Z])/g,nr=v(function(t){return t.replace(er,"-$1").toLowerCase()}),rr=function(t,e,n){return!1},or=function(t){return t},ir="data-server-rendered",ar=["component","directive","filter"],sr=["beforeCreate","created","beforeMount","mounted","beforeUpdate","updated","beforeDestroy","destroyed","activated","deactivated","errorCaptured"],cr={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:rr,isReservedAttr:rr,isUnknownElement:rr,getTagNamespace:g,parsePlatformTagName:or,mustUseProp:rr,_lifecycleHooks:sr},ur=Object.freeze({}),lr=/[^\w.$]/,fr="__proto__"in{},dr="undefined"!=typeof window,pr=dr&&window.navigator.userAgent.toLowerCase(),vr=pr&&/msie|trident/.test(pr),hr=pr&&pr.indexOf("msie 9.0")>0,mr=pr&&pr.indexOf("edge/")>0,yr=pr&&pr.indexOf("android")>0,_r=pr&&/iphone|ipad|ipod|ios/.test(pr),gr=(pr&&/chrome\/\d+/.test(pr),{}.watch),br=!1;if(dr)try{var Cr={};Object.defineProperty(Cr,"passive",{get:function(){br=!0}}),window.addEventListener("test-passive",null,Cr)}catch(t){}var wr,Ar,$r=function(){return void 0===wr&&(wr=!dr&&"undefined"!=typeof global&&"server"===global.process.env.VUE_ENV),wr},xr=dr&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,kr="undefined"!=typeof Symbol&&k(Symbol)&&"undefined"!=typeof Reflect&&k(Reflect.ownKeys);Ar="undefined"!=typeof Set&&k(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var Or=g,Sr=0,Er=function(){this.id=Sr++,this.subs=[]};Er.prototype.addSub=function(t){this.subs.push(t)},Er.prototype.removeSub=function(t){d(this.subs,t)},Er.prototype.depend=function(){Er.target&&Er.target.addDep(this)},Er.prototype.notify=function(){for(var t=this.subs.slice(),e=0,n=t.length;e<n;e++)t[e].update()},Er.target=null;var jr=[],Ir=function(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.functionalContext=void 0,this.functionalOptions=void 0,this.functionalScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1},Tr={child:{configurable:!0}};Tr.child.get=function(){return this.componentInstance},Object.defineProperties(Ir.prototype,Tr);var Dr=function(t){void 0===t&&(t="");var e=new Ir;return e.text=t,e.isComment=!0,e},Lr=Array.prototype,Pr=Object.create(Lr);["push","pop","shift","unshift","splice","sort","reverse"].forEach(function(t){var e=Lr[t];$(Pr,t,function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];var o,i=e.apply(this,n),a=this.__ob__;switch(t){case"push":case"unshift":o=n;break;case"splice":o=n.slice(2)}return o&&a.observeArray(o),a.dep.notify(),i})});var Nr=Object.getOwnPropertyNames(Pr),Mr={shouldConvert:!0},Ur=function(t){this.value=t,this.dep=new Er,this.vmCount=0,$(t,"__ob__",this),Array.isArray(t)?((fr?T:D)(t,Pr,Nr),this.observeArray(t)):this.walk(t)};Ur.prototype.walk=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)P(t,e[n],t[e[n]])},Ur.prototype.observeArray=function(t){for(var e=0,n=t.length;e<n;e++)L(t[e])};var Fr=cr.optionMergeStrategies;Fr.data=function(t,e,n){return n?R(t,e,n):e&&"function"!=typeof e?t:R(t,e)},sr.forEach(function(t){Fr[t]=V}),ar.forEach(function(t){Fr[t+"s"]=B}),Fr.watch=function(t,e,n,r){if(t===gr&&(t=void 0),e===gr&&(e=void 0),!e)return Object.create(t||null);if(!t)return e;var o={};y(o,t);for(var i in e){var a=o[i],s=e[i];a&&!Array.isArray(a)&&(a=[a]),o[i]=a?a.concat(s):Array.isArray(s)?s:[s]}return o},Fr.props=Fr.methods=Fr.inject=Fr.computed=function(t,e,n,r){if(!t)return e;var o=Object.create(null);return y(o,t),e&&y(o,e),o},Fr.provide=R;var Rr,Vr,Br=function(t,e){return void 0===e?t:e},Hr=[],zr=!1,Wr=!1;if("undefined"!=typeof setImmediate&&k(setImmediate))Vr=function(){setImmediate(et)};else if("undefined"==typeof MessageChannel||!k(MessageChannel)&&"[object MessageChannelConstructor]"!==MessageChannel.toString())Vr=function(){setTimeout(et,0)};else{var qr=new MessageChannel,Kr=qr.port2;qr.port1.onmessage=et,Vr=function(){Kr.postMessage(1)}}if("undefined"!=typeof Promise&&k(Promise)){var Gr=Promise.resolve();Rr=function(){Gr.then(et),_r&&setTimeout(g)}}else Rr=Vr;var Jr,Zr=v(function(t){var e="&"===t.charAt(0),n="~"===(t=e?t.slice(1):t).charAt(0),r="!"===(t=n?t.slice(1):t).charAt(0);return t=r?t.slice(1):t,{name:t,once:n,capture:r,passive:e}}),Qr=null,Xr=[],Yr=[],to={},eo=!1,no=!1,ro=0,oo=0,io=function(t,e,n,r){this.vm=t,t._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++oo,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new Ar,this.newDepIds=new Ar,this.expression="","function"==typeof e?this.getter=e:(this.getter=x(e),this.getter||(this.getter=function(){})),this.value=this.lazy?void 0:this.get()};io.prototype.get=function(){O(this);var t,e=this.vm;try{t=this.getter.call(e,e)}catch(t){if(!this.user)throw t;X(t,e,'getter for watcher "'+this.expression+'"')}finally{this.deep&&Ut(t),S(),this.cleanupDeps()}return t},io.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))},io.prototype.cleanupDeps=function(){for(var t=this,e=this.deps.length;e--;){var n=t.deps[e];t.newDepIds.has(n.id)||n.removeSub(t)}var r=this.depIds;this.depIds=this.newDepIds,this.newDepIds=r,this.newDepIds.clear(),r=this.deps,this.deps=this.newDeps,this.newDeps=r,this.newDeps.length=0},io.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():Mt(this)},io.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||i(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(t){X(t,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,t,e)}}},io.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},io.prototype.depend=function(){for(var t=this,e=this.deps.length;e--;)t.deps[e].depend()},io.prototype.teardown=function(){var t=this;if(this.active){this.vm._isBeingDestroyed||d(this.vm._watchers,this);for(var e=this.deps.length;e--;)t.deps[e].removeSub(t);this.active=!1}};var ao=new Ar,so={enumerable:!0,configurable:!0,get:g,set:g},co={lazy:!0};le(fe.prototype);var uo={init:function(t,e,n,r){if(!t.componentInstance||t.componentInstance._isDestroyed)(t.componentInstance=he(t,Qr,n,r)).$mount(e?t.elm:void 0,e);else if(t.data.keepAlive){var o=t;uo.prepatch(o,o)}},prepatch:function(t,e){var n=e.componentOptions;Ot(e.componentInstance=t.componentInstance,n.propsData,n.listeners,e,n.children)},insert:function(t){var e=t.context,n=t.componentInstance;n._isMounted||(n._isMounted=!0,It(n,"mounted")),t.data.keepAlive&&(e._isMounted?Pt(n):Et(n,!0))},destroy:function(t){var e=t.componentInstance;e._isDestroyed||(t.data.keepAlive?jt(e,!0):e.$destroy())}},lo=Object.keys(uo),fo=1,po=2,vo=0;!function(t){t.prototype._init=function(t){var e=this;e._uid=vo++,e._isVue=!0,t&&t._isComponent?Ae(e,t):e.$options=q($e(e.constructor),t||{},e),e._renderProxy=e,e._self=e,xt(e),_t(e),we(e),It(e,"beforeCreate"),Xt(e),Vt(e),Qt(e),It(e,"created"),e.$options.el&&e.$mount(e.$options.el)}}(Oe),function(t){var e={};e.get=function(){return this._data};var n={};n.get=function(){return this._props},Object.defineProperty(t.prototype,"$data",e),Object.defineProperty(t.prototype,"$props",n),t.prototype.$set=N,t.prototype.$delete=M,t.prototype.$watch=function(t,e,n){var r=this;if(a(e))return Zt(r,t,e,n);(n=n||{}).user=!0;var o=new io(r,t,e,n);return n.immediate&&e.call(r,o.value),function(){o.teardown()}}}(Oe),function(t){var e=/^hook:/;t.prototype.$on=function(t,n){var r=this,o=this;if(Array.isArray(t))for(var i=0,a=t.length;i<a;i++)r.$on(t[i],n);else(o._events[t]||(o._events[t]=[])).push(n),e.test(t)&&(o._hasHookEvent=!0);return o},t.prototype.$once=function(t,e){function n(){r.$off(t,n),e.apply(r,arguments)}var r=this;return n.fn=e,r.$on(t,n),r},t.prototype.$off=function(t,e){var n=this,r=this;if(!arguments.length)return r._events=Object.create(null),r;if(Array.isArray(t)){for(var o=0,i=t.length;o<i;o++)n.$off(t[o],e);return r}var a=r._events[t];if(!a)return r;if(!e)return r._events[t]=null,r;if(e)for(var s,c=a.length;c--;)if((s=a[c])===e||s.fn===e){a.splice(c,1);break}return r},t.prototype.$emit=function(t){var e=this,n=e._events[t];if(n){n=n.length>1?m(n):n;for(var r=m(arguments,1),o=0,i=n.length;o<i;o++)try{n[o].apply(e,r)}catch(n){X(n,e,'event handler for "'+t+'"')}}return e}}(Oe),function(t){t.prototype._update=function(t,e){var n=this;n._isMounted&&It(n,"beforeUpdate");var r=n.$el,o=n._vnode,i=Qr;Qr=n,n._vnode=t,o?n.$el=n.__patch__(o,t):(n.$el=n.__patch__(n.$el,t,e,!1,n.$options._parentElm,n.$options._refElm),n.$options._parentElm=n.$options._refElm=null),Qr=i,r&&(r.__vue__=null),n.$el&&(n.$el.__vue__=n),n.$vnode&&n.$parent&&n.$vnode===n.$parent._vnode&&(n.$parent.$el=n.$el)},t.prototype.$forceUpdate=function(){var t=this;t._watcher&&t._watcher.update()},t.prototype.$destroy=function(){var t=this;if(!t._isBeingDestroyed){It(t,"beforeDestroy"),t._isBeingDestroyed=!0;var e=t.$parent;!e||e._isBeingDestroyed||t.$options.abstract||d(e.$children,t),t._watcher&&t._watcher.teardown();for(var n=t._watchers.length;n--;)t._watchers[n].teardown();t._data.__ob__&&t._data.__ob__.vmCount--,t._isDestroyed=!0,t.__patch__(t._vnode,null),It(t,"destroyed"),t.$off(),t.$el&&(t.$el.__vue__=null),t.$vnode&&(t.$vnode.parent=null)}}}(Oe),function(t){le(t.prototype),t.prototype.$nextTick=function(t){return rt(t,this)},t.prototype._render=function(){var t=this,e=t.$options,n=e.render,r=e._parentVnode;if(t._isMounted)for(var o in t.$slots){var i=t.$slots[o];i._rendered&&(t.$slots[o]=I(i,!0))}t.$scopedSlots=r&&r.data.scopedSlots||ur,t.$vnode=r;var a;try{a=n.call(t._renderProxy,t.$createElement)}catch(e){X(e,t,"render"),a=t._vnode}return a instanceof Ir||(a=Dr()),a.parent=r,a}}(Oe);var ho=[String,RegExp,Array],mo={KeepAlive:{name:"keep-alive",abstract:!0,props:{include:ho,exclude:ho,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){var t=this;for(var e in t.cache)Me(t.cache,e,t.keys)},watch:{include:function(t){Ne(this,function(e){return Pe(t,e)})},exclude:function(t){Ne(this,function(e){return!Pe(t,e)})}},render:function(){var t=yt(this.$slots.default),e=t&&t.componentOptions;if(e){var n=Le(e);if(n&&(this.exclude&&Pe(this.exclude,n)||this.include&&!Pe(this.include,n)))return t;var r=this,o=r.cache,i=r.keys,a=null==t.key?e.Ctor.cid+(e.tag?"::"+e.tag:""):t.key;o[a]?(t.componentInstance=o[a].componentInstance,d(i,a),i.push(a)):(o[a]=t,i.push(a),this.max&&i.length>parseInt(this.max)&&Me(o,i[0],i,this._vnode)),t.data.keepAlive=!0}return t}}};!function(t){var e={};e.get=function(){return cr},Object.defineProperty(t,"config",e),t.util={warn:Or,extend:y,mergeOptions:q,defineReactive:P},t.set=N,t.delete=M,t.nextTick=rt,t.options=Object.create(null),ar.forEach(function(e){t.options[e+"s"]=Object.create(null)}),t.options._base=t,y(t.options.components,mo),Se(t),Ee(t),je(t),De(t)}(Oe),Object.defineProperty(Oe.prototype,"$isServer",{get:$r}),Object.defineProperty(Oe.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Oe.version="2.5.3";var yo,_o,go=f("style,class"),bo=f("input,textarea,option,select,progress"),Co=f("contenteditable,draggable,spellcheck"),wo=f("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,translate,truespeed,typemustmatch,visible"),Ao="http://www.w3.org/1999/xlink",$o=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},xo=function(t){return $o(t)?t.slice(6,t.length):""},ko=function(t){return null==t||!1===t},Oo={svg:"http://www.w3.org/2000/svg",math:"http://www.w3.org/1998/Math/MathML"},So=f("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"),Eo=f("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",!0),jo=function(t){return So(t)||Eo(t)},Io=Object.create(null),To=f("text,number,password,search,email,tel,url"),Do=Object.freeze({createElement:function(t,e){var n=document.createElement(t);return"select"!==t?n:(e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)},createElementNS:function(t,e){return document.createElementNS(Oo[t],e)},createTextNode:function(t){return document.createTextNode(t)},createComment:function(t){return document.createComment(t)},insertBefore:function(t,e,n){t.insertBefore(e,n)},removeChild:function(t,e){t.removeChild(e)},appendChild:function(t,e){t.appendChild(e)},parentNode:function(t){return t.parentNode},nextSibling:function(t){return t.nextSibling},tagName:function(t){return t.tagName},setTextContent:function(t,e){t.textContent=e},setAttribute:function(t,e,n){t.setAttribute(e,n)}}),Lo={create:function(t,e){qe(e)},update:function(t,e){t.data.ref!==e.data.ref&&(qe(t,!0),qe(e))},destroy:function(t){qe(t,!0)}},Po=new Ir("",{},[]),No=["create","activate","update","remove","destroy"],Mo={create:Ze,update:Ze,destroy:function(t){Ze(t,Po)}},Uo=Object.create(null),Fo=[Lo,Mo],Ro={create:en,update:en},Vo={create:rn,update:rn},Bo="__r",Ho="__c",zo={create:un,update:un},Wo={create:ln,update:ln},qo=v(function(t){var e={},n=/;(?![^(]*\))/g,r=/:(.+)/;return t.split(n).forEach(function(t){if(t){var n=t.split(r);n.length>1&&(e[n[0].trim()]=n[1].trim())}}),e}),Ko=/^--/,Go=/\s*!important$/,Jo=function(t,e,n){if(Ko.test(e))t.style.setProperty(e,n);else if(Go.test(n))t.style.setProperty(e,n.replace(Go,""),"important");else{var r=Qo(e);if(Array.isArray(n))for(var o=0,i=n.length;o<i;o++)t.style[r]=n[o];else t.style[r]=n}},Zo=["Webkit","Moz","ms"],Qo=v(function(t){if(_o=_o||document.createElement("div").style,"filter"!==(t=Yn(t))&&t in _o)return t;for(var e=t.charAt(0).toUpperCase()+t.slice(1),n=0;n<Zo.length;n++){var r=Zo[n]+e;if(r in _o)return r}}),Xo={create:yn,update:yn},Yo=v(function(t){return{enterClass:t+"-enter",enterToClass:t+"-enter-to",enterActiveClass:t+"-enter-active",leaveClass:t+"-leave",leaveToClass:t+"-leave-to",leaveActiveClass:t+"-leave-active"}}),ti=dr&&!hr,ei="transition",ni="animation",ri="transition",oi="transitionend",ii="animation",ai="animationend";ti&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(ri="WebkitTransition",oi="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(ii="WebkitAnimation",ai="webkitAnimationEnd"));var si=dr?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()},ci=/\b(transform|all)(,|$)/,ui=function(r){function i(t){return new Ir(I.tagName(t).toLowerCase(),{},[],void 0,t)}function a(t,e){function n(){0==--n.listeners&&s(t)}return n.listeners=e,n}function s(t){var n=I.parentNode(t);e(n)&&I.removeChild(n,t)}function c(t,r,o,i,a){if(t.isRootInsert=!a,!u(t,r,o,i)){var s=t.data,c=t.children,l=t.tag;e(l)?(t.elm=t.ns?I.createElementNS(t.ns,l):I.createElement(l,t),y(t),v(t,c,r),e(s)&&m(t,r),p(o,t.elm,i)):n(t.isComment)?(t.elm=I.createComment(t.text),p(o,t.elm,i)):(t.elm=I.createTextNode(t.text),p(o,t.elm,i))}}function u(t,r,o,i){var a=t.data;if(e(a)){var s=e(t.componentInstance)&&a.keepAlive;if(e(a=a.hook)&&e(a=a.init)&&a(t,!1,o,i),e(t.componentInstance))return l(t,r),n(s)&&d(t,r,o,i),!0}}function l(t,n){e(t.data.pendingInsert)&&(n.push.apply(n,t.data.pendingInsert),t.data.pendingInsert=null),t.elm=t.componentInstance.$el,h(t)?(m(t,n),y(t)):(qe(t),n.push(t))}function d(t,n,r,o){for(var i,a=t;a.componentInstance;)if(a=a.componentInstance._vnode,e(i=a.data)&&e(i=i.transition)){for(i=0;i<E.activate.length;++i)E.activate[i](Po,a);n.push(a);break}p(r,t.elm,o)}function p(t,n,r){e(t)&&(e(r)?r.parentNode===t&&I.insertBefore(t,n,r):I.appendChild(t,n))}function v(t,e,n){if(Array.isArray(e))for(var r=0;r<e.length;++r)c(e[r],n,t.elm,null,!0);else o(t.text)&&I.appendChild(t.elm,I.createTextNode(t.text))}function h(t){for(;t.componentInstance;)t=t.componentInstance._vnode;return e(t.tag)}function m(t,n){for(var r=0;r<E.create.length;++r)E.create[r](Po,t);e(O=t.data.hook)&&(e(O.create)&&O.create(Po,t),e(O.insert)&&n.push(t))}function y(t){var n;if(e(n=t.functionalScopeId))I.setAttribute(t.elm,n,"");else for(var r=t;r;)e(n=r.context)&&e(n=n.$options._scopeId)&&I.setAttribute(t.elm,n,""),r=r.parent;e(n=Qr)&&n!==t.context&&n!==t.functionalContext&&e(n=n.$options._scopeId)&&I.setAttribute(t.elm,n,"")}function _(t,e,n,r,o,i){for(;r<=o;++r)c(n[r],i,t,e)}function g(t){var n,r,o=t.data;if(e(o))for(e(n=o.hook)&&e(n=n.destroy)&&n(t),n=0;n<E.destroy.length;++n)E.destroy[n](t);if(e(n=t.children))for(r=0;r<t.children.length;++r)g(t.children[r])}function b(t,n,r,o){for(;r<=o;++r){var i=n[r];e(i)&&(e(i.tag)?(C(i),g(i)):s(i.elm))}}function C(t,n){if(e(n)||e(t.data)){var r,o=E.remove.length+1;for(e(n)?n.listeners+=o:n=a(t.elm,o),e(r=t.componentInstance)&&e(r=r._vnode)&&e(r.data)&&C(r,n),r=0;r<E.remove.length;++r)E.remove[r](t,n);e(r=t.data.hook)&&e(r=r.remove)?r(t,n):n()}else s(t.elm)}function w(n,r,o,i,a){for(var s,u,l,f=0,d=0,p=r.length-1,v=r[0],h=r[p],m=o.length-1,y=o[0],g=o[m],C=!a;f<=p&&d<=m;)t(v)?v=r[++f]:t(h)?h=r[--p]:Ke(v,y)?($(v,y,i),v=r[++f],y=o[++d]):Ke(h,g)?($(h,g,i),h=r[--p],g=o[--m]):Ke(v,g)?($(v,g,i),C&&I.insertBefore(n,v.elm,I.nextSibling(h.elm)),v=r[++f],g=o[--m]):Ke(h,y)?($(h,y,i),C&&I.insertBefore(n,h.elm,v.elm),h=r[--p],y=o[++d]):(t(s)&&(s=Je(r,f,p)),t(u=e(y.key)?s[y.key]:A(y,r,f,p))?c(y,i,n,v.elm):Ke(l=r[u],y)?($(l,y,i),r[u]=void 0,C&&I.insertBefore(n,l.elm,v.elm)):c(y,i,n,v.elm),y=o[++d]);f>p?_(n,t(o[m+1])?null:o[m+1].elm,o,d,m,i):d>m&&b(n,r,f,p)}function A(t,n,r,o){for(var i=r;i<o;i++){var a=n[i];if(e(a)&&Ke(t,a))return i}}function $(r,o,i,a){if(r!==o){var s=o.elm=r.elm;if(n(r.isAsyncPlaceholder))e(o.asyncFactory.resolved)?k(r.elm,o,i):o.isAsyncPlaceholder=!0;else if(n(o.isStatic)&&n(r.isStatic)&&o.key===r.key&&(n(o.isCloned)||n(o.isOnce)))o.componentInstance=r.componentInstance;else{var c,u=o.data;e(u)&&e(c=u.hook)&&e(c=c.prepatch)&&c(r,o);var l=r.children,f=o.children;if(e(u)&&h(o)){for(c=0;c<E.update.length;++c)E.update[c](r,o);e(c=u.hook)&&e(c=c.update)&&c(r,o)}t(o.text)?e(l)&&e(f)?l!==f&&w(s,l,f,i,a):e(f)?(e(r.text)&&I.setTextContent(s,""),_(s,null,f,0,f.length-1,i)):e(l)?b(s,l,0,l.length-1):e(r.text)&&I.setTextContent(s,""):r.text!==o.text&&I.setTextContent(s,o.text),e(u)&&e(c=u.hook)&&e(c=c.postpatch)&&c(r,o)}}}function x(t,r,o){if(n(o)&&e(t.parent))t.parent.data.pendingInsert=r;else for(var i=0;i<r.length;++i)r[i].data.hook.insert(r[i])}function k(t,r,o){if(n(r.isComment)&&e(r.asyncFactory))return r.elm=t,r.isAsyncPlaceholder=!0,!0;r.elm=t;var i=r.tag,a=r.data,s=r.children;if(e(a)&&(e(O=a.hook)&&e(O=O.init)&&O(r,!0),e(O=r.componentInstance)))return l(r,o),!0;if(e(i)){if(e(s))if(t.hasChildNodes())if(e(O=a)&&e(O=O.domProps)&&e(O=O.innerHTML)){if(O!==t.innerHTML)return!1}else{for(var c=!0,u=t.firstChild,f=0;f<s.length;f++){if(!u||!k(u,s[f],o)){c=!1;break}u=u.nextSibling}if(!c||u)return!1}else v(r,s,o);if(e(a))for(var d in a)if(!T(d)){m(r,o);break}}else t.data!==r.text&&(t.data=r.text);return!0}var O,S,E={},j=r.modules,I=r.nodeOps;for(O=0;O<No.length;++O)for(E[No[O]]=[],S=0;S<j.length;++S)e(j[S][No[O]])&&E[No[O]].push(j[S][No[O]]);var T=f("attrs,style,class,staticClass,staticStyle,key");return function(r,o,a,s,u,l){if(!t(o)){var f=!1,d=[];if(t(r))f=!0,c(o,d,u,l);else{var p=e(r.nodeType);if(!p&&Ke(r,o))$(r,o,d,s);else{if(p){if(1===r.nodeType&&r.hasAttribute(ir)&&(r.removeAttribute(ir),a=!0),n(a)&&k(r,o,d))return x(o,d,!0),r;r=i(r)}var v=r.elm,m=I.parentNode(v);if(c(o,d,v._leaveCb?null:m,I.nextSibling(v)),e(o.parent))for(var y=o.parent,_=h(o);y;){for(var C=0;C<E.destroy.length;++C)E.destroy[C](y);if(y.elm=o.elm,_){for(var w=0;w<E.create.length;++w)E.create[w](Po,y);var A=y.data.hook.insert;if(A.merged)for(var O=1;O<A.fns.length;O++)A.fns[O]()}else qe(y);y=y.parent}e(m)?b(m,[r],0,0):e(r.tag)&&g(r)}}return x(o,d,f),o.elm}e(r)&&g(r)}}({nodeOps:Do,modules:[Ro,Vo,zo,Wo,Xo,dr?{create:Tn,activate:Tn,remove:function(t,e){!0!==t.data.show?En(t,e):e()}}:{}].concat(Fo)});hr&&document.addEventListener("selectionchange",function(){var t=document.activeElement;t&&t.vmodel&&Fn(t,"input")});var li={inserted:function(t,e,n,r){"select"===n.tag?(r.elm&&!r.elm._vOptions?at(n,"postpatch",function(){li.componentUpdated(t,e,n)}):Dn(t,e,n.context),t._vOptions=[].map.call(t.options,Nn)):("textarea"===n.tag||To(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener("change",Un),yr||(t.addEventListener("compositionstart",Mn),t.addEventListener("compositionend",Un)),hr&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if("select"===n.tag){Dn(t,e,n.context);var r=t._vOptions,o=t._vOptions=[].map.call(t.options,Nn);o.some(function(t,e){return!b(t,r[e])})&&(t.multiple?e.value.some(function(t){return Pn(t,o)}):e.value!==e.oldValue&&Pn(e.value,o))&&Fn(t,"change")}}},fi={model:li,show:{bind:function(t,e,n){var r=e.value,o=(n=Rn(n)).data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,Sn(n,function(){t.style.display=i})):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value;r!==e.oldValue&&((n=Rn(n)).data&&n.data.transition?(n.data.show=!0,r?Sn(n,function(){t.style.display=t.__vOriginalDisplay}):En(n,function(){t.style.display="none"})):t.style.display=r?t.__vOriginalDisplay:"none")},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}}},di={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]},pi={name:"transition",props:di,abstract:!0,render:function(t){var e=this,n=this.$options._renderChildren;if(n&&(n=n.filter(function(t){return t.tag||mt(t)})).length){var r=this.mode,i=n[0];if(zn(this.$vnode))return i;var a=Vn(i);if(!a)return i;if(this._leaving)return Hn(t,i);var s="__transition-"+this._uid+"-";a.key=null==a.key?a.isComment?s+"comment":s+a.tag:o(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=Bn(this),u=this._vnode,l=Vn(u);if(a.data.directives&&a.data.directives.some(function(t){return"show"===t.name})&&(a.data.show=!0),l&&l.data&&!Wn(a,l)&&!mt(l)){var f=l.data.transition=y({},c);if("out-in"===r)return this._leaving=!0,at(f,"afterLeave",function(){e._leaving=!1,e.$forceUpdate()}),Hn(t,i);if("in-out"===r){if(mt(a))return u;var d,p=function(){d()};at(c,"afterEnter",p),at(c,"enterCancelled",p),at(f,"delayLeave",function(t){d=t})}}return i}}},vi=y({tag:String,moveClass:String},di);delete vi.mode;var hi={Transition:pi,TransitionGroup:{props:vi,render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=Bn(this),s=0;s<o.length;s++){var c=o[s];c.tag&&null!=c.key&&0!==String(c.key).indexOf("__vlist")&&(i.push(c),n[c.key]=c,(c.data||(c.data={})).transition=a)}if(r){for(var u=[],l=[],f=0;f<r.length;f++){var d=r[f];d.data.transition=a,d.data.pos=d.elm.getBoundingClientRect(),n[d.key]?u.push(d):l.push(d)}this.kept=t(e,null,u),this.removed=l}return t(e,null,i)},beforeUpdate:function(){this.__patch__(this._vnode,this.kept,!1,!0),this._vnode=this.kept},updated:function(){var t=this.prevChildren,e=this.moveClass||(this.name||"v")+"-move";t.length&&this.hasMove(t[0].elm,e)&&(t.forEach(qn),t.forEach(Kn),t.forEach(Gn),this._reflow=document.body.offsetHeight,t.forEach(function(t){if(t.data.moved){var n=t.elm,r=n.style;wn(n,e),r.transform=r.WebkitTransform=r.transitionDuration="",n.addEventListener(oi,n._moveCb=function t(r){r&&!/transform$/.test(r.propertyName)||(n.removeEventListener(oi,t),n._moveCb=null,An(n,e))})}}))},methods:{hasMove:function(t,e){if(!ti)return!1;if(this._hasMove)return this._hasMove;var n=t.cloneNode();t._transitionClasses&&t._transitionClasses.forEach(function(t){gn(n,t)}),_n(n,e),n.style.display="none",this.$el.appendChild(n);var r=xn(n);return this.$el.removeChild(n),this._hasMove=r.hasTransform}}}};return Oe.config.mustUseProp=function(t,e,n){return"value"===n&&bo(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},Oe.config.isReservedTag=jo,Oe.config.isReservedAttr=go,Oe.config.getTagNamespace=function(t){return Eo(t)?"svg":"math"===t?"math":void 0},Oe.config.isUnknownElement=function(t){if(!dr)return!0;if(jo(t))return!1;if(t=t.toLowerCase(),null!=Io[t])return Io[t];var e=document.createElement(t);return t.indexOf("-")>-1?Io[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Io[t]=/HTMLUnknownElement/.test(e.toString())},y(Oe.options.directives,fi),y(Oe.options.components,hi),Oe.prototype.__patch__=dr?ui:g,Oe.prototype.$mount=function(t,e){return t=t&&dr?We(t):void 0,kt(this,t,e)},Oe.nextTick(function(){cr.devtools&&xr&&xr.emit("init",Oe)},0),Oe}); \ No newline at end of file diff --git a/dist/vue.runtime.mjs b/dist/vue.runtime.mjs new file mode 100644 index 00000000000..83d889271bd --- /dev/null +++ b/dist/vue.runtime.mjs @@ -0,0 +1,76 @@ +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 new file mode 100644 index 00000000000..d15ec443cd9 --- /dev/null +++ b/examples/classic/commits/app.js @@ -0,0 +1,46 @@ +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/commits/index.html b/examples/classic/commits/index.html new file mode 100644 index 00000000000..10bc8d4d40a --- /dev/null +++ b/examples/classic/commits/index.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> + <head> + <title>Vue.js github commits example</title> + <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> + <!-- Delete ".min" for console warnings in development --> + <script src="../../../dist/vue.min.js"></script> + </head> + <body> + <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="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> + </li> + </ul> + </div> + <script src="app.js"></script> + </body> +</html> diff --git a/examples/classic/elastic-header/index.html b/examples/classic/elastic-header/index.html new file mode 100644 index 00000000000..095c1beeb20 --- /dev/null +++ b/examples/classic/elastic-header/index.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<html lang="en"> + <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> + <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"> + <div class="draggable-header-view" + @mousedown="startDrag" @touchstart="startDrag" + @mousemove="onDrag" @touchmove="onDrag" + @mouseup="stopDrag" @touchend="stopDrag" @mouseleave="stopDrag"> + <svg class="bg" width="320" height="560"> + <path :d="headerPath" fill="#3F51B5"></path> + </svg> + <div class="header"> + <slot name="header"></slot> + </div> + <div class="content" :style="contentPosition"> + <slot name="content"></slot> + </div> + </div> + </script> + </head> + <body> + + <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> + + <script> + Vue.component('draggable-header-view', { + template: '#header-view-template', + data: function () { + return { + dragging: false, + // quadratic bezier control point + c: { x: 160, y: 160 }, + // record drag start point + start: { x: 0, y: 0 } + } + }, + computed: { + headerPath: function () { + return 'M0,0 L320,0 320,160' + + 'Q' + this.c.x + ',' + this.c.y + + ' 0,160' + }, + contentPosition: function () { + var dy = this.c.y - 160 + var dampen = dy > 0 ? 2 : 4 + return { + transform: 'translate3d(0,' + dy / dampen + 'px,0)' + } + } + }, + methods: { + startDrag: function (e) { + e = e.changedTouches ? e.changedTouches[0] : e + this.dragging = true + this.start.x = e.pageX + this.start.y = e.pageY + }, + onDrag: function (e) { + e = e.changedTouches ? e.changedTouches[0] : e + if (this.dragging) { + this.c.x = 160 + (e.pageX - this.start.x) + // dampen vertical drag by a factor + var dy = e.pageY - this.start.y + var dampen = dy > 0 ? 1.5 : 4 + this.c.y = 160 + dy / dampen + } + }, + stopDrag: function () { + if (this.dragging) { + this.dragging = false + dynamics.animate(this.c, { + x: 160, + y: 160 + }, { + type: dynamics.spring, + duration: 700, + friction: 280 + }) + } + } + } + }) + + new Vue({ el: '#app' }) + </script> + </body> +</html> diff --git a/examples/elastic-header/style.css b/examples/classic/elastic-header/style.css similarity index 100% rename from examples/elastic-header/style.css rename to examples/classic/elastic-header/style.css diff --git a/examples/firebase/app.js b/examples/classic/firebase/app.js similarity index 100% rename from examples/firebase/app.js rename to examples/classic/firebase/app.js diff --git a/examples/classic/firebase/index.html b/examples/classic/firebase/index.html new file mode 100644 index 00000000000..f2d2f805f07 --- /dev/null +++ b/examples/classic/firebase/index.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <title>Vue.js firebase + validation 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> + <!-- Firebase --> + <script src="https://www.gstatic.com/firebasejs/3.4.0/firebase.js"></script> + <!-- VueFire --> + <script src="https://unpkg.com/vuefire@1.3.0"></script> + </head> + <body> + <div id="app"> + <ul is="transition-group"> + <li v-for="user in users" class="user" :key="user['.key']"> + <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 type="submit" value="Add User"> + </form> + <ul class="errors"> + <li v-show="!validation.name">Name cannot be empty.</li> + <li v-show="!validation.email">Please provide a valid email address.</li> + </ul> + </div> + <script src="app.js"></script> + </body> +</html> diff --git a/examples/firebase/style.css b/examples/classic/firebase/style.css similarity index 100% rename from examples/firebase/style.css rename to examples/classic/firebase/style.css diff --git a/examples/grid/grid.js b/examples/classic/grid/grid.js similarity index 100% rename from examples/grid/grid.js rename to examples/classic/grid/grid.js diff --git a/examples/classic/grid/index.html b/examples/classic/grid/index.html new file mode 100644 index 00000000000..41b07060afe --- /dev/null +++ b/examples/classic/grid/index.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <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> + </head> + <body> + + <!-- 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: sortKey == key }"> + {{ key | capitalize }} + <span class="arrow" :class="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> + + <!-- demo root element --> + <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> + + <script src="grid.js"></script> + + </body> +</html> diff --git a/examples/grid/style.css b/examples/classic/grid/style.css similarity index 100% rename from examples/grid/style.css rename to examples/classic/grid/style.css diff --git a/examples/classic/markdown/index.html b/examples/classic/markdown/index.html new file mode 100644 index 00000000000..bc77e29c07c --- /dev/null +++ b/examples/classic/markdown/index.html @@ -0,0 +1,39 @@ +<!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/markdown/style.css b/examples/classic/markdown/style.css similarity index 100% rename from examples/markdown/style.css rename to examples/classic/markdown/style.css diff --git a/examples/classic/modal/index.html b/examples/classic/modal/index.html new file mode 100644 index 00000000000..9acb7a42f69 --- /dev/null +++ b/examples/classic/modal/index.html @@ -0,0 +1,72 @@ +<!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/modal/style.css b/examples/classic/modal/style.css similarity index 100% rename from examples/modal/style.css rename to examples/classic/modal/style.css diff --git a/examples/classic/move-animations/index.html b/examples/classic/move-animations/index.html new file mode 100644 index 00000000000..e89d137b2b7 --- /dev/null +++ b/examples/classic/move-animations/index.html @@ -0,0 +1,93 @@ +<!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/select2/index.html b/examples/classic/select2/index.html similarity index 100% rename from examples/select2/index.html rename to examples/classic/select2/index.html diff --git a/examples/classic/svg/index.html b/examples/classic/svg/index.html new file mode 100644 index 00000000000..75d229638bb --- /dev/null +++ b/examples/classic/svg/index.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Vue.js SVG graph example</title> + <link rel="stylesheet" href="style.css"> + <!-- Delete ".min" for console warnings in development --> + <script src="../../../dist/vue.min.js"></script> + </head> + <body> + + <!-- 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> + + <!-- template for the axis label component. --> + <script type="text/x-template" id="axis-label-template"> + <text :x="point.x" :y="point.y">{{stat.label}}</text> + </script> + + <!-- demo root element --> + <div id="demo"> + <!-- Use the 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> + + <p style="font-size:12px">* input[type="range"] requires IE10 or above.</p> + + <script src="svg.js"></script> + + </body> +</html> diff --git a/examples/svg/style.css b/examples/classic/svg/style.css similarity index 100% rename from examples/svg/style.css rename to examples/classic/svg/style.css diff --git a/examples/classic/svg/svg.js b/examples/classic/svg/svg.js new file mode 100644 index 00000000000..c6df94582ef --- /dev/null +++ b/examples/classic/svg/svg.js @@ -0,0 +1,85 @@ +// The raw data to observe +var 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 } +] + +// A reusable polygon graph component +Vue.component('polygraph', { + props: ['stats'], + template: '#polygraph-template', + 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(' ') + } + }, + components: { + // a sub component for the labels + 'axis-label': { + props: { + stat: Object, + index: Number, + total: Number + }, + template: '#axis-label-template', + computed: { + point: function () { + return valueToPoint(+this.stat.value + 10, this.index, this.total) + } + } + } + } +}) + +// 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 + } +} + +// bootstrap the demo +new Vue({ + el: '#demo', + data: { + newLabel: '', + stats: globalStats + }, + methods: { + add: function (e) { + e.preventDefault() + if (!this.newLabel) return + this.stats.push({ + label: this.newLabel, + value: 100 + }) + this.newLabel = '' + }, + remove: function (stat) { + if (this.stats.length > 3) { + this.stats.splice(this.stats.indexOf(stat), 1) + } else { + alert("Can't delete more!") + } + } + } +}) diff --git a/examples/classic/todomvc/app.js b/examples/classic/todomvc/app.js new file mode 100644 index 00000000000..7a1a65486a3 --- /dev/null +++ b/examples/classic/todomvc/app.js @@ -0,0 +1,157 @@ +// 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 new file mode 100644 index 00000000000..3d9557edf99 --- /dev/null +++ b/examples/classic/todomvc/index.html @@ -0,0 +1,69 @@ +<!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/classic/todomvc/readme.md b/examples/classic/todomvc/readme.md new file mode 100644 index 00000000000..db6affe8af7 --- /dev/null +++ b/examples/classic/todomvc/readme.md @@ -0,0 +1,27 @@ +# Vue.js TodoMVC Example + +> 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)_ + +## Learning Vue.js +The [Vue.js website](https://v2.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/) + +Get help from other Vue.js users: + +* [Vue.js official forum](https://forum.vuejs.org) +* [Vue.js on Twitter](https://twitter.com/vuejs) +* [Vue.js on Gitter](https://gitter.im/vuejs/vue) + +_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ + +## Credit + +This TodoMVC application was created by [Evan You](https://evanyou.me). diff --git a/examples/classic/tree/index.html b/examples/classic/tree/index.html new file mode 100644 index 00000000000..a32ff272f75 --- /dev/null +++ b/examples/classic/tree/index.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Vue.js tree view example</title> + <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> + <!-- Delete ".min" for console warnings in development --> + <script src="../../../dist/vue.min.js"></script> + </head> + <body> + + <!-- 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-show="open" v-if="isFolder"> + <item + class="item" + v-for="model in model.children" + :model="model"> + </item> + <li class="add" @click="addChild">+</li> + </ul> + </li> + </script> + + <p>(You can double click on an item to turn it into a folder.)</p> + + <!-- the demo root element --> + <ul id="demo"> + <item + class="item" + :model="treeData"> + </item> + </ul> + + <!-- demo code --> + <script src="tree.js"></script> + </body> +</html> diff --git a/examples/tree/tree.js b/examples/classic/tree/tree.js similarity index 100% rename from examples/tree/tree.js rename to examples/classic/tree/tree.js diff --git a/examples/commits/app.js b/examples/commits/app.js deleted file mode 100644 index 029d660a538..00000000000 --- a/examples/commits/app.js +++ /dev/null @@ -1,47 +0,0 @@ -var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=' - -/** - * 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 () { - 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/commits/index.html b/examples/commits/index.html deleted file mode 100644 index 0b355a28748..00000000000 --- a/examples/commits/index.html +++ /dev/null @@ -1,47 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>Vue.js github commits example</title> - <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> - <!-- Delete ".min" for console warnings in development --> - <script src="../../dist/vue.min.js"></script> - </head> - <body> - <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="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> - </li> - </ul> - </div> - <script src="app.js"></script> - </body> -</html> diff --git a/examples/composition/commits.html b/examples/composition/commits.html new file mode 100644 index 00000000000..3c16a3f7bb8 --- /dev/null +++ b/examples/composition/commits.html @@ -0,0 +1,75 @@ +<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 new file mode 100644 index 00000000000..090d3d7dbad --- /dev/null +++ b/examples/composition/grid.html @@ -0,0 +1,173 @@ +<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 new file mode 100644 index 00000000000..d3387de4a43 --- /dev/null +++ b/examples/composition/markdown.html @@ -0,0 +1,66 @@ +<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 new file mode 100644 index 00000000000..2b4d5e0c316 --- /dev/null +++ b/examples/composition/svg.html @@ -0,0 +1,172 @@ +<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 new file mode 100644 index 00000000000..d95b39d2735 --- /dev/null +++ b/examples/composition/todomvc.html @@ -0,0 +1,241 @@ +<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 new file mode 100644 index 00000000000..c39fe3987b7 --- /dev/null +++ b/examples/composition/tree.html @@ -0,0 +1,124 @@ +<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/elastic-header/index.html b/examples/elastic-header/index.html deleted file mode 100644 index de6d6a2bfd1..00000000000 --- a/examples/elastic-header/index.html +++ /dev/null @@ -1,105 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <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> - <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"> - <div class="draggable-header-view" - @mousedown="startDrag" @touchstart="startDrag" - @mousemove="onDrag" @touchmove="onDrag" - @mouseup="stopDrag" @touchend="stopDrag" @mouseleave="stopDrag"> - <svg class="bg" width="320" height="560"> - <path :d="headerPath" fill="#3F51B5"></path> - </svg> - <div class="header"> - <slot name="header"></slot> - </div> - <div class="content" :style="contentPosition"> - <slot name="content"></slot> - </div> - </div> - </script> - </head> - <body> - - <div id="app" @touchmove.prevent> - <draggable-header-view> - <template slot="header"> - <h1>Elastic Draggable SVG Header</h1> - <p>with <a href="https://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> - - <script> - Vue.component('draggable-header-view', { - template: '#header-view-template', - data: function () { - return { - dragging: false, - // quadratic bezier control point - c: { x: 160, y: 160 }, - // record drag start point - start: { x: 0, y: 0 } - } - }, - computed: { - headerPath: function () { - return 'M0,0 L320,0 320,160' + - 'Q' + this.c.x + ',' + this.c.y + - ' 0,160' - }, - contentPosition: function () { - var dy = this.c.y - 160 - var dampen = dy > 0 ? 2 : 4 - return { - transform: 'translate3d(0,' + dy / dampen + 'px,0)' - } - } - }, - methods: { - startDrag: function (e) { - e = e.changedTouches ? e.changedTouches[0] : e - this.dragging = true - this.start.x = e.pageX - this.start.y = e.pageY - }, - onDrag: function (e) { - e = e.changedTouches ? e.changedTouches[0] : e - if (this.dragging) { - this.c.x = 160 + (e.pageX - this.start.x) - // dampen vertical drag by a factor - var dy = e.pageY - this.start.y - var dampen = dy > 0 ? 1.5 : 4 - this.c.y = 160 + dy / dampen - } - }, - stopDrag: function () { - if (this.dragging) { - this.dragging = false - dynamics.animate(this.c, { - x: 160, - y: 160 - }, { - type: dynamics.spring, - duration: 700, - friction: 280 - }) - } - } - } - }) - - new Vue({ el: '#app' }) - </script> - </body> -</html> diff --git a/examples/firebase/index.html b/examples/firebase/index.html deleted file mode 100644 index 5b1cf3d4a5e..00000000000 --- a/examples/firebase/index.html +++ /dev/null @@ -1,35 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <title>Vue.js firebase + validation 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> - <!-- Firebase --> - <script src="https://www.gstatic.com/firebasejs/3.4.0/firebase.js"></script> - <!-- VueFire --> - <script src="https://unpkg.com/vuefire@1.3.0"></script> - </head> - <body> - <div id="app"> - <ul is="transition-group"> - <li v-for="user in users" class="user" :key="user['.key']"> - <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"> - <input v-model="newUser.email"> - <input type="submit" value="Add User"> - </form> - <ul class="errors"> - <li v-show="!validation.name">Name cannot be empty.</li> - <li v-show="!validation.email">Please provide a valid email address.</li> - </ul> - </div> - <script src="app.js"></script> - </body> -</html> diff --git a/examples/grid/index.html b/examples/grid/index.html deleted file mode 100644 index d668cd76cc5..00000000000 --- a/examples/grid/index.html +++ /dev/null @@ -1,52 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <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> - </head> - <body> - - <!-- 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: sortKey == key }"> - {{ key | capitalize }} - <span class="arrow" :class="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> - - <!-- demo root element --> - <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> - - <script src="grid.js"></script> - - </body> -</html> diff --git a/examples/markdown/index.html b/examples/markdown/index.html deleted file mode 100644 index 9ca987466ba..00000000000 --- a/examples/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="https://unpkg.com/marked@0.3.6"></script> - <script src="https://unpkg.com/lodash@4.16.0"></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(this.input, { sanitize: true }) - } - }, - methods: { - update: _.debounce(function (e) { - this.input = e.target.value - }, 300) - } - }) - </script> - - </body> -</html> diff --git a/examples/modal/index.html b/examples/modal/index.html deleted file mode 100644 index e2121617646..00000000000 --- a/examples/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"> - <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/move-animations/index.html b/examples/move-animations/index.html deleted file mode 100644 index 46819adfd5f..00000000000 --- a/examples/move-animations/index.html +++ /dev/null @@ -1,88 +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 .5s cubic-bezier(.55,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="https://cdn.jsdelivr.net/lodash/4.3.0/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/svg/index.html b/examples/svg/index.html deleted file mode 100644 index e50fc80084f..00000000000 --- a/examples/svg/index.html +++ /dev/null @@ -1,57 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="utf-8"> - <title>Vue.js SVG graph example</title> - <link rel="stylesheet" href="style.css"> - <!-- Delete ".min" for console warnings in development --> - <script src="../../dist/vue.min.js"></script> - <script src="https://unpkg.com/marky/dist/marky.min.js"></script> - </head> - <body> - - <!-- 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> - - <!-- template for the axis label component. --> - <script type="text/x-template" id="axis-label-template"> - <text :x="point.x" :y="point.y">{{stat.label}}</text> - </script> - - <!-- demo root element --> - <div id="demo"> - <!-- Use the 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> - - <p style="font-size:12px">* input[type="range"] requires IE10 or above.</p> - - <script src="svg.js"></script> - - </body> -</html> diff --git a/examples/svg/svg.js b/examples/svg/svg.js deleted file mode 100644 index f4491ce3088..00000000000 --- a/examples/svg/svg.js +++ /dev/null @@ -1,87 +0,0 @@ -// The raw data to observe -var stats = [ - { label: 'A', value: 100 }, - { label: 'B', value: 100 }, - { label: 'C', value: 100 }, - { label: 'D', value: 100 }, - { label: 'E', value: 100 }, - { label: 'F', value: 100 } -] - -// A reusable polygon graph component -Vue.component('polygraph', { - props: ['stats'], - template: '#polygraph-template', - 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(' ') - } - }, - components: { - // a sub component for the labels - 'axis-label': { - props: { - stat: Object, - index: Number, - total: Number - }, - template: '#axis-label-template', - computed: { - point: function () { - return valueToPoint( - +this.stat.value + 10, - this.index, - this.total - ) - } - } - } - } -}) - -// 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 - } -} - -// bootstrap the demo -new Vue({ - el: '#demo', - data: { - newLabel: '', - stats: stats - }, - methods: { - add: function (e) { - e.preventDefault() - if (!this.newLabel) return - this.stats.push({ - label: this.newLabel, - value: 100 - }) - this.newLabel = '' - }, - remove: function (stat) { - if (this.stats.length > 3) { - this.stats.splice(this.stats.indexOf(stat), 1) - } else { - alert('Can\'t delete more!') - } - } - } -}) diff --git a/examples/todomvc/app.js b/examples/todomvc/app.js deleted file mode 100644 index f8bfeb1ea61..00000000000 --- a/examples/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://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 - }) - } - } - }, - - 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://vuejs.org/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/todomvc/index.html b/examples/todomvc/index.html deleted file mode 100644 index edf28d84728..00000000000 --- a/examples/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="https://unpkg.com/todomvc-app-css@2.0.4/index.css"> - <script src="https://unpkg.com/director@1.2.8/build/director.js"></script> - <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 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="http://evanyou.me">Evan You</a></p> - <p>Part of <a href="http://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/todomvc/readme.md b/examples/todomvc/readme.md deleted file mode 100644 index 3155b80944d..00000000000 --- a/examples/todomvc/readme.md +++ /dev/null @@ -1,27 +0,0 @@ -# Vue.js TodoMVC Example - -> 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 - vuejs.org](https://vuejs.org)_ - -## Learning Vue.js -The [Vue.js website](https://vuejs.org/) is a great resource to get started. - -Here are some links you may find helpful: - -* [Official Guide](https://vuejs.org/guide/) -* [API Reference](https://vuejs.org/api/) -* [Examples](https://vuejs.org/examples/) - -Get help from other Vue.js users: - -* [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) - -_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ - -## Credit - -This TodoMVC application was created by [Evan You](http://evanyou.me). diff --git a/examples/tree/index.html b/examples/tree/index.html deleted file mode 100644 index bfb9db4a95e..00000000000 --- a/examples/tree/index.html +++ /dev/null @@ -1,62 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="utf-8"> - <title>Vue.js tree view example</title> - <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> - <!-- Delete ".min" for console warnings in development --> - <script src="../../dist/vue.min.js"></script> - </head> - <body> - - <!-- 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-show="open" v-if="isFolder"> - <item - class="item" - v-for="model in model.children" - :model="model"> - </item> - <li class="add" @click="addChild">+</li> - </ul> - </li> - </script> - - <p>(You can double click on an item to turn it into a folder.)</p> - - <!-- the demo root element --> - <ul id="demo"> - <item - class="item" - :model="treeData"> - </item> - </ul> - - <!-- demo code --> - <script src="tree.js"></script> - </body> -</html> diff --git a/flow/compiler.js b/flow/compiler.js deleted file mode 100644 index a5ebc5f3862..00000000000 --- a/flow/compiler.js +++ /dev/null @@ -1,197 +0,0 @@ -declare type CompilerOptions = { - warn?: Function; // allow customizing warning in different environments; e.g. node - expectHTML?: boolean; // only false for non-web builds - modules?: Array<ModuleOptions>; // platform specific modules; e.g. style; class - staticKeys?: string; // a list of AST properties to be considered static; for optimization - directives?: { [key: string]: Function }; // platform specific directives - isUnaryTag?: (tag: string) => ?boolean; // check if a tag is unary for the platform - canBeLeftOpenTag?: (tag: string) => ?boolean; // check if a tag can be left opened - isReservedTag?: (tag: string) => ?boolean; // check if a tag is a native for the platform - mustUseProp?: (tag: string, type: ?string, name: string) => boolean; // check if an attribute should be bound as a property - isPreTag?: (attr: string) => ?boolean; // check if a tag needs to preserve whitespace - getTagNamespace?: (tag: string) => ?string; // check the namespace for a tag - transforms?: Array<Function>; // a list of transforms on parsed AST before codegen - preserveWhitespace?: boolean; - isFromDOM?: boolean; - shouldDecodeTags?: boolean; - shouldDecodeNewlines?: boolean; - shouldDecodeNewlinesForHref?: boolean; - - // for ssr optimization compiler - scopeId?: string; - - // runtime user-configurable - delimiters?: [string, string]; // template delimiters - - // allow user kept comments - comments?: boolean -}; - -declare type CompiledResult = { - ast: ?ASTElement; - render: string; - staticRenderFns: Array<string>; - stringRenderFns?: Array<string>; - errors?: Array<string>; - tips?: Array<string>; -}; - -declare type ModuleOptions = { - // returning an ASTElement from pre/transforms replaces the element - preTransformNode: (el: ASTElement) => ?ASTElement; - transformNode: (el: ASTElement) => ?ASTElement; - // 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 -}; - -declare type ASTModifiers = { [key: string]: boolean }; -declare type ASTIfCondition = { exp: ?string; block: ASTElement }; -declare type ASTIfConditions = Array<ASTIfCondition>; - -declare type ASTElementHandler = { - value: string; - modifiers: ?ASTModifiers; -}; - -declare type ASTElementHandlers = { - [key: string]: ASTElementHandler | Array<ASTElementHandler>; -}; - -declare type ASTDirective = { - name: string; - rawName: string; - value: string; - arg: ?string; - modifiers: ?ASTModifiers; -}; - -declare type ASTNode = ASTElement | ASTText | ASTExpression; - -declare type ASTElement = { - type: 1; - tag: string; - attrsList: Array<{ name: string; value: string }>; - attrsMap: { [key: string]: string | null }; - parent: ASTElement | void; - children: Array<ASTNode>; - - processed?: true; - - static?: boolean; - staticRoot?: boolean; - staticInFor?: boolean; - staticProcessed?: boolean; - hasBindings?: boolean; - - text?: string; - attrs?: Array<{ name: string; value: string }>; - props?: Array<{ name: string; value: string }>; - plain?: boolean; - pre?: true; - ns?: string; - - component?: string; - inlineTemplate?: true; - transitionMode?: string | null; - slotName?: ?string; - slotTarget?: ?string; - slotScope?: ?string; - scopedSlots?: { [name: string]: ASTElement }; - - ref?: string; - refInFor?: boolean; - - if?: string; - ifProcessed?: boolean; - elseif?: string; - else?: true; - ifConditions?: ASTIfConditions; - - for?: string; - forProcessed?: boolean; - key?: string; - alias?: string; - iterator1?: string; - iterator2?: string; - - staticClass?: string; - classBinding?: string; - staticStyle?: string; - styleBinding?: string; - events?: ASTElementHandlers; - nativeEvents?: ASTElementHandlers; - - transition?: string | true; - transitionOnAppear?: boolean; - - model?: { - value: string; - callback: string; - expression: string; - }; - - directives?: Array<ASTDirective>; - - forbidden?: true; - once?: true; - onceProcessed?: boolean; - wrapData?: (code: string) => string; - wrapListeners?: (code: string) => string; - - // 2.4 ssr optimization - ssrOptimizability?: number; - - // weex specific - appendAsTree?: boolean; -}; - -declare type ASTExpression = { - type: 2; - expression: string; - text: string; - static?: boolean; - // 2.4 ssr optimization - ssrOptimizability?: number; -}; - -declare type ASTText = { - type: 3; - text: string; - static?: boolean; - isComment?: boolean; - // 2.4 ssr optimization - ssrOptimizability?: number; -}; - -// SFC-parser related declarations - -// an object format describing a single-file component. -declare type SFCDescriptor = { - template: ?SFCBlock; - script: ?SFCBlock; - styles: Array<SFCBlock>; - customBlocks: Array<SFCCustomBlock>; -} - -declare type SFCCustomBlock = { - type: string; - content: string; - start?: number; - end?: number; - src?: string; - attrs: {[attribute:string]: string}; -}; - -declare type SFCBlock = { - type: string; - content: string; - start?: number; - end?: number; - lang?: string; - src?: string; - scoped?: boolean; - module?: string | boolean; -}; diff --git a/flow/component.js b/flow/component.js deleted file mode 100644 index 5286944553c..00000000000 --- a/flow/component.js +++ /dev/null @@ -1,144 +0,0 @@ -import type { Config } from '../src/core/config' -import type VNode from '../src/core/vdom/vnode' -import type Watcher from '../src/core/observer/watcher' - -declare interface Component { - // constructor information - static cid: number; - static options: Object; - // extend - static extend: (options: Object) => Function; - static superOptions: Object; - static extendOptions: Object; - static sealedOptions: Object; - static super: Class<Component>; - // assets - static directive: (id: string, def?: Function | Object) => Function | Object | void; - static component: (id: string, def?: Class<Component> | Object) => Class<Component>; - static filter: (id: string, def?: Function) => Function | void; - - // public properties - $el: any; // so that we can attach __vue__ to it - $data: Object; - $props: Object; - $options: ComponentOptions; - $parent: Component | void; - $root: Component; - $children: Array<Component>; - $refs: { [key: string]: Component | Element | Array<Component | Element> | void }; - $slots: { [key: string]: Array<VNode> }; - $scopedSlots: { [key: string]: () => VNodeChildren }; - $vnode: VNode; // the placeholder node for the component in parent's render tree - $attrs: { [key: string] : string }; - $listeners: { [key: string]: Function | Array<Function> }; - $isServer: boolean; - - // public methods - $mount: (el?: Element | string, hydrating?: boolean) => Component; - $forceUpdate: () => void; - $destroy: () => void; - $set: <T>(target: Object | Array<T>, key: string | number, val: T) => T; - $delete: <T>(target: Object | Array<T>, key: string | number) => void; - $watch: (expOrFn: string | Function, cb: Function, options?: Object) => 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<mixed>) => Component; - $nextTick: (fn: Function) => void | Promise<*>; - $createElement: (tag?: string | Component, data?: Object, children?: VNodeChildren) => VNode; - - // private properties - _uid: number; - _name: string; // this only exists in dev mode - _isVue: true; - _self: Component; - _renderProxy: Component; - _renderContext: ?Component; - _watcher: Watcher; - _watchers: Array<Watcher>; - _computedWatchers: { [key: string]: Watcher }; - _data: Object; - _props: Object; - _events: Object; - _inactive: boolean | null; - _directInactive: boolean; - _isMounted: boolean; - _isDestroyed: boolean; - _isBeingDestroyed: boolean; - _vnode: ?VNode; // self root node - _hasHookEvent: boolean; - _provided: ?Object; - - // private methods - - // lifecycle - _init: Function; - _mount: (el?: Element | void, hydrating?: boolean) => Component; - _update: (vnode: VNode, hydrating?: boolean) => void; - - // rendering - _render: () => VNode; - - __patch__: ( - a: Element | VNode | void, - b: VNode, - 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: mixed) => string; - // text to VNode - _v: (value: string | number) => VNode; - // toNumber - _n: (value: string) => number | string; - // empty vnode - _e: () => VNode; - // loose equal - _q: (a: mixed, b: mixed) => boolean; - // loose indexOf - _i: (arr: Array<mixed>, val: mixed) => number; - // resolveFilter - _f: (id: string) => Function; - // renderList - _l: (val: mixed, render: Function) => ?Array<VNode>; - // renderSlot - _t: (name: string, fallback: ?Array<VNode>, props: ?Object) => ?Array<VNode>; - // 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; - // resolve scoped slots - _u: (scopedSlots: ScopedSlotsData, res?: Object) => { [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/flow/global-api.js b/flow/global-api.js deleted file mode 100644 index 0b2efb23ac3..00000000000 --- a/flow/global-api.js +++ /dev/null @@ -1,21 +0,0 @@ -declare interface GlobalAPI { - cid: number; - options: Object; - config: Config; - util: Object; - - extend: (options: Object) => Function; - 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<*>; - use: (plugin: Function | Object) => void; - mixin: (mixin: Object) => void; - compile: (template: string) => { render: Function, staticRenderFns: Array<Function> }; - - directive: (id: string, def?: Function | Object) => Function | Object | void; - component: (id: string, def?: Class<Component> | Object) => Class<Component>; - filter: (id: string, def?: Function) => Function | void; - - // allow dynamic method registration - [key: string]: any -}; diff --git a/flow/modules.js b/flow/modules.js deleted file mode 100644 index a660e9c9101..00000000000 --- a/flow/modules.js +++ /dev/null @@ -1,44 +0,0 @@ -declare module 'he' { - declare function escape(html: string): string; - declare function decode(html: string): string; -} - -declare module 'source-map' { - declare class SourceMapGenerator { - setSourceContent(filename: string, content: string): void; - addMapping(mapping: Object): void; - toString(): string; - } - declare class SourceMapConsumer { - constructor (map: Object): void; - originalPositionFor(position: { line: number; column: number; }): { - source: ?string; - line: ?number; - column: ?number; - }; - } -} - -declare module 'lru-cache' { - declare var exports: { - (): any - } -} - -declare module 'de-indent' { - declare var exports: { - (input: string): string - } -} - -declare module 'serialize-javascript' { - declare var exports: { - (input: string, options: { isJSON: boolean }): string - } -} - -declare module 'lodash.template' { - declare var exports: { - (input: string, options: { interpolate: RegExp, escape: RegExp }): Function - } -} diff --git a/flow/options.js b/flow/options.js deleted file mode 100644 index aa87fceb836..00000000000 --- a/flow/options.js +++ /dev/null @@ -1,95 +0,0 @@ -declare type InternalComponentOptions = { - _isComponent: true; - parent: Component; - propsData: ?Object; - _parentVnode: VNode; - _parentListeners: ?Object; - _renderChildren: ?Array<VNode>; - _componentTag: ?string; - _parentElm: ?Node; - _refElm: ?Node; - render?: Function; - staticRenderFns?: Array<Function> -}; - -type InjectKey = string | Symbol; - -declare type ComponentOptions = { - // data - data: Object | Function | void; - props?: { [key: string]: PropOptions }; - propsData?: ?Object; - computed?: { - [key: string]: Function | { - get?: Function; - set?: Function; - cache?: boolean - } - }; - methods?: { [key: string]: Function }; - watch?: { [key: string]: Function | string }; - - // DOM - el?: string | Element; - template?: string; - render: (h: () => VNode) => VNode; - renderError?: (h: () => VNode, err: Error) => VNode; - staticRenderFns?: Array<() => VNode>; - - // lifecycle - beforeCreate?: Function; - created?: Function; - beforeMount?: Function; - mounted?: Function; - beforeUpdate?: Function; - updated?: Function; - activated?: Function; - deactivated?: Function; - beforeDestroy?: Function; - destroyed?: Function; - errorCaptured?: () => boolean | void; - - // assets - directives?: { [key: string]: Object }; - components?: { [key: string]: Class<Component> }; - transitions?: { [key: string]: Object }; - filters?: { [key: string]: Function }; - - // context - provide?: { [key: string | Symbol]: any } | () => { [key: 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?: Class<Component> | Object; - delimiters?: [string, string]; - comments?: boolean; - inheritAttrs?: boolean; - - // private - _isComponent?: true; - _propKeys?: Array<string>; - _parentVnode?: VNode; - _parentListeners?: ?Object; - _renderChildren?: ?Array<VNode>; - _componentTag: ?string; - _scopeId: ?string; - _base: Class<Component>; - _parentElm: ?Node; - _refElm: ?Node; -}; - -declare type PropOptions = { - type: Function | Array<Function> | null; - default: any; - required: ?boolean; - validator: ?Function; -} diff --git a/flow/ssr.js b/flow/ssr.js deleted file mode 100644 index d6a94592454..00000000000 --- a/flow/ssr.js +++ /dev/null @@ -1,21 +0,0 @@ -declare type ComponentWithCacheContext = { - type: 'ComponentWithCache'; - bufferIndex: number; - buffer: Array<string>; - key: string; -}; - -declare type ElementContext = { - type: 'Element'; - children: Array<VNode>; - rendered: number; - endTag: string; - total: number; -}; - -declare type ComponentContext = { - type: 'Component'; - prevActive: Component; -}; - -declare type RenderState = ComponentContext | ComponentWithCacheContext | ElementContext; diff --git a/flow/vnode.js b/flow/vnode.js deleted file mode 100644 index 46fe3deac67..00000000000 --- a/flow/vnode.js +++ /dev/null @@ -1,78 +0,0 @@ -declare type VNodeChildren = Array<?VNode | string | VNodeChildren> | string; - -declare type VNodeComponentOptions = { - Ctor: Class<Component>; - propsData: ?Object; - listeners: ?Object; - children: ?Array<VNode>; - tag?: string; -}; - -declare type MountedComponentVNode = { - context: Component; - componentOptions: VNodeComponentOptions; - componentInstance: Component; - parent: VNode; - data: VNodeData; -}; - -// interface for vnodes in update modules -declare type VNodeWithData = { - tag: string; - data: VNodeData; - children: ?Array<VNode>; - text: void; - elm: any; - ns: string | void; - context: Component; - key: string | number | void; - parent?: VNodeWithData; - componentOptions?: VNodeComponentOptions; - componentInstance?: Component; - isRootInsert: boolean; -}; - -declare interface VNodeData { - key?: string | number; - slot?: string; - ref?: string; - is?: string; - pre?: boolean; - tag?: string; - staticClass?: string; - class?: any; - staticStyle?: { [key: string]: any }; - style?: 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; - }; -}; - -declare type VNodeDirective = { - name: string; - rawName: string; - value?: any; - oldValue?: any; - arg?: string; - modifiers?: ASTModifiers; - def?: Object; -}; - -declare type ScopedSlotsData = Array<{ key: string, fn: Function } | ScopedSlotsData>; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 954108e74e0..00000000000 --- a/package-lock.json +++ /dev/null @@ -1,9412 +0,0 @@ -{ - "name": "vue", - "version": "2.5.3", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/node": { - "version": "8.0.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.33.tgz", - "integrity": "sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==", - "dev": true - }, - "@types/source-map": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.1.tgz", - "integrity": "sha512-/GVAjL1Y8puvZab63n8tsuBiYwZt1bApMdx58/msQ9ID5T05ov+wm/ZV1DvYC/DKKEygpTJViqQvkh5Rhrl4CA==", - "dev": true - }, - "@types/tapable": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-0.2.4.tgz", - "integrity": "sha512-pclMAvhPnXJcJu1ZZ8bQthuUcdDWzDuxDdbSf6l1U6s4fP6EBiZpPsOZYqFOrbqDV97sXGFSsb6AUpiLfv4xIA==", - "dev": true - }, - "@types/uglify-js": { - "version": "2.6.29", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-2.6.29.tgz", - "integrity": "sha512-BdFLCZW0GTl31AbqXSak8ss/MqEZ3DN2MH9rkAyGoTuzK7ifGUlX+u0nfbWeTsa7IPcZhtn8BlpYBXSV+vqGhQ==", - "dev": true, - "requires": { - "@types/source-map": "0.5.1" - } - }, - "@types/webpack": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-3.0.13.tgz", - "integrity": "sha512-gWVIAVaGapOPHOrjUQalUqdJWdUIn8w5gpKzz2L14megpB1euvq4HxvGusp9nhtkYGjD/sIg20Zse42b7+7BDw==", - "dev": true, - "requires": { - "@types/node": "8.0.33", - "@types/tapable": "0.2.4", - "@types/uglify-js": "2.6.29" - } - }, - "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true, - "requires": { - "mime-types": "2.1.16", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "4.0.13" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "3.3.0" - } - }, - "acorn-object-spread": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/acorn-object-spread/-/acorn-object-spread-1.0.0.tgz", - "integrity": "sha1-SOrQ9KjrFplaF6Dbn/xqyq2kumg=", - "dev": true, - "requires": { - "acorn": "3.3.0" - } - }, - "adm-zip": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=", - "dev": true - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", - "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", - "dev": true, - "requires": { - "extend": "3.0.1", - "semver": "5.0.3" - }, - "dependencies": { - "semver": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", - "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", - "dev": true - } - } - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - } - }, - "archiver": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz", - "integrity": "sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI=", - "dev": true, - "requires": { - "archiver-utils": "1.3.0", - "async": "2.5.0", - "buffer-crc32": "0.2.13", - "glob": "7.1.2", - "lodash": "4.17.4", - "readable-stream": "2.3.3", - "tar-stream": "1.5.4", - "walkdir": "0.0.11", - "zip-stream": "1.2.0" - }, - "dependencies": { - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - } - } - }, - "archiver-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz", - "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=", - "dev": true, - "requires": { - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lazystream": "1.0.0", - "lodash": "4.17.4", - "normalize-path": "2.1.1", - "readable-stream": "2.3.3" - } - }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "asn1.js": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", - "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true - }, - "assertion-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", - "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", - "dev": true - }, - "ast-types": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.12.tgz", - "integrity": "sha1-sTYwDWcCZiWuFTJpgsqZGOXbc8k=", - "dev": true - }, - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "dev": true - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.0", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.0", - "debug": "2.6.8", - "json5": "0.5.1", - "lodash": "4.17.4", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.7", - "slash": "1.0.0", - "source-map": "0.5.7" - } - }, - "babel-eslint": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", - "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0" - } - }, - "babel-generator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", - "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.4", - "source-map": "0.5.7", - "trim-right": "1.0.1" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-vue-jsx-merge-props": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.2.tgz", - "integrity": "sha1-rOscNzWIJ54nVeoc/TXCI5T9M/g=", - "dev": true - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-loader": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.2.tgz", - "integrity": "sha512-jRwlFbINAeyDStqK6Dd5YuY0k5YuzQUvlz2ZamuXrXmxav3pNqe9vfJ402+2G+OmlJSXxCOpB6Uz0INM7RQe2A==", - "dev": true, - "requires": { - "find-cache-dir": "1.0.0", - "loader-utils": "1.1.0", - "mkdirp": "0.5.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz", - "integrity": "sha1-GN3oS/POMp/d8/QQP66SFFbY5Yc=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "istanbul-lib-instrument": "1.7.5", - "test-exclude": "4.1.1" - } - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true - }, - "babel-plugin-syntax-flow": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", - "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", - "dev": true - }, - "babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", - "dev": true - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-plugin-syntax-class-properties": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "6.26.0", - "babel-helper-function-name": "6.24.1", - "babel-helper-optimise-call-expression": "6.24.1", - "babel-helper-replace-supers": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", - "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "6.24.1", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "6.24.1", - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "regexpu-core": "2.0.0" - } - }, - "babel-plugin-transform-flow-strip-types": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", - "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", - "dev": true, - "requires": { - "babel-plugin-syntax-flow": "6.18.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "0.10.1" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-vue-jsx": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-3.5.0.tgz", - "integrity": "sha512-5vCg8K7aiiLwrFJ45ZF/b4cIiFpGAoYL5uNZpbgiZFptBc5LkueBCQXTVexrd1IFlpTV7XndqFjtWjcJ54JGUQ==", - "dev": true, - "requires": { - "esutils": "2.0.2" - } - }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0" - } - }, - "babel-preset-flow-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-flow-vue/-/babel-preset-flow-vue-1.0.0.tgz", - "integrity": "sha1-vSjrZLU9ZfnEcmKzLObGAz/nBnE=", - "dev": true, - "requires": { - "babel-plugin-syntax-flow": "6.18.0", - "babel-plugin-transform-class-properties": "6.24.1", - "babel-plugin-transform-flow-strip-types": "6.22.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.1", - "home-or-tmp": "2.0.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "source-map-support": "0.4.16" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.8", - "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.4", - "to-fast-properties": "1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", - "dev": true - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "big.js": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", - "integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=", - "dev": true - }, - "binary-extensions": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", - "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", - "dev": true - }, - "bl": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", - "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", - "dev": true, - "requires": { - "readable-stream": "1.0.34" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", - "dev": true - }, - "bluebird": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", - "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.17.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.17.2.tgz", - "integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=", - "dev": true, - "requires": { - "bytes": "2.4.0", - "content-type": "1.0.2", - "debug": "2.6.7", - "depd": "1.1.1", - "http-errors": "1.6.2", - "iconv-lite": "0.4.15", - "on-finished": "2.3.0", - "qs": "6.4.0", - "raw-body": "2.2.0", - "type-is": "1.6.15" - }, - "dependencies": { - "debug": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", - "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-resolve": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", - "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true - }, - "browserify-aes": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", - "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=", - "dev": true, - "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.2", - "inherits": "2.0.3" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "dev": true, - "requires": { - "browserify-aes": "1.0.6", - "browserify-des": "1.0.0", - "evp_bytestokey": "1.0.2" - } - }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.5" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.0" - } - }, - "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true, - "requires": { - "pako": "0.2.9" - } - }, - "buble": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/buble/-/buble-0.16.0.tgz", - "integrity": "sha512-Eb5vt1+IvXXPyYD1IIQIuaBwIuJOSWQ2kXzULlg5I83aLGF2qzcjRU2joYusnWFgAenvJ9xTOMvZvT0bb8BLbg==", - "dev": true, - "requires": { - "acorn": "3.3.0", - "acorn-jsx": "3.0.1", - "acorn-object-spread": "1.0.0", - "chalk": "1.1.3", - "magic-string": "0.14.0", - "minimist": "1.2.0", - "os-homedir": "1.0.2", - "vlq": "0.2.2" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8", - "isarray": "1.0.0" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", - "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", - "dev": true - }, - "cachedir": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-1.1.1.tgz", - "integrity": "sha1-4TYwdeogahJ2fZK7cRyKL3ahD2I=", - "dev": true, - "requires": { - "os-homedir": "1.0.2" - } - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "0.2.0" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - } - }, - "chai-nightwatch": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.1.1.tgz", - "integrity": "sha1-HKVt52jTwIaP5/wvTTLC/olOa+k=", - "dev": true, - "requires": { - "assertion-error": "1.0.0", - "deep-eql": "0.1.3" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.1.2", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "chromedriver": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.32.0.tgz", - "integrity": "sha512-75r5nVhCLJybAyk+2VStFXpoUvEh+M/QmvrewUZSjiwrUyRfZ/3poRyysbsVDL6GjUhpD7sGrR252Y9Ab5i67Q==", - "dev": true, - "requires": { - "extract-zip": "1.6.5", - "kew": "0.7.0", - "mkdirp": "0.5.1", - "request": "2.81.0", - "rimraf": "2.6.1" - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "codecov.io": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/codecov.io/-/codecov.io-0.1.6.tgz", - "integrity": "sha1-Wd/QLaH/McL7K5Uq2K0W/TeBtyg=", - "dev": true, - "requires": { - "request": "2.42.0", - "urlgrey": "0.4.0" - }, - "dependencies": { - "asn1": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", - "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", - "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", - "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", - "dev": true, - "optional": true - }, - "boom": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", - "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", - "dev": true, - "requires": { - "hoek": "0.9.1" - } - }, - "caseless": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.6.0.tgz", - "integrity": "sha1-gWfBq4OX+1u5X5bSjlqBxQ8kesQ=", - "dev": true - }, - "combined-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", - "dev": true, - "optional": true, - "requires": { - "delayed-stream": "0.0.5" - } - }, - "cryptiles": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", - "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", - "dev": true, - "optional": true, - "requires": { - "boom": "0.4.2" - } - }, - "delayed-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", - "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", - "dev": true, - "optional": true - }, - "forever-agent": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", - "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=", - "dev": true - }, - "form-data": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", - "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", - "dev": true, - "optional": true, - "requires": { - "async": "0.9.2", - "combined-stream": "0.0.7", - "mime": "1.2.11" - } - }, - "hawk": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", - "integrity": "sha1-h81JH5tG5OKurKM1QWdmiF0tHtk=", - "dev": true, - "optional": true, - "requires": { - "boom": "0.4.2", - "cryptiles": "0.2.2", - "hoek": "0.9.1", - "sntp": "0.2.4" - } - }, - "hoek": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", - "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=", - "dev": true - }, - "http-signature": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", - "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", - "dev": true, - "optional": true, - "requires": { - "asn1": "0.1.11", - "assert-plus": "0.1.5", - "ctype": "0.5.3" - } - }, - "mime-types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz", - "integrity": "sha1-mVrhOSq4r/y/yyZB3QVOlDwNXc4=", - "dev": true - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "dev": true - }, - "oauth-sign": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.4.0.tgz", - "integrity": "sha1-8ilW8x6nFRqCHl8vsywRPK2Ln2k=", - "dev": true, - "optional": true - }, - "qs": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz", - "integrity": "sha1-GbV/8k3CqZzh+L32r82ln472H4g=", - "dev": true - }, - "request": { - "version": "2.42.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.42.0.tgz", - "integrity": "sha1-VyvQFIk4VkBArHqxSLlkI6BjMEo=", - "dev": true, - "requires": { - "aws-sign2": "0.5.0", - "bl": "0.9.5", - "caseless": "0.6.0", - "forever-agent": "0.5.2", - "form-data": "0.1.4", - "hawk": "1.1.1", - "http-signature": "0.10.1", - "json-stringify-safe": "5.0.1", - "mime-types": "1.0.2", - "node-uuid": "1.4.8", - "oauth-sign": "0.4.0", - "qs": "1.2.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.4.3" - } - }, - "sntp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", - "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", - "dev": true, - "optional": true, - "requires": { - "hoek": "0.9.1" - } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true - } - } - }, - "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - }, - "combine-lists": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, - "commitizen": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-2.9.6.tgz", - "integrity": "sha1-wNAFNe8mTaf2Nzft/aQiiYP6IpE=", - "dev": true, - "requires": { - "cachedir": "1.1.1", - "chalk": "1.1.3", - "cz-conventional-changelog": "1.2.0", - "dedent": "0.6.0", - "detect-indent": "4.0.0", - "find-node-modules": "1.0.4", - "find-root": "1.0.0", - "fs-extra": "1.0.0", - "glob": "7.1.1", - "inquirer": "1.2.3", - "lodash": "4.17.2", - "minimist": "1.2.0", - "path-exists": "2.1.0", - "shelljs": "0.7.6", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "cz-conventional-changelog": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-1.2.0.tgz", - "integrity": "sha1-K8oElkyJGbI/P9aonvXmAIsxs/g=", - "dev": true, - "requires": { - "conventional-commit-types": "2.2.0", - "lodash.map": "4.6.0", - "longest": "1.0.1", - "pad-right": "0.2.2", - "right-pad": "1.0.1", - "word-wrap": "1.2.3" - } - }, - "glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "lodash": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.2.tgz", - "integrity": "sha1-NKMFW6vgTOQkZ7YH1wAHLH/2v0I=", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - }, - "shelljs": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.6.tgz", - "integrity": "sha1-N5zM+1a5HIYB5HkzVutTgpJN6a0=", - "dev": true, - "requires": { - "glob": "7.1.1", - "interpret": "1.0.3", - "rechoir": "0.6.2" - } - } - } - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compare-func": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", - "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", - "dev": true, - "requires": { - "array-ify": "1.0.0", - "dot-prop": "3.0.0" - } - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", - "dev": true - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "compress-commons": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.0.tgz", - "integrity": "sha1-WFhwku8g03y1i68AARLJJ4/3O58=", - "dev": true, - "requires": { - "buffer-crc32": "0.2.13", - "crc32-stream": "2.0.0", - "normalize-path": "2.1.1", - "readable-stream": "2.3.3" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" - } - }, - "connect": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.3.tgz", - "integrity": "sha512-GLSZqgjVxPvGYVD/2vz//gS201MEXk4b7t3nHV6OVnTdDNWi/Gm7Rpxs/ybvljPWvULys/wrzIV3jB3YvEc3nQ==", - "dev": true, - "requires": { - "debug": "2.6.8", - "finalhandler": "1.0.4", - "parseurl": "1.3.1", - "utils-merge": "1.0.0" - } - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-type": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", - "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", - "dev": true - }, - "conventional-changelog": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-1.1.5.tgz", - "integrity": "sha512-DUM0iXhgE11uRCoEQnFuYQA5zJcxfvKn940ZFyXziFl0y05yBCoy5ci2fcQYFWQ+OyyxgE2H02EdgYpuz9XG4w==", - "dev": true, - "requires": { - "conventional-changelog-angular": "1.5.0", - "conventional-changelog-atom": "0.1.1", - "conventional-changelog-codemirror": "0.2.0", - "conventional-changelog-core": "1.9.1", - "conventional-changelog-ember": "0.2.7", - "conventional-changelog-eslint": "0.2.0", - "conventional-changelog-express": "0.2.0", - "conventional-changelog-jquery": "0.1.0", - "conventional-changelog-jscs": "0.1.0", - "conventional-changelog-jshint": "0.2.0" - } - }, - "conventional-changelog-angular": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-1.5.0.tgz", - "integrity": "sha512-Gt0qSf5wdFmLabgdSlqjguDAmPyYTXtUl4WH5W3SlpElHhnU/UiCY3M7xcIkZxrNQfVA1UxUBgu65eBbaJnZVA==", - "dev": true, - "requires": { - "compare-func": "1.3.2", - "q": "1.5.0" - } - }, - "conventional-changelog-atom": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-0.1.1.tgz", - "integrity": "sha512-6Nlu/+MiD4gi7k3Z+N1vMJWpaPSdvFPWzPGnH4OXewHAxiAl0L/TT9CGgA01fosPxmYr4hMNtD7kyN0tkg8vIA==", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "conventional-changelog-codemirror": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.2.0.tgz", - "integrity": "sha512-jUbY98JoKdAOR5k3pOBiKZ+Iz9t2F84hL7x4WjSRW6x7FdeCEUOjyfml+YClE2h/h62Tf3mwur5jSO8upxxc1g==", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "conventional-changelog-core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-1.9.1.tgz", - "integrity": "sha512-Fo0bSeO+NsO6GtuQXts0xQeRpLrxaABTPU8NK4Zij9sJB3zFkU4BObSefJS4F4+EkKujaKCWtfS6Uih+9NpXrQ==", - "dev": true, - "requires": { - "conventional-changelog-writer": "2.0.1", - "conventional-commits-parser": "2.0.0", - "dateformat": "1.0.12", - "get-pkg-repo": "1.4.0", - "git-raw-commits": "1.2.0", - "git-remote-origin-url": "2.0.0", - "git-semver-tags": "1.2.1", - "lodash": "4.17.4", - "normalize-package-data": "2.4.0", - "q": "1.5.0", - "read-pkg": "1.1.0", - "read-pkg-up": "1.0.1", - "through2": "2.0.3" - } - }, - "conventional-changelog-ember": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-0.2.7.tgz", - "integrity": "sha512-Xj1v9uVcKM8N798hMr7e6yiw8IFyvI6Ik+TdjdmG54uGupqvEEWW37xAbPPbdKvgoitbyZkqUTancj055actEg==", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "conventional-changelog-eslint": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-0.2.0.tgz", - "integrity": "sha512-WGKnC0bGPD6BHGiRBfYqNGfy6DZDn2jGs1yxPRT8I2796wYdGqsbDF4477o4fdsxUJvckoW2OFPqkmRMQaCHSA==", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "conventional-changelog-express": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-0.2.0.tgz", - "integrity": "sha512-ujSEmbWfozC1iIjH5Pl7AKtREowvAl10whs1q6c7nZLnoNZK5CmdB2PQ/V42O6rCgUzaLX+ACRW2+g0A/Htqvw==", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "conventional-changelog-jquery": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-0.1.0.tgz", - "integrity": "sha1-Agg5cWLjhGmG5xJztsecW1+A9RA=", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "conventional-changelog-jscs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-jscs/-/conventional-changelog-jscs-0.1.0.tgz", - "integrity": "sha1-BHnrRDzH1yxYvwvPDvHURKkvDlw=", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "conventional-changelog-jshint": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-0.2.0.tgz", - "integrity": "sha512-uUP4c0et6F2teapl+YY2JHFAHD401U5CkgI+P8PyU0y1zS8BdBy6EnhqgZEXhFOp9fPzUdic+Wv/9alOqw3agQ==", - "dev": true, - "requires": { - "compare-func": "1.3.2", - "q": "1.5.0" - } - }, - "conventional-changelog-writer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-2.0.1.tgz", - "integrity": "sha512-X4qC758celQOKw0iUPAsH5sJX6fH6N5dboFc3elXb1/SIKhsYMukhhaxWmxRdtVUSqGt9rZg8giwBQG5B2GeKg==", - "dev": true, - "requires": { - "compare-func": "1.3.2", - "conventional-commits-filter": "1.0.0", - "dateformat": "1.0.12", - "handlebars": "4.0.10", - "json-stringify-safe": "5.0.1", - "lodash": "4.17.4", - "meow": "3.7.0", - "semver": "5.4.1", - "split": "1.0.1", - "through2": "2.0.3" - }, - "dependencies": { - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2.3.8" - } - } - } - }, - "conventional-commit-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-2.2.0.tgz", - "integrity": "sha1-XblXOdbCEqy+e29lahG5QLqmiUY=", - "dev": true - }, - "conventional-commits-filter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-1.0.0.tgz", - "integrity": "sha1-b8KmWTcrw/IznPn//34bA0S5MDk=", - "dev": true, - "requires": { - "is-subset": "0.1.1", - "modify-values": "1.0.0" - } - }, - "conventional-commits-parser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-2.0.0.tgz", - "integrity": "sha512-8od6g684Fhi5Vpp4ABRv/RBsW1AY6wSHbJHEK6FGTv+8jvAAnlABniZu/FVmX9TcirkHepaEsa1QGkRvbg0CKw==", - "dev": true, - "requires": { - "JSONStream": "1.3.1", - "is-text-path": "1.0.1", - "lodash": "4.17.4", - "meow": "3.7.0", - "split2": "2.1.1", - "through2": "2.0.3", - "trim-off-newlines": "1.0.1" - } - }, - "convert-source-map": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", - "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", - "dev": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", - "dev": true - }, - "crc": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", - "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=", - "dev": true - }, - "crc32-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz", - "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=", - "dev": true, - "requires": { - "crc": "3.4.4", - "readable-stream": "2.3.3" - } - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" - } - }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "sha.js": "2.4.8" - } - }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.8" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "crypto-browserify": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", - "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==", - "dev": true, - "requires": { - "browserify-cipher": "1.0.0", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", - "inherits": "2.0.3", - "pbkdf2": "3.0.13", - "public-encrypt": "4.0.0", - "randombytes": "2.0.5" - } - }, - "ctype": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", - "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", - "dev": true, - "optional": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "1.0.2" - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "cz-conventional-changelog": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-2.0.0.tgz", - "integrity": "sha1-Val5r9/pXnAkh50qD1kkYwFwtTM=", - "dev": true, - "requires": { - "conventional-commit-types": "2.2.0", - "lodash.map": "4.6.0", - "longest": "1.0.1", - "pad-right": "0.2.2", - "right-pad": "1.0.1", - "word-wrap": "1.2.3" - } - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "0.10.30" - } - }, - "dargs": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", - "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "data-uri-to-buffer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", - "dev": true - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "dateformat": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", - "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", - "dev": true, - "requires": { - "get-stdin": "4.0.1", - "meow": "3.7.0" - } - }, - "de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", - "dev": true - }, - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "dedent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.6.0.tgz", - "integrity": "sha1-Dm2o8M5Sg471zsXI+TlrDBtko8s=", - "dev": true - }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, - "requires": { - "type-detect": "0.1.1" - } - }, - "deep-equal": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", - "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "defined": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", - "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", - "dev": true - }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "dev": true, - "requires": { - "ast-types": "0.9.12", - "escodegen": "1.8.1", - "esprima": "3.1.3" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - } - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "detect-file": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", - "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", - "dev": true, - "requires": { - "fs-exists-sync": "0.1.0" - } - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", - "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.0", - "randombytes": "2.0.5" - } - }, - "doctrine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", - "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", - "dev": true, - "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "1.0.1", - "ent": "2.2.0", - "extend": "3.0.1", - "void-elements": "2.0.1" - } - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", - "dev": true - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domhandler": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", - "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "domutils": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", - "integrity": "sha1-GVjMC0yUJuntNn+xyOhUiRsPo/8=", - "dev": true, - "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" - } - }, - "dot-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", - "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", - "dev": true, - "requires": { - "is-obj": "1.0.1" - } - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "ecstatic": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-2.2.1.tgz", - "integrity": "sha512-ztE4WqheoWLh3wv+HQwy7dACnvNY620coWpa+XqY6R2cVWgaAT2lUISU1Uf7JpdLLJCURktJOaA9av2AOzsyYQ==", - "dev": true, - "requires": { - "he": "1.1.1", - "mime": "1.2.11", - "minimist": "1.2.0", - "url-join": "2.0.2" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "ejs": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-0.8.3.tgz", - "integrity": "sha1-24qsR/+Ap9+CtMgsEm/olwhwYm8=", - "dev": true - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", - "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "engine.io": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", - "dev": true, - "requires": { - "accepts": "1.3.3", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "ws": "1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "engine.io-client": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parsejson": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "1.1.2", - "xmlhttprequest-ssl": "1.5.3", - "yeast": "0.1.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "engine.io-parser": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "0.0.6", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary": "0.1.7", - "wtf-8": "1.0.0" - } - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "object-assign": "4.1.1", - "tapable": "0.2.8" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - }, - "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "dev": true, - "requires": { - "prr": "0.0.0" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es5-ext": { - "version": "0.10.30", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.30.tgz", - "integrity": "sha1-cUGhaDZpfbq/qq7uQUlc4p9SyTk=", - "dev": true, - "requires": { - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1" - } - }, - "es6-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", - "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-symbol": "3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-iterator": "2.0.1", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-promise": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", - "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng==", - "dev": true - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "2.7.3", - "estraverse": "1.9.3", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.2.0" - }, - "dependencies": { - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" - } - }, - "eslint": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", - "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "chalk": "1.1.3", - "concat-stream": "1.6.0", - "debug": "2.6.8", - "doctrine": "2.0.0", - "escope": "3.6.0", - "espree": "3.5.0", - "esquery": "1.0.0", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "file-entry-cache": "2.0.0", - "glob": "7.1.2", - "globals": "9.18.0", - "ignore": "3.3.5", - "imurmurhash": "0.1.4", - "inquirer": "0.12.0", - "is-my-json-valid": "2.16.1", - "is-resolvable": "1.0.0", - "js-yaml": "3.9.1", - "json-stable-stringify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.2", - "path-is-inside": "1.0.2", - "pluralize": "1.2.1", - "progress": "1.1.8", - "require-uncached": "1.0.3", - "shelljs": "0.7.8", - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1", - "table": "3.8.3", - "text-table": "0.2.0", - "user-home": "2.0.0" - }, - "dependencies": { - "inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", - "dev": true, - "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "figures": "1.7.0", - "lodash": "4.17.4", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" - } - }, - "run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "eslint-loader": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.9.0.tgz", - "integrity": "sha512-40aN976qSNPyb9ejTqjEthZITpls1SVKtwguahmH1dzGCwQU/vySE+xX33VZmD8csU0ahVNCtFlsPgKqRBiqgg==", - "dev": true, - "requires": { - "loader-fs-cache": "1.0.1", - "loader-utils": "1.1.0", - "object-assign": "4.1.1", - "object-hash": "1.1.8", - "rimraf": "2.6.1" - } - }, - "eslint-plugin-flowtype": { - "version": "2.35.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.35.1.tgz", - "integrity": "sha512-YTCeVsMOi3ga8PJjdAV97FaTNXNknzrO+4ZDMHJN65i4uMjL5KgfgQZpyVsBirWOfgXMKRduxpsyM64K/0LbXw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - }, - "eslint-plugin-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-2.0.3.tgz", - "integrity": "sha1-fImIOrDIX6XSi2ZqFKTpBqqQuJc=", - "dev": true, - "requires": { - "htmlparser2": "3.9.2" - } - }, - "eslint-plugin-jasmine": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jasmine/-/eslint-plugin-jasmine-2.8.4.tgz", - "integrity": "sha1-Z6VVHj0dXguMa1Sq66uVNw9dN94=", - "dev": true - }, - "eslint-plugin-vue-libs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue-libs/-/eslint-plugin-vue-libs-1.2.1.tgz", - "integrity": "sha512-GSmpfPqSX4d1Etnuh/w98qLw1voc1HYAF9nqROUTwKVz/d3mJEnuR9rJn8XDzBG1ddErBgO11kLI3TUVMNdJ+A==", - "dev": true, - "requires": { - "eslint-plugin-html": "2.0.3" - } - }, - "espree": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.0.tgz", - "integrity": "sha1-mDWGJb3QVYYeon4oZ+pyn69GPY0=", - "dev": true, - "requires": { - "acorn": "5.1.1", - "acorn-jsx": "3.0.1" - }, - "dependencies": { - "acorn": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", - "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "esquery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", - "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", - "dev": true, - "requires": { - "estraverse": "4.2.0" - } - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, - "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "estree-walker": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.2.1.tgz", - "integrity": "sha1-va/oCVOD2EFNXcLs9MkXO225QS4=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30" - } - }, - "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", - "dev": true - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "evp_bytestokey": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.2.tgz", - "integrity": "sha512-ni0r0lrm7AOzsh2qC5mi9sj8S0gmj5fLNjfFpxN05FB4tAVZEKotbkjOtLPqTCX/CXT7NsUr6juZb4IFJeNNdA==", - "dev": true, - "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, - "expand-braces": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", - "dev": true, - "requires": { - "array-slice": "0.2.3", - "array-unique": "0.2.1", - "braces": "0.1.5" - }, - "dependencies": { - "braces": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", - "dev": true, - "requires": { - "expand-range": "0.1.1" - } - }, - "expand-range": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", - "dev": true, - "requires": { - "is-number": "0.1.1", - "repeat-string": "0.2.2" - } - }, - "is-number": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", - "dev": true - }, - "repeat-string": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", - "dev": true - } - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" - } - }, - "expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", - "dev": true, - "requires": { - "os-homedir": "1.0.2" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "external-editor": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", - "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", - "dev": true, - "requires": { - "extend": "3.0.1", - "spawn-sync": "1.0.15", - "tmp": "0.0.29" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "extract-zip": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.5.tgz", - "integrity": "sha1-maBnNbbqIOqbcF13ms/8yHz/BEA=", - "dev": true, - "requires": { - "concat-stream": "1.6.0", - "debug": "2.2.0", - "mkdirp": "0.5.0", - "yauzl": "2.4.1" - }, - "dependencies": { - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, - "requires": { - "pend": "1.2.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "1.2.2", - "object-assign": "4.1.1" - } - }, - "file-loader": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.2.tgz", - "integrity": "sha512-N+uhF3mswIFeziHQjGScJ/yHXYt3DiLBeC+9vWW+WjUBiClMSOlV1YrXQi+7KM2aA3Rn4Bybgv+uXFQbfkzpvg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "finalhandler": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz", - "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==", - "dev": true, - "requires": { - "debug": "2.6.8", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.1", - "statuses": "1.3.1", - "unpipe": "1.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "1.0.1", - "make-dir": "1.0.0", - "pkg-dir": "2.0.0" - } - }, - "find-node-modules": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-1.0.4.tgz", - "integrity": "sha1-tt6zzMtpnIcDdne87eLF9YYrJVA=", - "dev": true, - "requires": { - "findup-sync": "0.4.2", - "merge": "1.2.0" - } - }, - "find-root": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.0.0.tgz", - "integrity": "sha1-li/yEaqyXGUg/u641ih/j26VgHo=", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "findup-sync": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.2.tgz", - "integrity": "sha1-qBF9D3MST1pFRoOVef5S1xKfteU=", - "dev": true, - "requires": { - "detect-file": "0.1.0", - "is-glob": "2.0.1", - "micromatch": "2.3.11", - "resolve-dir": "0.1.1" - } - }, - "flat-cache": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", - "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", - "dev": true, - "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" - } - }, - "flow-bin": { - "version": "0.54.1", - "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.54.1.tgz", - "integrity": "sha1-cQG8zPAG3AZScUqK7wxyB4p2BRA=", - "dev": true - }, - "flow-remove-types-no-whitespace": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/flow-remove-types-no-whitespace/-/flow-remove-types-no-whitespace-1.0.5.tgz", - "integrity": "sha1-PSl5haC+1Rl7j7DL6J7BY/jeqes=", - "dev": true, - "requires": { - "babylon": "6.18.0", - "magic-string": "0.16.0" - }, - "dependencies": { - "magic-string": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.16.0.tgz", - "integrity": "sha1-lw67DacZMwEoX7GqZQ85vdgetFo=", - "dev": true, - "requires": { - "vlq": "0.2.2" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.16" - } - }, - "fs-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "1.0.0" - } - }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", - "dev": true - }, - "fs-extra": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", - "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "klaw": "1.3.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", - "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", - "dev": true, - "optional": true, - "requires": { - "nan": "2.7.0", - "node-pre-gyp": "0.6.36" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.36", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - } - } - }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "dev": true, - "requires": { - "readable-stream": "1.1.14", - "xregexp": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "requires": { - "is-property": "1.0.2" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-pkg-repo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", - "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", - "dev": true, - "requires": { - "hosted-git-info": "2.5.0", - "meow": "3.7.0", - "normalize-package-data": "2.4.0", - "parse-github-repo-url": "1.4.1", - "through2": "2.0.3" - } - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.1.tgz", - "integrity": "sha512-7aelVrYqCLuVjq2kEKRTH8fXPTC0xKTkM+G7UlFkEwCXY3sFbSxvY375JoFowOAYbkaU47SrBvOefUlLZZ+6QA==", - "dev": true, - "requires": { - "data-uri-to-buffer": "1.2.0", - "debug": "2.6.8", - "extend": "3.0.1", - "file-uri-to-path": "1.0.0", - "ftp": "0.3.10", - "readable-stream": "2.3.3" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "git-raw-commits": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-1.2.0.tgz", - "integrity": "sha1-DzqL/ZmuDy2LkiTViJKXXppS0Dw=", - "dev": true, - "requires": { - "dargs": "4.1.0", - "lodash.template": "4.4.0", - "meow": "3.7.0", - "split2": "2.1.1", - "through2": "2.0.3" - } - }, - "git-remote-origin-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", - "dev": true, - "requires": { - "gitconfiglocal": "1.0.0", - "pify": "2.3.0" - } - }, - "git-semver-tags": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-1.2.1.tgz", - "integrity": "sha512-fFyxtzTHCTQKwB4clA2AInVrlflBbVbbJD4NWwmxKXHUgsU/K9kmHNlkPLqFiuy9xu9q3lNopghR4VXeQwZbTQ==", - "dev": true, - "requires": { - "meow": "3.7.0", - "semver": "5.4.1" - } - }, - "gitconfiglocal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", - "dev": true, - "requires": { - "ini": "1.3.4" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", - "dev": true, - "requires": { - "global-prefix": "0.1.5", - "is-windows": "0.2.0" - } - }, - "global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", - "dev": true, - "requires": { - "homedir-polyfill": "1.0.1", - "ini": "1.3.4", - "is-windows": "0.2.0", - "which": "1.3.0" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, - "growl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", - "dev": true - }, - "handlebars": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", - "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", - "dev": true, - "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "optional": true, - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "optional": true - } - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-binary": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "hasha": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", - "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", - "dev": true, - "requires": { - "is-stream": "1.1.0", - "pinkie-promise": "2.0.1" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "dev": true, - "requires": { - "parse-passwd": "1.0.0" - } - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", - "dev": true - }, - "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", - "dev": true, - "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.4.1", - "domutils": "1.6.2", - "entities": "1.1.1", - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true, - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.3.1" - } - }, - "http-proxy": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", - "dev": true, - "requires": { - "eventemitter3": "1.2.0", - "requires-port": "1.0.0" - } - }, - "http-proxy-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz", - "integrity": "sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo=", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.8", - "extend": "3.0.1" - } - }, - "http-server": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.10.0.tgz", - "integrity": "sha1-sqRGsWqduH7TxiK6m+sbCFsSNKc=", - "dev": true, - "requires": { - "colors": "1.0.3", - "corser": "2.0.1", - "ecstatic": "2.2.1", - "http-proxy": "1.16.2", - "opener": "1.4.3", - "optimist": "0.6.1", - "portfinder": "1.0.13", - "union": "0.4.6" - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "https-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", - "dev": true - }, - "https-proxy-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", - "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.8", - "extend": "3.0.1" - } - }, - "iconv-lite": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", - "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", - "dev": true - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true - }, - "ignore": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz", - "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", - "dev": true - }, - "inquirer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz", - "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=", - "dev": true, - "requires": { - "ansi-escapes": "1.4.0", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "external-editor": "1.1.1", - "figures": "1.7.0", - "lodash": "4.17.4", - "mute-stream": "0.0.6", - "pinkie-promise": "2.0.1", - "run-async": "2.3.0", - "rx": "4.1.0", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" - } - }, - "interpret": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", - "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=", - "dev": true - }, - "invariant": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.0.1.tgz", - "integrity": "sha1-x+NWzeoiWucbNtcPLnGpK6TkJZA=", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.10.0" - } - }, - "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "is-my-json-valid": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", - "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", - "dev": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", - "dev": true, - "requires": { - "is-path-inside": "1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, - "is-resolvable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", - "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", - "dev": true, - "requires": { - "tryit": "1.0.3" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true - }, - "is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", - "dev": true, - "requires": { - "text-extensions": "1.5.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isbinaryfile": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", - "dev": true, - "requires": { - "abbrev": "1.0.9", - "async": "1.5.2", - "escodegen": "1.8.1", - "esprima": "2.7.3", - "glob": "5.0.15", - "handlebars": "4.0.10", - "js-yaml": "3.9.1", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "once": "1.4.0", - "resolve": "1.1.7", - "supports-color": "3.2.3", - "which": "1.3.0", - "wordwrap": "1.0.0" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } - } - }, - "istanbul-lib-coverage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", - "integrity": "sha512-0+1vDkmzxqJIn5rcoEqapSB4DmPxE31EtI2dF2aCkV5esN9EWHxZ0dwgDClivMXJqE7zaYQxq30hj5L0nlTN5Q==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.5.tgz", - "integrity": "sha1-rbWW+PDLi5XnOSBjUaOKWGryGx4=", - "dev": true, - "requires": { - "babel-generator": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.1.1", - "semver": "5.4.1" - } - }, - "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", - "dev": true, - "requires": { - "exit": "0.1.2", - "glob": "7.1.2", - "jasmine-core": "2.8.0" - } - }, - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz", - "integrity": "sha512-CbcG379L1e+mWBnLvHWWeLs8GyV/EMw862uLI3c+GxVyDHWZcjZinwuBd3iW2pgxgIlksW/1vNJa4to+RvDOww==", - "dev": true, - "requires": { - "argparse": "1.0.9", - "esprima": "4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "karma": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", - "integrity": "sha512-k5pBjHDhmkdaUccnC7gE3mBzZjcxyxYsYVaqiL2G5AqlfLyBO5nw2VdNK+O16cveEPd/gIOWULH7gkiYYwVNHg==", - "dev": true, - "requires": { - "bluebird": "3.5.0", - "body-parser": "1.17.2", - "chokidar": "1.7.0", - "colors": "1.1.2", - "combine-lists": "1.0.1", - "connect": "3.6.3", - "core-js": "2.5.1", - "di": "0.0.1", - "dom-serialize": "2.2.1", - "expand-braces": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "http-proxy": "1.16.2", - "isbinaryfile": "3.0.2", - "lodash": "3.10.1", - "log4js": "0.6.38", - "mime": "1.4.0", - "minimatch": "3.0.4", - "optimist": "0.6.1", - "qjobs": "1.1.5", - "range-parser": "1.2.0", - "rimraf": "2.6.1", - "safe-buffer": "5.1.1", - "socket.io": "1.7.3", - "source-map": "0.5.7", - "tmp": "0.0.31", - "useragent": "2.2.1" - }, - "dependencies": { - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "mime": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.0.tgz", - "integrity": "sha512-n9ChLv77+QQEapYz8lV+rIZAW3HhAPW2CXnzb1GN5uMkuczshwvkW7XPsbzU0ZQN3sP47Er2KVkp2p3KyqZKSQ==", - "dev": true - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - } - } - }, - "karma-chrome-launcher": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", - "dev": true, - "requires": { - "fs-access": "1.0.1", - "which": "1.3.0" - } - }, - "karma-coverage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-1.1.1.tgz", - "integrity": "sha1-Wv+LOc9plNwi3kyENix2ABtjfPY=", - "dev": true, - "requires": { - "dateformat": "1.0.12", - "istanbul": "0.4.5", - "lodash": "3.10.1", - "minimatch": "3.0.4", - "source-map": "0.5.7" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - } - } - }, - "karma-firefox-launcher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.0.1.tgz", - "integrity": "sha1-zlj0fCATqIFW1VpdYTN8CZz1u1E=", - "dev": true - }, - "karma-jasmine": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.0.tgz", - "integrity": "sha1-IuTAa/mhguUpTR9wXjczgRuBCs8=", - "dev": true - }, - "karma-mocha-reporter": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/karma-mocha-reporter/-/karma-mocha-reporter-2.2.4.tgz", - "integrity": "sha1-DJyyLCfYZND2aU3wzwHKq86QZNQ=", - "dev": true, - "requires": { - "chalk": "2.1.0", - "log-symbols": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "karma-phantomjs-launcher": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.4.tgz", - "integrity": "sha1-0jyjSAG9qYY60xjju0vUBisTrNI=", - "dev": true, - "requires": { - "lodash": "4.17.4", - "phantomjs-prebuilt": "2.1.15" - } - }, - "karma-safari-launcher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz", - "integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=", - "dev": true - }, - "karma-sauce-launcher": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/karma-sauce-launcher/-/karma-sauce-launcher-1.2.0.tgz", - "integrity": "sha512-lEhtGRGS+3Yw6JSx/vJY9iQyHNtTjcojrSwNzqNUOaDceKDu9dPZqA/kr69bUO9G2T6GKbu8AZgXqy94qo31Jg==", - "dev": true, - "requires": { - "q": "1.5.0", - "sauce-connect-launcher": "1.2.2", - "saucelabs": "1.4.0", - "wd": "1.4.0" - } - }, - "karma-sourcemap-loader": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz", - "integrity": "sha1-kTIsd/jxPUb+0GKwQuEAnUxFBdg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11" - } - }, - "karma-webpack": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-2.0.4.tgz", - "integrity": "sha1-Pi1PSLqUqHjhxmu44a5hKJh6F1s=", - "dev": true, - "requires": { - "async": "0.9.2", - "loader-utils": "0.2.17", - "lodash": "3.10.1", - "source-map": "0.1.43", - "webpack-dev-middleware": "1.12.0" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.1.3", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "kew": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", - "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - } - }, - "loader-fs-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", - "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", - "dev": true, - "requires": { - "find-cache-dir": "0.1.1", - "mkdirp": "0.5.1" - }, - "dependencies": { - "find-cache-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", - "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", - "dev": true, - "requires": { - "commondir": "1.0.1", - "mkdirp": "0.5.1", - "pkg-dir": "1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "1.1.2" - } - } - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "3.1.3", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true - }, - "lodash._arraycopy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz", - "integrity": "sha1-due3wfH7klRzdIeKVi7Qaj5Q9uE=", - "dev": true - }, - "lodash._arrayeach": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz", - "integrity": "sha1-urFWsqkNPxu9XGU0AzSeXlkz754=", - "dev": true - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "3.0.1", - "lodash.keys": "3.1.2" - } - }, - "lodash._baseclone": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz", - "integrity": "sha1-MDUZv2OT/n5C802LYw73eU41Qrc=", - "dev": true, - "requires": { - "lodash._arraycopy": "3.0.0", - "lodash._arrayeach": "3.0.0", - "lodash._baseassign": "3.2.0", - "lodash._basefor": "3.0.3", - "lodash.isarray": "3.0.4", - "lodash.keys": "3.1.2" - } - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basecreate": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", - "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", - "dev": true - }, - "lodash._basefor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", - "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI=", - "dev": true - }, - "lodash._bindcallback": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "lodash._stack": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lodash._stack/-/lodash._stack-4.1.3.tgz", - "integrity": "sha1-dRqnbBuWSwR+dtFPxyoJP8teLdA=", - "dev": true - }, - "lodash.clone": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-3.0.3.tgz", - "integrity": "sha1-hGiMc9MrWpDKJWFpY/GJJSqZcEM=", - "dev": true, - "requires": { - "lodash._baseclone": "3.3.0", - "lodash._bindcallback": "3.0.1", - "lodash._isiterateecall": "3.0.9" - } - }, - "lodash.create": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", - "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", - "dev": true, - "requires": { - "lodash._baseassign": "3.2.0", - "lodash._basecreate": "3.0.3", - "lodash._isiterateecall": "3.0.9" - } - }, - "lodash.defaultsdeep": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.3.2.tgz", - "integrity": "sha1-bBpYbmxWR7DmTi15gUG4g2FYvoo=", - "dev": true, - "requires": { - "lodash._baseclone": "4.5.7", - "lodash._stack": "4.1.3", - "lodash.isplainobject": "4.0.6", - "lodash.keysin": "4.2.0", - "lodash.mergewith": "4.6.0", - "lodash.rest": "4.0.5" - }, - "dependencies": { - "lodash._baseclone": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz", - "integrity": "sha1-zkKt4IOE711i+nfDD2GkbmhvhDQ=", - "dev": true - } - } - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" - } - }, - "lodash.keysin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.keysin/-/lodash.keysin-4.2.0.tgz", - "integrity": "sha1-jMP7NcLZSsxEOhhj4C+kB5nqbyg=", - "dev": true - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", - "dev": true - }, - "lodash.mergewith": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", - "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", - "dev": true - }, - "lodash.rest": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/lodash.rest/-/lodash.rest-4.0.5.tgz", - "integrity": "sha1-lU73UEkmIDjJbR/Jiyj9r58Hcqo=", - "dev": true - }, - "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "dev": true, - "requires": { - "lodash._reinterpolate": "3.0.0", - "lodash.templatesettings": "4.1.0" - } - }, - "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "dev": true, - "requires": { - "lodash._reinterpolate": "3.0.0" - } - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.0.0.tgz", - "integrity": "sha512-ValCSal2pRxRbet7O69a/1g5fZ2MLxf1YXIslNrdJF42ofY9zVf6MTqTwfhG+2x168xrbZATCgFQfXAwdNHv+w==", - "dev": true, - "requires": { - "chalk": "2.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "log4js": { - "version": "0.6.38", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", - "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", - "dev": true, - "requires": { - "readable-stream": "1.0.34", - "semver": "4.3.6" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "magic-string": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.14.0.tgz", - "integrity": "sha1-VyJK7xcByu7Sc7F6OalW5ysXJGI=", - "dev": true, - "requires": { - "vlq": "0.2.2" - } - }, - "make-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", - "integrity": "sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg=", - "dev": true, - "requires": { - "pify": "2.3.0" - } - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - } - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "merge": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", - "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", - "dev": true - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - }, - "miller-rabin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", - "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" - } - }, - "mime": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", - "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", - "dev": true - }, - "mime-db": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", - "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=", - "dev": true - }, - "mime-types": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", - "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", - "dev": true, - "requires": { - "mime-db": "1.29.0" - } - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mkpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-1.0.0.tgz", - "integrity": "sha1-67Opd+evHGg65v2hK1Raa6bFhT0=", - "dev": true - }, - "mocha-nightwatch": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/mocha-nightwatch/-/mocha-nightwatch-3.2.2.tgz", - "integrity": "sha1-kby5s73gV912d8eBJeSR5Y1mZHw=", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.9.0", - "debug": "2.2.0", - "diff": "1.4.0", - "escape-string-regexp": "1.0.5", - "glob": "7.0.5", - "growl": "1.9.2", - "json3": "3.3.2", - "lodash.create": "3.1.1", - "mkdirp": "0.5.1", - "supports-color": "3.1.2" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": "1.0.1" - } - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "glob": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", - "integrity": "sha1-tCAqaQmbu00pKnwblbZoK2fr3JU=", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - }, - "supports-color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "modify-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.0.tgz", - "integrity": "sha1-4rbN65zhn5kxelNyLz2/XfXqqrI=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "mute-stream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", - "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=", - "dev": true - }, - "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", - "dev": true, - "optional": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "dev": true - }, - "nightwatch": { - "version": "0.9.16", - "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-0.9.16.tgz", - "integrity": "sha1-xKw+xxGw/wR8PcqcZVc2XuI2UZ8=", - "dev": true, - "requires": { - "chai-nightwatch": "0.1.1", - "ejs": "0.8.3", - "lodash.clone": "3.0.3", - "lodash.defaultsdeep": "4.3.2", - "minimatch": "3.0.3", - "mkpath": "1.0.0", - "mocha-nightwatch": "3.2.2", - "optimist": "0.6.1", - "proxy-agent": "2.0.0", - "q": "1.4.1" - }, - "dependencies": { - "minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", - "dev": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - } - } - }, - "nightwatch-helpers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/nightwatch-helpers/-/nightwatch-helpers-1.2.0.tgz", - "integrity": "sha1-ze/rdjUHShQYJae20blVtteBCg8=", - "dev": true - }, - "node-libs-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", - "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", - "dev": true, - "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.1.4", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.11.1", - "domain-browser": "1.1.7", - "events": "1.1.1", - "https-browserify": "0.0.1", - "os-browserify": "0.2.1", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.3", - "stream-browserify": "2.0.1", - "stream-http": "2.7.2", - "string_decoder": "0.10.31", - "timers-browserify": "2.0.4", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1.0.9" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "null-check": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-hash": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.8.tgz", - "integrity": "sha1-KKZZz5h9lqTavnhgKJ87UybEoDw=", - "dev": true - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "opener": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz", - "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=", - "dev": true - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } - } - }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, - "os-browserify": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", - "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "1.0.0" - } - }, - "os-shim": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.1.0" - } - }, - "pac-proxy-agent": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz", - "integrity": "sha512-QBELCWyLYPgE2Gj+4wUEiMscHrQ8nRPBzYItQNOHWavwBt25ohZHQC4qnd5IszdVVrFbLsQ+dPkm6eqdjJAmwQ==", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.8", - "extend": "3.0.1", - "get-uri": "2.0.1", - "http-proxy-agent": "1.0.0", - "https-proxy-agent": "1.0.0", - "pac-resolver": "2.0.0", - "raw-body": "2.2.0", - "socks-proxy-agent": "2.1.1" - } - }, - "pac-resolver": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-2.0.0.tgz", - "integrity": "sha1-mbiNLxk/ve78HJpSnB8yYKtSd80=", - "dev": true, - "requires": { - "co": "3.0.6", - "degenerator": "1.0.4", - "ip": "1.0.1", - "netmask": "1.0.6", - "thunkify": "2.1.2" - }, - "dependencies": { - "co": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/co/-/co-3.0.6.tgz", - "integrity": "sha1-FEXyJsXrlWE45oyawwFn6n0ua9o=", - "dev": true - } - } - }, - "pad-right": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", - "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=", - "dev": true, - "requires": { - "repeat-string": "1.6.1" - } - }, - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", - "dev": true - }, - "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", - "dev": true, - "requires": { - "asn1.js": "4.9.1", - "browserify-aes": "1.0.6", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.2", - "pbkdf2": "3.0.13" - } - }, - "parse-github-repo-url": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", - "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", - "dev": true - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parsejson": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseurl": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", - "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "pbkdf2": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.13.tgz", - "integrity": "sha512-+dCHxDH+djNtjgWmvVC/my3SYBAKpKNqKSjLkp+GtWWYe4XPE+e/PSD2aCanlEZZnqPk2uekTKNC/ccbwd2X2Q==", - "dev": true, - "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.8" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true - }, - "phantomjs-prebuilt": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.15.tgz", - "integrity": "sha1-IPhugtM0nFBZF1J3RbekEeCLOQM=", - "dev": true, - "requires": { - "es6-promise": "4.0.5", - "extract-zip": "1.6.5", - "fs-extra": "1.0.0", - "hasha": "2.2.0", - "kew": "0.7.0", - "progress": "1.1.8", - "request": "2.81.0", - "request-progress": "2.0.1", - "which": "1.2.14" - }, - "dependencies": { - "es6-promise": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", - "integrity": "sha1-eILzCt3lskDM+n99eMVIMwlRrkI=", - "dev": true - }, - "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - } - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "2.1.0" - } - }, - "pluralize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", - "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", - "dev": true - }, - "portfinder": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", - "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", - "dev": true, - "requires": { - "async": "1.5.2", - "debug": "2.6.8", - "mkdirp": "0.5.1" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "private": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", - "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true - }, - "proxy-agent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-2.0.0.tgz", - "integrity": "sha1-V+tTR6qAXXTsaByyVknbo5yTNJk=", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "debug": "2.6.8", - "extend": "3.0.1", - "http-proxy-agent": "1.0.0", - "https-proxy-agent": "1.0.0", - "lru-cache": "2.6.5", - "pac-proxy-agent": "1.1.0", - "socks-proxy-agent": "2.1.1" - }, - "dependencies": { - "lru-cache": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz", - "integrity": "sha1-5W1jVBSO3o13B7WNFDIg/QjfD9U=", - "dev": true - } - } - }, - "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", - "randombytes": "2.0.5" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "q": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", - "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=", - "dev": true - }, - "qjobs": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", - "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", - "dev": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "randombytes": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", - "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz", - "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=", - "dev": true, - "requires": { - "bytes": "2.4.0", - "iconv-lite": "0.4.15", - "unpipe": "1.0.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - } - } - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" - } - }, - "readline2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", - "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "mute-stream": "0.0.5" - }, - "dependencies": { - "mute-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", - "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", - "dev": true - } - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "1.4.0" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" - } - }, - "regenerate": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", - "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", - "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "private": "0.1.7" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "1.3.2", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.16", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - } - }, - "request-progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", - "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", - "dev": true, - "requires": { - "throttleit": "1.0.0" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" - } - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", - "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", - "dev": true, - "requires": { - "path-parse": "1.0.5" - } - }, - "resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", - "dev": true, - "requires": { - "expand-tilde": "1.2.2", - "global-modules": "0.2.3" - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, - "resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", - "dev": true, - "requires": { - "through": "2.3.8" - } - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "0.1.4" - } - }, - "right-pad": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/right-pad/-/right-pad-1.0.1.tgz", - "integrity": "sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA=", - "dev": true - }, - "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", - "dev": true, - "requires": { - "hash-base": "2.0.2", - "inherits": "2.0.3" - } - }, - "rollup": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.50.0.tgz", - "integrity": "sha512-7RqCBQ9iwsOBPkjYgoIaeUij606mSkDMExP0NT7QDI3bqkHYQHrQ83uoNIXwPcQm/vP2VbsUz3kiyZZ1qPlLTQ==", - "dev": true - }, - "rollup-plugin-alias": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-alias/-/rollup-plugin-alias-1.3.1.tgz", - "integrity": "sha1-qRUv7EtqZRDa6TmJUXynhTwypvo=", - "dev": true, - "requires": { - "slash": "1.0.0" - } - }, - "rollup-plugin-babel": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-3.0.2.tgz", - "integrity": "sha512-ALGPBFtwJZcYHsNPM6RGJlEncTzAARPvZOGjNPZgDe5hS5t6sJGjiOWibEFVEz5LQN7S7spvCBILaS4N1Cql2w==", - "dev": true, - "requires": { - "rollup-pluginutils": "1.5.2" - } - }, - "rollup-plugin-buble": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-buble/-/rollup-plugin-buble-0.16.0.tgz", - "integrity": "sha512-dPIvH9iK9AUGRrqpARL6TTNY85BJpc5OK5PiCAnFaRe7C1boRBVRXiL0SYsYNVnyYYPl6vu0lVSb722eMSw1Eg==", - "dev": true, - "requires": { - "buble": "0.16.0", - "rollup-pluginutils": "2.0.1" - }, - "dependencies": { - "buble": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/buble/-/buble-0.16.0.tgz", - "integrity": "sha512-Eb5vt1+IvXXPyYD1IIQIuaBwIuJOSWQ2kXzULlg5I83aLGF2qzcjRU2joYusnWFgAenvJ9xTOMvZvT0bb8BLbg==", - "dev": true, - "requires": { - "acorn": "3.3.0", - "acorn-jsx": "3.0.1", - "acorn-object-spread": "1.0.0", - "chalk": "1.1.3", - "magic-string": "0.14.0", - "minimist": "1.2.0", - "os-homedir": "1.0.2", - "vlq": "0.2.2" - } - }, - "estree-walker": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", - "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "rollup-pluginutils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", - "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=", - "dev": true, - "requires": { - "estree-walker": "0.3.1", - "micromatch": "2.3.11" - } - } - } - }, - "rollup-plugin-commonjs": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.0.tgz", - "integrity": "sha512-EhSkStWuGtfFBI9l89KfUb7yOw3IpcQAtYao/R20FvkiKHDndEF9cyWhuUoSW0igY2/hdH0EeUl/3b9d5xHL3w==", - "dev": true, - "requires": { - "acorn": "5.1.1", - "estree-walker": "0.5.0", - "magic-string": "0.22.4", - "resolve": "1.4.0", - "rollup-pluginutils": "2.0.1" - }, - "dependencies": { - "acorn": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", - "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==", - "dev": true - }, - "estree-walker": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.0.tgz", - "integrity": "sha512-/bEAy+yKAZQrEWUhGmS3H9XpGqSDBtRzX0I2PgMw9kA2n1jN22uV5B5p7MFdZdvWdXCRJztXAfx6ZeRfgkEETg==", - "dev": true - }, - "magic-string": { - "version": "0.22.4", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz", - "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==", - "dev": true, - "requires": { - "vlq": "0.2.2" - } - }, - "rollup-pluginutils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", - "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=", - "dev": true, - "requires": { - "estree-walker": "0.3.1", - "micromatch": "2.3.11" - }, - "dependencies": { - "estree-walker": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", - "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", - "dev": true - } - } - } - } - }, - "rollup-plugin-flow-no-whitespace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-flow-no-whitespace/-/rollup-plugin-flow-no-whitespace-1.0.0.tgz", - "integrity": "sha1-vUuhvNma1biCNNcubi2s6pqgLRY=", - "dev": true, - "requires": { - "flow-remove-types-no-whitespace": "1.0.5", - "rollup-pluginutils": "1.5.2" - } - }, - "rollup-plugin-node-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.0.tgz", - "integrity": "sha1-i4l8TDAw1QASd7BRSyXSygloPuA=", - "dev": true, - "requires": { - "browser-resolve": "1.11.2", - "builtin-modules": "1.1.1", - "is-module": "1.0.0", - "resolve": "1.4.0" - } - }, - "rollup-plugin-replace": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.0.0.tgz", - "integrity": "sha512-pK9mTd/FNrhtBxcTBXoh0YOwRIShV0gGhv9qvUtNcXHxIMRZMXqfiZKVBmCRGp8/2DJRy62z2JUE7/5tP6WxOQ==", - "dev": true, - "requires": { - "magic-string": "0.22.4", - "minimatch": "3.0.4", - "rollup-pluginutils": "2.0.1" - }, - "dependencies": { - "estree-walker": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", - "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", - "dev": true - }, - "magic-string": { - "version": "0.22.4", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz", - "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==", - "dev": true, - "requires": { - "vlq": "0.2.2" - } - }, - "rollup-pluginutils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", - "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=", - "dev": true, - "requires": { - "estree-walker": "0.3.1", - "micromatch": "2.3.11" - } - } - } - }, - "rollup-pluginutils": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", - "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=", - "dev": true, - "requires": { - "estree-walker": "0.2.1", - "minimatch": "3.0.4" - } - }, - "rollup-watch": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/rollup-watch/-/rollup-watch-4.3.1.tgz", - "integrity": "sha512-6yjnIwfjpSrqA8IafyIu7fsEyeImNR4aDjA1bQ7KWeVuiA+Clfsx8+PGQkyABWIQzmauQ//tIJ5wAxLXsXs8qQ==", - "dev": true, - "requires": { - "chokidar": "1.7.0", - "require-relative": "0.8.7", - "rollup-pluginutils": "2.0.1" - }, - "dependencies": { - "estree-walker": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", - "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", - "dev": true - }, - "rollup-pluginutils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", - "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=", - "dev": true, - "requires": { - "estree-walker": "0.3.1", - "micromatch": "2.3.11" - } - } - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "2.1.0" - } - }, - "rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=", - "dev": true - }, - "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", - "dev": true - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - }, - "sauce-connect-launcher": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/sauce-connect-launcher/-/sauce-connect-launcher-1.2.2.tgz", - "integrity": "sha1-c0bMj73EQxkTI0ObBzNFH181IfI=", - "dev": true, - "requires": { - "adm-zip": "0.4.7", - "async": "2.5.0", - "https-proxy-agent": "1.0.0", - "lodash": "4.17.4", - "rimraf": "2.6.1" - }, - "dependencies": { - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - } - } - }, - "saucelabs": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.4.0.tgz", - "integrity": "sha1-uTSpr52ih0s/QKrh/N5QpEZvXzg=", - "dev": true, - "requires": { - "https-proxy-agent": "1.0.0" - } - }, - "selenium-server": { - "version": "2.53.1", - "resolved": "https://registry.npmjs.org/selenium-server/-/selenium-server-2.53.1.tgz", - "integrity": "sha1-1oFSiBLzwuBTGmt+YT4juwLM6KY=", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, - "serialize-javascript": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", - "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - }, - "sha.js": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", - "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true, - "requires": { - "glob": "7.1.2", - "interpret": "1.0.3", - "rechoir": "0.6.2" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "dev": true - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "socket.io": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", - "dev": true, - "requires": { - "debug": "2.3.3", - "engine.io": "1.8.3", - "has-binary": "0.1.7", - "object-assign": "4.1.0", - "socket.io-adapter": "0.5.0", - "socket.io-client": "1.7.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "object-assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", - "dev": true - } - } - }, - "socket.io-adapter": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", - "dev": true, - "requires": { - "debug": "2.3.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-client": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", - "dev": true, - "requires": { - "backo2": "1.0.2", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.3.3", - "engine.io-client": "1.8.3", - "has-binary": "0.1.7", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseuri": "0.0.5", - "socket.io-parser": "2.3.1", - "to-array": "0.1.4" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", - "dev": true, - "requires": { - "component-emitter": "1.1.2", - "debug": "2.2.0", - "isarray": "0.0.1", - "json3": "3.3.2" - }, - "dependencies": { - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - } - } - }, - "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" - }, - "dependencies": { - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - } - } - }, - "socks-proxy-agent": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz", - "integrity": "sha512-sFtmYqdUK5dAMh85H0LEVFUCO7OhJJe1/z2x/Z6mxp3s7/QPf1RkZmpZy+BpuU0bEjcV9npqKjq9Y3kwFUjnxw==", - "dev": true, - "requires": { - "agent-base": "2.1.1", - "extend": "3.0.1", - "socks": "1.1.10" - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.16.tgz", - "integrity": "sha512-A6vlydY7H/ljr4L2UOhDSajQdZQ6dMD7cLH0pzwcmwLyc9u8PNI4WGtnfDDzX7uzGL6c/T+ORL97Zlh+S4iOrg==", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "spawn-sync": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", - "dev": true, - "requires": { - "concat-stream": "1.6.0", - "os-shim": "0.1.3" - } - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true, - "requires": { - "spdx-license-ids": "1.2.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, - "split": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", - "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", - "dev": true, - "requires": { - "through": "2.3.8" - } - }, - "split2": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-2.1.1.tgz", - "integrity": "sha1-eh9VHhdqkOzTNF9yRqDP4XXvT9A=", - "dev": true, - "requires": { - "through2": "2.0.3" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "dev": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "requires": { - "duplexer": "0.1.1" - } - }, - "stream-http": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", - "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", - "dev": true, - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", - "dev": true, - "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", - "lodash": "4.17.4", - "slice-ansi": "0.0.4", - "string-width": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", - "dev": true - }, - "tape": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.0.tgz", - "integrity": "sha1-Df7scJIn+8yRcKvn8EaWKycUMds=", - "dev": true, - "requires": { - "deep-equal": "0.1.2", - "defined": "0.0.0", - "inherits": "2.0.3", - "jsonify": "0.0.0", - "resumer": "0.0.0", - "split": "0.2.10", - "stream-combiner": "0.0.4", - "through": "2.3.8" - } - }, - "tar-stream": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", - "integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=", - "dev": true, - "requires": { - "bl": "1.2.1", - "end-of-stream": "1.4.0", - "readable-stream": "2.3.3", - "xtend": "4.0.1" - }, - "dependencies": { - "bl": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", - "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - } - } - } - }, - "test-exclude": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz", - "integrity": "sha512-35+Asrsk3XHJDBgf/VRFexPgh3UyETv8IAn/LRTiZjVy6rjPVqdEk8dJcJYBzl1w0XCJM48lvTy8SfEsCWS4nA==", - "dev": true, - "requires": { - "arrify": "1.0.1", - "micromatch": "2.3.11", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "require-main-filename": "1.0.1" - } - }, - "text-extensions": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.5.0.tgz", - "integrity": "sha1-0cstFLXQvEW/3Kigikc/aMfrDLw=", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", - "dev": true - }, - "time-stamp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz", - "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", - "integrity": "sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg==", - "dev": true, - "requires": { - "setimmediate": "1.0.5" - } - }, - "tmp": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz", - "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "tough-cookie": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", - "dev": true, - "requires": { - "punycode": "1.4.1" - } - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-off-newlines": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", - "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "tryit": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", - "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2" - } - }, - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - }, - "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.16" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz", - "integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=", - "dev": true - }, - "uglify-js": { - "version": "3.0.28", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.28.tgz", - "integrity": "sha512-0h/qGay016GG2lVav3Kz174F3T2Vjlz2v6HCt+WDQpoXfco0hWwF5gHK9yh88mUYvIC+N7Z8NT8WpjSp1yoqGA==", - "dev": true, - "requires": { - "commander": "2.11.0", - "source-map": "0.5.7" - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, - "underscore.string": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", - "integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=", - "dev": true, - "requires": { - "sprintf-js": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "union": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/union/-/union-0.4.6.tgz", - "integrity": "sha1-GY+9rrolTniLDvy2MLwR8kopWeA=", - "dev": true, - "requires": { - "qs": "2.3.3" - }, - "dependencies": { - "qs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", - "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=", - "dev": true - } - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-join": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.2.tgz", - "integrity": "sha1-wHJ1aWetJLi1nldBVRyqx49QuLc=", - "dev": true - }, - "urlgrey": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.0.tgz", - "integrity": "sha1-8GU1cED7NcOzEdTl3DZITZbb6gY=", - "dev": true, - "requires": { - "tape": "2.3.0" - } - }, - "user-home": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", - "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", - "dev": true, - "requires": { - "os-homedir": "1.0.2" - } - }, - "useragent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", - "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", - "dev": true, - "requires": { - "lru-cache": "2.2.4", - "tmp": "0.0.29" - }, - "dependencies": { - "lru-cache": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", - "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", - "dev": true - } - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "utils-merge": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", - "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", - "dev": true - }, - "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true, - "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" - } - }, - "vargs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/vargs/-/vargs-0.1.0.tgz", - "integrity": "sha1-a2GE2mUgzDIEzhtAfKwm2SYJ6/8=", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "vlq": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.2.tgz", - "integrity": "sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE=", - "dev": true - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "walkdir": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz", - "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=", - "dev": true - }, - "watchpack": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", - "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", - "dev": true, - "requires": { - "async": "2.5.0", - "chokidar": "1.7.0", - "graceful-fs": "4.1.11" - }, - "dependencies": { - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - } - } - }, - "wd": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/wd/-/wd-1.4.0.tgz", - "integrity": "sha512-VHii2f+jck5fgEcTQYCR3z99B99tPz0HlLCGsNowI2qsI21xMnKwd9O3SnJQnEBq0Erx9FPFfiZno+OYtXDXyw==", - "dev": true, - "requires": { - "archiver": "1.3.0", - "async": "2.0.1", - "lodash": "4.16.2", - "mkdirp": "0.5.1", - "q": "1.4.1", - "request": "2.79.0", - "underscore.string": "3.3.4", - "vargs": "0.1.0" - }, - "dependencies": { - "async": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.0.1.tgz", - "integrity": "sha1-twnMAoCpw28J9FNr6CPIOKkEniU=", - "dev": true, - "requires": { - "lodash": "4.16.2" - } - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.11.0", - "is-my-json-valid": "2.16.1", - "pinkie-promise": "2.0.1" - } - }, - "lodash": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.16.2.tgz", - "integrity": "sha1-PmJtuCcEimmSgaihJSJjJs/A5lI=", - "dev": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", - "dev": true - }, - "request": { - "version": "2.79.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", - "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", - "dev": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.16", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.4.3", - "uuid": "3.1.0" - } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true - } - } - }, - "webpack": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.7.0.tgz", - "integrity": "sha512-MjAA0ZqO1ba7ZQJRnoCdbM56mmFpipOPUv/vQpwwfSI42p5PVDdoiuK2AL2FwFUVgT859Jr43bFZXRg/LNsqvg==", - "dev": true, - "requires": { - "acorn": "5.1.1", - "acorn-dynamic-import": "2.0.2", - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "async": "2.5.0", - "enhanced-resolve": "3.4.1", - "interpret": "1.0.3", - "json-loader": "0.5.7", - "json5": "0.5.1", - "loader-runner": "2.3.0", - "loader-utils": "0.2.17", - "memory-fs": "0.4.1", - "mkdirp": "0.5.1", - "node-libs-browser": "2.0.0", - "source-map": "0.5.7", - "supports-color": "3.2.3", - "tapable": "0.2.8", - "uglify-js": "2.8.29", - "watchpack": "1.4.0", - "webpack-sources": "1.0.1", - "yargs": "6.6.0" - }, - "dependencies": { - "acorn": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", - "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==", - "dev": true - }, - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.1.3", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, - "yargs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", - "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", - "dev": true, - "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "4.2.1" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - } - } - } - } - } - }, - "webpack-dev-middleware": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz", - "integrity": "sha1-007++y7dp+HTtdvgcolRMhllFwk=", - "dev": true, - "requires": { - "memory-fs": "0.4.1", - "mime": "1.4.0", - "path-is-absolute": "1.0.1", - "range-parser": "1.2.0", - "time-stamp": "2.0.0" - }, - "dependencies": { - "mime": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.0.tgz", - "integrity": "sha512-n9ChLv77+QQEapYz8lV+rIZAW3HhAPW2CXnzb1GN5uMkuczshwvkW7XPsbzU0ZQN3sP47Er2KVkp2p3KyqZKSQ==", - "dev": true - } - } - }, - "webpack-sources": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz", - "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==", - "dev": true, - "requires": { - "source-list-map": "2.0.0", - "source-map": "0.5.7" - } - }, - "weex-js-runtime": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/weex-js-runtime/-/weex-js-runtime-0.23.0.tgz", - "integrity": "sha512-0K2jfuqJ2yQ4NgBI7RcXsBz1WrRjuWLlEtLMd7OA78HCdSgZPDUXH/vL0ecaqL7IGzvEsTl9y9echhoWKLqj5w==", - "dev": true - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "0.5.1" - } - }, - "ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", - "dev": true, - "requires": { - "options": "0.0.6", - "ultron": "1.0.2" - } - }, - "wtf-8": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", - "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", - "dev": true - }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "optional": true - } - } - }, - "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", - "dev": true, - "requires": { - "camelcase": "3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } - } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true, - "requires": { - "fd-slicer": "1.0.1" - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "zip-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz", - "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=", - "dev": true, - "requires": { - "archiver-utils": "1.3.0", - "compress-commons": "1.2.0", - "lodash": "4.17.4", - "readable-stream": "2.3.3" - } - } - } -} diff --git a/package.json b/package.json index 7e72ab11777..52308153a89 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "vue", - "version": "2.5.3", + "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", @@ -10,38 +11,67 @@ "files": [ "src", "dist/*.js", - "types/*.d.ts" + "dist/*.mjs", + "types/*.d.ts", + "compiler-sfc", + "packages/compiler-sfc" ], + "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 build/config.js --environment TARGET:web-full-dev", - "dev:cjs": "rollup -w -c build/config.js --environment TARGET:web-runtime-cjs", - "dev:esm": "rollup -w -c build/config.js --environment TARGET:web-runtime-esm", - "dev:test": "karma start test/unit/karma.dev.config.js", - "dev:ssr": "rollup -w -c build/config.js --environment TARGET:web-server-renderer", - "dev:compiler": "rollup -w -c build/config.js --environment TARGET:web-compiler ", - "dev:weex": "rollup -w -c build/config.js --environment TARGET:weex-framework", - "dev:weex:factory": "rollup -w -c build/config.js --environment TARGET:weex-factory", - "dev:weex:compiler": "rollup -w -c build/config.js --environment TARGET:weex-compiler ", - "build": "node build/build.js", - "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer", - "build:weex": "npm run build -- weex", - "test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:weex", - "test:unit": "karma start test/unit/karma.unit.config.js", - "test:cover": "karma start test/unit/karma.cover.config.js", - "test:e2e": "npm run build -- web-full-prod,web-server-basic-renderer && node test/e2e/runner.js", - "test:weex": "npm run build:weex && jasmine JASMINE_CONFIG_PATH=test/weex/jasmine.json", - "test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.json", - "test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2", - "test:types": "tsc -p ./types/test/tsconfig.json", - "lint": "eslint src build test", - "flow": "flow check", - "sauce": "karma start test/unit/karma.sauce.config.js", + "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": "bash build/release.sh", - "release:weex": "bash build/release-weex.sh", - "release:note": "node build/gen-release-note.js", - "setup": "node build/setup.js", - "commit": "git-cz" + "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", @@ -56,81 +86,51 @@ "url": "https://github.com/vuejs/vue/issues" }, "homepage": "https://github.com/vuejs/vue#readme", - "devDependencies": { - "@types/node": "^8.0.33", - "@types/webpack": "^3.0.13", - "babel-core": "^6.25.0", - "babel-eslint": "^7.2.3", - "babel-helper-vue-jsx-merge-props": "^2.0.2", - "babel-loader": "^7.0.0", - "babel-plugin-istanbul": "^4.1.4", - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "babel-plugin-transform-vue-jsx": "^3.4.3", - "babel-preset-es2015": "^6.24.1", - "babel-preset-flow-vue": "^1.0.0", - "buble": "^0.16.0", - "chalk": "^1.1.3", - "chromedriver": "^2.30.1", - "codecov.io": "^0.1.6", - "commitizen": "^2.9.6", - "conventional-changelog": "^1.1.3", - "cross-spawn": "^5.1.0", - "cz-conventional-changelog": "^2.0.0", - "de-indent": "^1.0.2", - "es6-promise": "^4.1.0", - "eslint": "^3.0.0", - "eslint-loader": "^1.7.1", - "eslint-plugin-flowtype": "^2.34.0", - "eslint-plugin-jasmine": "^2.8.4", - "eslint-plugin-vue-libs": "^1.2.0", - "file-loader": "^0.11.2", - "flow-bin": "^0.54.0", - "hash-sum": "^1.0.2", - "he": "^1.1.1", - "http-server": "^0.10.0", - "jasmine": "^2.6.0", - "jasmine-core": "^2.6.3", - "karma": "^1.7.0", - "karma-chrome-launcher": "^2.1.1", - "karma-coverage": "^1.1.1", - "karma-firefox-launcher": "^1.0.1", - "karma-jasmine": "^1.1.0", - "karma-mocha-reporter": "^2.2.3", - "karma-phantomjs-launcher": "^1.0.4", - "karma-safari-launcher": "^1.0.0", - "karma-sauce-launcher": "^1.1.0", - "karma-sourcemap-loader": "^0.3.7", - "karma-webpack": "^2.0.3", - "lodash": "^4.17.4", - "lodash.template": "^4.4.0", - "lodash.uniq": "^4.5.0", - "lru-cache": "^4.1.1", - "nightwatch": "^0.9.16", - "nightwatch-helpers": "^1.2.0", - "phantomjs-prebuilt": "^2.1.14", - "resolve": "^1.3.3", - "rollup": "^0.50.0", - "rollup-plugin-alias": "^1.3.1", - "rollup-plugin-babel": "^3.0.2", - "rollup-plugin-buble": "^0.16.0", - "rollup-plugin-commonjs": "^8.0.2", - "rollup-plugin-flow-no-whitespace": "^1.0.0", - "rollup-plugin-node-resolve": "^3.0.0", - "rollup-plugin-replace": "^2.0.0", - "rollup-watch": "^4.0.0", - "selenium-server": "^2.53.1", - "serialize-javascript": "^1.3.0", - "shelljs": "^0.7.8", - "typescript": "^2.5.2", - "uglify-js": "^3.0.15", - "webpack": "^2.6.1", - "weex-js-runtime": "^0.23.0", - "weex-styler": "^0.3.0" + "dependencies": { + "@vue/compiler-sfc": "workspace:*", + "csstype": "^3.1.0" }, - "config": { - "commitizen": { - "path": "./node_modules/cz-conventional-changelog" - } + "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" } } diff --git a/packages/compiler-sfc/api-extractor.json b/packages/compiler-sfc/api-extractor.json new file mode 100644 index 00000000000..eda03ee2119 --- /dev/null +++ b/packages/compiler-sfc/api-extractor.json @@ -0,0 +1,64 @@ +{ + "$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 new file mode 100644 index 00000000000..d53b029c14f --- /dev/null +++ b/packages/compiler-sfc/package.json @@ -0,0 +1,37 @@ +{ + "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 new file mode 100644 index 00000000000..306cb8077a7 --- /dev/null +++ b/packages/compiler-sfc/src/babelUtils.ts @@ -0,0 +1,423 @@ +// 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 new file mode 100644 index 00000000000..275945a2bfa --- /dev/null +++ b/packages/compiler-sfc/src/compileScript.ts @@ -0,0 +1,1916 @@ +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 new file mode 100644 index 00000000000..92698982664 --- /dev/null +++ b/packages/compiler-sfc/src/compileStyle.ts @@ -0,0 +1,147 @@ +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 new file mode 100644 index 00000000000..ebc6f6c6f2a --- /dev/null +++ b/packages/compiler-sfc/src/compileTemplate.ts @@ -0,0 +1,205 @@ +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 new file mode 100644 index 00000000000..48f8cb70244 --- /dev/null +++ b/packages/compiler-sfc/src/cssVars.ts @@ -0,0 +1,179 @@ +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 new file mode 100644 index 00000000000..fc050c52e57 --- /dev/null +++ b/packages/compiler-sfc/src/index.ts @@ -0,0 +1,31 @@ +// 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 new file mode 100644 index 00000000000..4b73a7ab01f --- /dev/null +++ b/packages/compiler-sfc/src/parse.ts @@ -0,0 +1,129 @@ +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 new file mode 100644 index 00000000000..05489280e63 --- /dev/null +++ b/packages/compiler-sfc/src/parseComponent.ts @@ -0,0 +1,220 @@ +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 new file mode 100644 index 00000000000..25ea5b9109b --- /dev/null +++ b/packages/compiler-sfc/src/prefixIdentifiers.ts @@ -0,0 +1,82 @@ +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 new file mode 100644 index 00000000000..8161da01118 --- /dev/null +++ b/packages/compiler-sfc/src/rewriteDefault.ts @@ -0,0 +1,121 @@ +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 new file mode 100644 index 00000000000..55f17c386f3 --- /dev/null +++ b/packages/compiler-sfc/src/stylePlugins/scoped.ts @@ -0,0 +1,203 @@ +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 new file mode 100644 index 00000000000..67c4a3f0f35 --- /dev/null +++ b/packages/compiler-sfc/src/stylePlugins/trim.ts @@ -0,0 +1,18 @@ +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 new file mode 100644 index 00000000000..b59ea302e72 --- /dev/null +++ b/packages/compiler-sfc/src/stylePreprocessors.ts @@ -0,0 +1,135 @@ +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 new file mode 100644 index 00000000000..71e3cfa2fe9 --- /dev/null +++ b/packages/compiler-sfc/src/templateCompilerModules/assetUrl.ts @@ -0,0 +1,84 @@ +// 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 new file mode 100644 index 00000000000..bec3dcd7847 --- /dev/null +++ b/packages/compiler-sfc/src/templateCompilerModules/srcset.ts @@ -0,0 +1,76 @@ +// 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 new file mode 100644 index 00000000000..8a2d19c6ce7 --- /dev/null +++ b/packages/compiler-sfc/src/templateCompilerModules/utils.ts @@ -0,0 +1,86 @@ +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 new file mode 100644 index 00000000000..c62f74c7bb7 --- /dev/null +++ b/packages/compiler-sfc/src/types.ts @@ -0,0 +1,69 @@ +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 new file mode 100644 index 00000000000..e4b698dc4f5 --- /dev/null +++ b/packages/compiler-sfc/src/warn.ts @@ -0,0 +1,16 @@ +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 new file mode 100644 index 00000000000..4b81610a48d --- /dev/null +++ b/packages/compiler-sfc/test/__snapshots__/compileScript.spec.ts.snap @@ -0,0 +1,971 @@ +// 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 new file mode 100644 index 00000000000..a9071ac1f98 --- /dev/null +++ b/packages/compiler-sfc/test/__snapshots__/cssVars.spec.ts.snap @@ -0,0 +1,189 @@ +// 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 new file mode 100644 index 00000000000..6e04789bfd7 --- /dev/null +++ b/packages/compiler-sfc/test/compileScript.spec.ts @@ -0,0 +1,1635 @@ +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 new file mode 100644 index 00000000000..8ad69ef42ff --- /dev/null +++ b/packages/compiler-sfc/test/compileStyle.spec.ts @@ -0,0 +1,203 @@ +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 new file mode 100644 index 00000000000..a405d644a47 --- /dev/null +++ b/packages/compiler-sfc/test/compileTemplate.spec.ts @@ -0,0 +1,258 @@ +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 new file mode 100644 index 00000000000..86f4e969c91 --- /dev/null +++ b/packages/compiler-sfc/test/cssVars.spec.ts @@ -0,0 +1,247 @@ +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 new file mode 100644 index 00000000000..83f73483e0c --- /dev/null +++ b/packages/compiler-sfc/test/parseComponent.spec.ts @@ -0,0 +1,269 @@ +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 new file mode 100644 index 00000000000..2eb05c8c0d1 --- /dev/null +++ b/packages/compiler-sfc/test/prefixIdentifiers.spec.ts @@ -0,0 +1,97 @@ +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 new file mode 100644 index 00000000000..fd9e4ef4def --- /dev/null +++ b/packages/compiler-sfc/test/rewriteDefault.spec.ts @@ -0,0 +1,311 @@ +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 new file mode 100644 index 00000000000..46308e37466 --- /dev/null +++ b/packages/compiler-sfc/test/stylePluginScoped.spec.ts @@ -0,0 +1,137 @@ +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 new file mode 100644 index 00000000000..c0fca46c617 --- /dev/null +++ b/packages/compiler-sfc/test/tsconfig.json @@ -0,0 +1,7 @@ +{ + "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 new file mode 100644 index 00000000000..773158999de --- /dev/null +++ b/packages/compiler-sfc/test/util.ts @@ -0,0 +1,35 @@ +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/vue-server-renderer/README.md b/packages/server-renderer/README.md similarity index 100% rename from packages/vue-server-renderer/README.md rename to packages/server-renderer/README.md diff --git a/packages/server-renderer/client-plugin.d.ts b/packages/server-renderer/client-plugin.d.ts new file mode 100644 index 00000000000..d0d639a6463 --- /dev/null +++ b/packages/server-renderer/client-plugin.d.ts @@ -0,0 +1,3 @@ +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 new file mode 100644 index 00000000000..f3a053cf6b1 --- /dev/null +++ b/packages/server-renderer/index.js @@ -0,0 +1,26 @@ +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 new file mode 100644 index 00000000000..89da3496f13 --- /dev/null +++ b/packages/server-renderer/package.json @@ -0,0 +1,44 @@ +{ + "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 new file mode 100644 index 00000000000..d0d639a6463 --- /dev/null +++ b/packages/server-renderer/server-plugin.d.ts @@ -0,0 +1,3 @@ +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 new file mode 100644 index 00000000000..d7587321976 --- /dev/null +++ b/packages/server-renderer/src/bundle-renderer/create-bundle-renderer.ts @@ -0,0 +1,159 @@ +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 new file mode 100644 index 00000000000..6e9d063693b --- /dev/null +++ b/packages/server-renderer/src/bundle-renderer/create-bundle-runner.ts @@ -0,0 +1,158 @@ +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 new file mode 100644 index 00000000000..6f03ebb131a --- /dev/null +++ b/packages/server-renderer/src/bundle-renderer/source-map-support.ts @@ -0,0 +1,55 @@ +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 new file mode 100644 index 00000000000..094ac929e90 --- /dev/null +++ b/packages/server-renderer/src/compiler.ts @@ -0,0 +1,6 @@ +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 new file mode 100644 index 00000000000..05f5509b4cf --- /dev/null +++ b/packages/server-renderer/src/create-basic-renderer.ts @@ -0,0 +1,37 @@ +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 new file mode 100644 index 00000000000..a4b992eb8c6 --- /dev/null +++ b/packages/server-renderer/src/create-renderer.ts @@ -0,0 +1,164 @@ +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/src/platforms/web/server/directives/index.js b/packages/server-renderer/src/directives/index.ts similarity index 100% rename from src/platforms/web/server/directives/index.js rename to packages/server-renderer/src/directives/index.ts diff --git a/packages/server-renderer/src/directives/model.ts b/packages/server-renderer/src/directives/model.ts new file mode 100644 index 00000000000..fa54933e0af --- /dev/null +++ b/packages/server-renderer/src/directives/model.ts @@ -0,0 +1,42 @@ +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 new file mode 100644 index 00000000000..4a84fc6168c --- /dev/null +++ b/packages/server-renderer/src/directives/show.ts @@ -0,0 +1,12 @@ +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 new file mode 100644 index 00000000000..ea8babbe0b9 --- /dev/null +++ b/packages/server-renderer/src/index-basic.ts @@ -0,0 +1,12 @@ +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 new file mode 100644 index 00000000000..604c69938d0 --- /dev/null +++ b/packages/server-renderer/src/index.ts @@ -0,0 +1,30 @@ +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 new file mode 100644 index 00000000000..943e48526f7 --- /dev/null +++ b/packages/server-renderer/src/modules/attrs.ts @@ -0,0 +1,65 @@ +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 new file mode 100644 index 00000000000..5fe93fb304a --- /dev/null +++ b/packages/server-renderer/src/modules/class.ts @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000000..3c4d12625f3 --- /dev/null +++ b/packages/server-renderer/src/modules/dom-props.ts @@ -0,0 +1,50 @@ +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 new file mode 100644 index 00000000000..40dd926ff19 --- /dev/null +++ b/packages/server-renderer/src/modules/index.ts @@ -0,0 +1,6 @@ +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 new file mode 100644 index 00000000000..23c8e0d4237 --- /dev/null +++ b/packages/server-renderer/src/modules/style.ts @@ -0,0 +1,40 @@ +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 new file mode 100644 index 00000000000..36669e191d2 --- /dev/null +++ b/packages/server-renderer/src/optimizing-compiler/codegen.ts @@ -0,0 +1,260 @@ +// 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 new file mode 100644 index 00000000000..780d8277806 --- /dev/null +++ b/packages/server-renderer/src/optimizing-compiler/index.ts @@ -0,0 +1,19 @@ +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 new file mode 100644 index 00000000000..3ece915ead4 --- /dev/null +++ b/packages/server-renderer/src/optimizing-compiler/modules.ts @@ -0,0 +1,118 @@ +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 new file mode 100644 index 00000000000..dfb9fb52388 --- /dev/null +++ b/packages/server-renderer/src/optimizing-compiler/optimizer.ts @@ -0,0 +1,140 @@ +/** + * 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 new file mode 100644 index 00000000000..7809d4e0a31 --- /dev/null +++ b/packages/server-renderer/src/optimizing-compiler/runtime-helpers.ts @@ -0,0 +1,148 @@ +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 new file mode 100644 index 00000000000..cd35727f412 --- /dev/null +++ b/packages/server-renderer/src/render-context.ts @@ -0,0 +1,134 @@ +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 new file mode 100644 index 00000000000..6cbe6298cca --- /dev/null +++ b/packages/server-renderer/src/render-stream.ts @@ -0,0 +1,100 @@ +/** + * 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 new file mode 100644 index 00000000000..b1116840ed5 --- /dev/null +++ b/packages/server-renderer/src/render.ts @@ -0,0 +1,459 @@ +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 new file mode 100644 index 00000000000..e09fc3a47ff --- /dev/null +++ b/packages/server-renderer/src/template-renderer/create-async-file-mapper.ts @@ -0,0 +1,53 @@ +/** + * 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 new file mode 100644 index 00000000000..124ece4cf51 --- /dev/null +++ b/packages/server-renderer/src/template-renderer/index.ts @@ -0,0 +1,306 @@ +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 new file mode 100644 index 00000000000..29baf23b546 --- /dev/null +++ b/packages/server-renderer/src/template-renderer/parse-template.ts @@ -0,0 +1,40 @@ +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 new file mode 100644 index 00000000000..38fe7ba37f4 --- /dev/null +++ b/packages/server-renderer/src/template-renderer/template-stream.ts @@ -0,0 +1,81 @@ +// 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 new file mode 100644 index 00000000000..777abb659f6 --- /dev/null +++ b/packages/server-renderer/src/util.ts @@ -0,0 +1,114 @@ +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 = { + '<': '<', + '>': '>', + '"': '"', + '&': '&' +} + +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 new file mode 100644 index 00000000000..d95061e0279 --- /dev/null +++ b/packages/server-renderer/src/webpack-plugin/client.ts @@ -0,0 +1,76 @@ +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 new file mode 100644 index 00000000000..439335ef9cd --- /dev/null +++ b/packages/server-renderer/src/webpack-plugin/server.ts @@ -0,0 +1,74 @@ +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 new file mode 100644 index 00000000000..1af5a7f28e5 --- /dev/null +++ b/packages/server-renderer/src/webpack-plugin/util.ts @@ -0,0 +1,76 @@ +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 new file mode 100644 index 00000000000..925c0f96588 --- /dev/null +++ b/packages/server-renderer/src/write.ts @@ -0,0 +1,51 @@ +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/test/ssr/async-loader.js b/packages/server-renderer/test/async-loader.js similarity index 100% rename from test/ssr/async-loader.js rename to packages/server-renderer/test/async-loader.js diff --git a/packages/server-renderer/test/compile-with-webpack.ts b/packages/server-renderer/test/compile-with-webpack.ts new file mode 100644 index 00000000000..37467595619 --- /dev/null +++ b/packages/server-renderer/test/compile-with-webpack.ts @@ -0,0 +1,73 @@ +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 new file mode 100644 index 00000000000..08c758522ef --- /dev/null +++ b/packages/server-renderer/test/fixtures/app.js @@ -0,0 +1,14 @@ +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/test/ssr/fixtures/async-bar.js b/packages/server-renderer/test/fixtures/async-bar.js similarity index 79% rename from test/ssr/fixtures/async-bar.js rename to packages/server-renderer/test/fixtures/async-bar.js index 5eb93c12eda..23349f8a9ee 100644 --- a/test/ssr/fixtures/async-bar.js +++ b/packages/server-renderer/test/fixtures/async-bar.js @@ -1,8 +1,8 @@ module.exports = { - beforeCreate () { + beforeCreate() { this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__') }, - render (h) { + render(h) { return h('div', 'async bar') } } diff --git a/test/ssr/fixtures/async-foo.js b/packages/server-renderer/test/fixtures/async-foo.js similarity index 81% rename from test/ssr/fixtures/async-foo.js rename to packages/server-renderer/test/fixtures/async-foo.js index 929d73f43c5..18e5b235e41 100644 --- a/test/ssr/fixtures/async-foo.js +++ b/packages/server-renderer/test/fixtures/async-foo.js @@ -3,11 +3,11 @@ import './test.css' import font from './test.woff2' import image from './test.png' -module.exports = { - beforeCreate () { +export default { + beforeCreate() { this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__') }, - render (h) { + 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 new file mode 100644 index 00000000000..7c0d1247280 --- /dev/null +++ b/packages/server-renderer/test/fixtures/cache-opt-out.js @@ -0,0 +1,18 @@ +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 new file mode 100644 index 00000000000..48d1410b2b3 --- /dev/null +++ b/packages/server-renderer/test/fixtures/cache.js @@ -0,0 +1,18 @@ +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/test/ssr/fixtures/error.js b/packages/server-renderer/test/fixtures/error.js similarity index 100% rename from test/ssr/fixtures/error.js rename to packages/server-renderer/test/fixtures/error.js diff --git a/packages/server-renderer/test/fixtures/nested-cache.js b/packages/server-renderer/test/fixtures/nested-cache.js new file mode 100644 index 00000000000..be72490d666 --- /dev/null +++ b/packages/server-renderer/test/fixtures/nested-cache.js @@ -0,0 +1,51 @@ +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/test/ssr/fixtures/promise-rejection.js b/packages/server-renderer/test/fixtures/promise-rejection.js similarity index 100% rename from test/ssr/fixtures/promise-rejection.js rename to packages/server-renderer/test/fixtures/promise-rejection.js diff --git a/packages/server-renderer/test/fixtures/split.js b/packages/server-renderer/test/fixtures/split.js new file mode 100644 index 00000000000..1d33acc4abe --- /dev/null +++ b/packages/server-renderer/test/fixtures/split.js @@ -0,0 +1,23 @@ +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/test/ssr/fixtures/test.css b/packages/server-renderer/test/fixtures/test.css similarity index 100% rename from test/ssr/fixtures/test.css rename to packages/server-renderer/test/fixtures/test.css diff --git a/test/ssr/fixtures/test.png b/packages/server-renderer/test/fixtures/test.png similarity index 100% rename from test/ssr/fixtures/test.png rename to packages/server-renderer/test/fixtures/test.png diff --git a/test/ssr/fixtures/test.woff2 b/packages/server-renderer/test/fixtures/test.woff2 similarity index 100% rename from test/ssr/fixtures/test.woff2 rename to packages/server-renderer/test/fixtures/test.woff2 diff --git a/packages/server-renderer/test/ssr-basic-renderer.spec.ts b/packages/server-renderer/test/ssr-basic-renderer.spec.ts new file mode 100644 index 00000000000..a26820af5be --- /dev/null +++ b/packages/server-renderer/test/ssr-basic-renderer.spec.ts @@ -0,0 +1,83 @@ +// @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 new file mode 100644 index 00000000000..608fc675c64 --- /dev/null +++ b/packages/server-renderer/test/ssr-bundle-render.spec.ts @@ -0,0 +1,325 @@ +// @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 new file mode 100644 index 00000000000..773fa9dd650 --- /dev/null +++ b/packages/server-renderer/test/ssr-reactivity.spec.ts @@ -0,0 +1,196 @@ +// @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 new file mode 100644 index 00000000000..638befa44b5 --- /dev/null +++ b/packages/server-renderer/test/ssr-stream.spec.ts @@ -0,0 +1,143 @@ +// @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 new file mode 100644 index 00000000000..e9749c05cca --- /dev/null +++ b/packages/server-renderer/test/ssr-string.spec.ts @@ -0,0 +1,2166 @@ +// @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 <span>rendering</span></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"><span>foo</span></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><span>foo</span></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><foo></div>` + }, + res => { + expect(res).toBe(`<div data-server-rendered="true"><foo></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 new file mode 100644 index 00000000000..b173c439e99 --- /dev/null +++ b/packages/server-renderer/test/ssr-template.spec.ts @@ -0,0 +1,630 @@ +// @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><script>hacks</script></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><script>hacks</script></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><script>hacks</script></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><script>hacks</script></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><script>hacks</script></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 new file mode 100644 index 00000000000..96cc3dcb9a0 --- /dev/null +++ b/packages/server-renderer/test/tsconfig.json @@ -0,0 +1,7 @@ +{ + "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 new file mode 100644 index 00000000000..0ba53383478 --- /dev/null +++ b/packages/server-renderer/test/utils.ts @@ -0,0 +1,22 @@ +/** + * 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 new file mode 100644 index 00000000000..6932cd1430f --- /dev/null +++ b/packages/server-renderer/types/index.d.ts @@ -0,0 +1,53 @@ +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 new file mode 100644 index 00000000000..423a58b4d81 --- /dev/null +++ b/packages/server-renderer/types/plugin.d.ts @@ -0,0 +1,11 @@ +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 new file mode 100644 index 00000000000..7a353066eda --- /dev/null +++ b/packages/server-renderer/types/test.ts @@ -0,0 +1,133 @@ +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 new file mode 100644 index 00000000000..465615893cd --- /dev/null +++ b/packages/server-renderer/types/tsconfig.json @@ -0,0 +1,14 @@ +{ + "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 new file mode 100644 index 00000000000..ac14f72e9fc --- /dev/null +++ b/packages/template-compiler/README.md @@ -0,0 +1,162 @@ +# 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 new file mode 100644 index 00000000000..378a17dfa24 --- /dev/null +++ b/packages/template-compiler/index.js @@ -0,0 +1,32 @@ +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 new file mode 100644 index 00000000000..78d6a74e42e --- /dev/null +++ b/packages/template-compiler/package.json @@ -0,0 +1,35 @@ +{ + "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 new file mode 100644 index 00000000000..105a52c9e94 --- /dev/null +++ b/packages/template-compiler/types/index.d.ts @@ -0,0 +1,247 @@ +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 new file mode 100644 index 00000000000..09202c90c5e --- /dev/null +++ b/packages/template-compiler/types/test.ts @@ -0,0 +1,90 @@ +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 new file mode 100644 index 00000000000..5e89be1ef9d --- /dev/null +++ b/packages/template-compiler/types/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "noEmit": true, + "paths": { + "vue": ["../../../types/index.d.ts"] + } + }, + "include": ["**/*.ts", "../../../types"] +} diff --git a/packages/vue-server-renderer/basic.js b/packages/vue-server-renderer/basic.js deleted file mode 100644 index fcd36e62be3..00000000000 --- a/packages/vue-server-renderer/basic.js +++ /dev/null @@ -1,8186 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.renderVueComponentToString = factory()); -}(this, (function () { 'use strict'; - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - - - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ - - -/** - * Convert an Array-like object to a real Array. - */ - - -/** - * Mix properties into target object. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -/* */ - -var 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,http-equiv,' + - 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + - 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,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,type,usemap,value,width,wrap' -); - -/* istanbul ignore next */ -var isRenderableAttr = function (name) { - return ( - isAttr(name) || - name.indexOf('data-') === 0 || - name.indexOf('aria-') === 0 - ) -}; -var propsToAttrMap = { - acceptCharset: 'accept-charset', - className: 'class', - htmlFor: 'for', - httpEquiv: 'http-equiv' -}; - -var ESC = { - '<': '<', - '>': '>', - '"': '"', - '&': '&' -}; - -function escape (s) { - return s.replace(/[<>"&]/g, escapeChar) -} - -function escapeChar (a) { - return ESC[a] || a -} - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - - - - - - - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function renderAttrs (node) { - var attrs = node.data.attrs; - var res = ''; - - var opts = node.parent && node.parent.componentOptions; - if (isUndef(opts) || opts.Ctor.options.inheritAttrs !== false) { - var parent = node.parent; - while (isDef(parent)) { - if (isDef(parent.data) && isDef(parent.data.attrs)) { - attrs = extend(extend({}, attrs), parent.data.attrs); - } - parent = parent.parent; - } - } - - if (isUndef(attrs)) { - return res - } - - for (var key in attrs) { - if (key === 'style') { - // leave it to the style module - continue - } - res += renderAttr(key, attrs[key]); - } - return res -} - -function renderAttr (key, value) { - if (isBooleanAttr(key)) { - if (!isFalsyAttrValue(value)) { - return (" " + key + "=\"" + key + "\"") - } - } else if (isEnumeratedAttr(key)) { - return (" " + key + "=\"" + (isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true') + "\"") - } else if (!isFalsyAttrValue(value)) { - return (" " + key + "=\"" + (escape(String(value))) + "\"") - } - return '' -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* */ - -function renderDOMProps (node) { - var props = node.data.domProps; - var res = ''; - - var 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 - } - - var attrs = node.data.attrs; - for (var 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, props[key], false); - } else { - // $flow-disable-line (WTF?) - var 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) { - var child = new VNode(undefined, undefined, undefined, text); - child.raw = raw; - node.children = [child]; -} - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ - - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - - -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: "development" !== 'production', - - /** - * Whether to enable devtools - */ - devtools: "development" !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -{ - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if ("development" !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ - - -/** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ -function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -{ - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - "development" !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && "development" !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if ("development" !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if ("development" !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if ("development" !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ - - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass$1(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass$1 ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - - - -var 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. -var 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 -); - -var isPreTag = function (tag) { return tag === 'pre'; }; - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - - - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ - -/* */ - -function renderClass (node) { - var classList = genClassForVnode(node); - if (classList !== '') { - return (" class=\"" + (escape(classList)) + "\"") - } -} - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -function genStyle (style) { - var styleText = ''; - for (var key in style) { - var value = style[key]; - var hyphenatedKey = hyphenate(key); - if (Array.isArray(value)) { - for (var i = 0, len = value.length; i < len; i++) { - styleText += hyphenatedKey + ":" + (value[i]) + ";"; - } - } else { - styleText += hyphenatedKey + ":" + value + ";"; - } - } - return styleText -} - -function renderStyle (vnode) { - var styleText = genStyle(getStyle(vnode, false)); - if (styleText !== '') { - return (" style=" + (JSON.stringify(escape(styleText)))) - } -} - -var modules = [ - renderAttrs, - renderDOMProps, - renderClass, - renderStyle -]; - -/* */ - -function show (node, dir) { - if (!dir.value) { - var style = node.data.style || (node.data.style = {}); - style.display = 'none'; - } -} - -/* */ - -// 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. -function model (node, dir) { - if (!node.children) { return } - var value = dir.value; - var isMultiple = node.data.attrs && node.data.attrs.multiple; - for (var i = 0, l = node.children.length; i < l; i++) { - var option = node.children[i]; - if (option.tag === 'option') { - if (isMultiple) { - var 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) { - var 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) { - var data = option.data || (option.data = {}); - var attrs = data.attrs || (data.attrs = {}); - attrs.selected = ''; -} - -var directives = { - show: show, - model: model -}; - -/* */ - -var 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) -var 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 -var 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' -); - -/* */ - -var MAX_STACK_DEPTH = 1000; -var noop$1 = function (_) { return _; }; - -var defer = typeof process !== 'undefined' && process.nextTick - ? process.nextTick - : typeof Promise !== 'undefined' - ? function (fn) { return Promise.resolve().then(fn); } - : typeof setTimeout !== 'undefined' - ? setTimeout - : noop$1; - -if (defer === noop$1) { - 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.' - ) -} - -function createWriteFunction ( - write, - onError -) { - var stackDepth = 0; - var cachedWrite = function (text, next) { - if (text && cachedWrite.caching) { - cachedWrite.cacheBuffer[cachedWrite.cacheBuffer.length - 1] += text; - } - var waitForNext = write(text, next); - if (waitForNext !== true) { - if (stackDepth >= MAX_STACK_DEPTH) { - defer(function () { - try { next(); } catch (e) { - onError(e); - } - }); - } else { - stackDepth++; - next(); - stackDepth--; - } - } - }; - cachedWrite.caching = false; - cachedWrite.cacheBuffer = []; - cachedWrite.componentBuffer = []; - return cachedWrite -} - -/* */ - -var RenderContext = function RenderContext (options) { - 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; - - var 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); -}; - -RenderContext.prototype.next = function next () { - var lastState = this.renderStates[this.renderStates.length - 1]; - if (isUndef(lastState)) { - return this.done() - } - switch (lastState.type) { - case 'Element': - var children = lastState.children; - var total = lastState.total; - var rendered = lastState.rendered++; - if (rendered < total) { - this.renderNode(children[rendered], false, this); - } else { - this.renderStates.pop(); - this.write(lastState.endTag, this.next); - } - break - case 'Component': - this.renderStates.pop(); - this.activeInstance = lastState.prevActive; - this.next(); - break - case 'ComponentWithCache': - this.renderStates.pop(); - var buffer = lastState.buffer; - var bufferIndex = lastState.bufferIndex; - var componentBuffer = lastState.componentBuffer; - var key = lastState.key; - var 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. - this.write.caching = false; - } else { - // parent component is also being cached, - // merge self into parent's result - buffer[bufferIndex - 1] += result.html; - var prev = componentBuffer[bufferIndex - 1]; - result.components.forEach(function (c) { return prev.add(c); }); - } - buffer.length = bufferIndex; - componentBuffer.length = bufferIndex; - this.next(); - break - } -}; - -function normalizeAsync (cache, method) { - var fn = cache[method]; - if (isUndef(fn)) { - return - } else if (fn.length > 1) { - return function (key, cb) { return fn.call(cache, key, cb); } - } else { - return function (key, cb) { return cb(fn.call(cache, key)); } - } -} - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - "development" !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - if ("development" !== 'production' && staticClass) { - var expression = parseText(staticClass, options.delimiters); - if (expression) { - 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">.' - ); - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass); - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } -} - -function genData (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -var klass = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData -}; - -/* */ - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - if (staticStyle) { - /* istanbul ignore if */ - { - var expression = parseText(staticStyle, options.delimiters); - if (expression) { - 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.staticStyle = JSON.stringify(parseStyleText(staticStyle)); - } - - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:(" + (el.styleBinding) + "),"; - } - return data -} - -var style = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$1 -}; - -var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - - - - - -function createCommonjsModule(fn, module) { - return module = { exports: {} }, fn(module, module.exports), module.exports; -} - -var he = createCommonjsModule(function (module, exports) { -/*! https://mths.be/he v1.1.1 by @mathias | MIT license */ -(function(root) { - - // Detect free variables `exports`. - var freeExports = 'object' == 'object' && exports; - - // Detect free variable `module`. - var freeModule = 'object' == 'object' && module && - module.exports == freeExports && module; - - // Detect free variable `global`, from Node.js or Browserified code, - // and use it as `root`. - var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal; - if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { - root = freeGlobal; - } - - /*--------------------------------------------------------------------------*/ - - // All astral symbols. - var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; - // All ASCII symbols (not just printable ASCII) except those listed in the - // first column of the overrides table. - // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides - var regexAsciiWhitelist = /[\x01-\x7F]/g; - // All BMP symbols that are not ASCII newlines, printable ASCII symbols, or - // code points listed in the first column of the overrides table on - // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides. - var regexBmpWhitelist = /[\x01-\t\x0B\f\x0E-\x1F\x7F\x81\x8D\x8F\x90\x9D\xA0-\uFFFF]/g; - - var regexEncodeNonAscii = /<\u20D2|=\u20E5|>\u20D2|\u205F\u200A|\u219D\u0338|\u2202\u0338|\u2220\u20D2|\u2229\uFE00|\u222A\uFE00|\u223C\u20D2|\u223D\u0331|\u223E\u0333|\u2242\u0338|\u224B\u0338|\u224D\u20D2|\u224E\u0338|\u224F\u0338|\u2250\u0338|\u2261\u20E5|\u2264\u20D2|\u2265\u20D2|\u2266\u0338|\u2267\u0338|\u2268\uFE00|\u2269\uFE00|\u226A\u0338|\u226A\u20D2|\u226B\u0338|\u226B\u20D2|\u227F\u0338|\u2282\u20D2|\u2283\u20D2|\u228A\uFE00|\u228B\uFE00|\u228F\u0338|\u2290\u0338|\u2293\uFE00|\u2294\uFE00|\u22B4\u20D2|\u22B5\u20D2|\u22D8\u0338|\u22D9\u0338|\u22DA\uFE00|\u22DB\uFE00|\u22F5\u0338|\u22F9\u0338|\u2933\u0338|\u29CF\u0338|\u29D0\u0338|\u2A6D\u0338|\u2A70\u0338|\u2A7D\u0338|\u2A7E\u0338|\u2AA1\u0338|\u2AA2\u0338|\u2AAC\uFE00|\u2AAD\uFE00|\u2AAF\u0338|\u2AB0\u0338|\u2AC5\u0338|\u2AC6\u0338|\u2ACB\uFE00|\u2ACC\uFE00|\u2AFD\u20E5|[\xA0-\u0113\u0116-\u0122\u0124-\u012B\u012E-\u014D\u0150-\u017E\u0192\u01B5\u01F5\u0237\u02C6\u02C7\u02D8-\u02DD\u0311\u0391-\u03A1\u03A3-\u03A9\u03B1-\u03C9\u03D1\u03D2\u03D5\u03D6\u03DC\u03DD\u03F0\u03F1\u03F5\u03F6\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E\u045F\u2002-\u2005\u2007-\u2010\u2013-\u2016\u2018-\u201A\u201C-\u201E\u2020-\u2022\u2025\u2026\u2030-\u2035\u2039\u203A\u203E\u2041\u2043\u2044\u204F\u2057\u205F-\u2063\u20AC\u20DB\u20DC\u2102\u2105\u210A-\u2113\u2115-\u211E\u2122\u2124\u2127-\u2129\u212C\u212D\u212F-\u2131\u2133-\u2138\u2145-\u2148\u2153-\u215E\u2190-\u219B\u219D-\u21A7\u21A9-\u21AE\u21B0-\u21B3\u21B5-\u21B7\u21BA-\u21DB\u21DD\u21E4\u21E5\u21F5\u21FD-\u2205\u2207-\u2209\u220B\u220C\u220F-\u2214\u2216-\u2218\u221A\u221D-\u2238\u223A-\u2257\u2259\u225A\u225C\u225F-\u2262\u2264-\u228B\u228D-\u229B\u229D-\u22A5\u22A7-\u22B0\u22B2-\u22BB\u22BD-\u22DB\u22DE-\u22E3\u22E6-\u22F7\u22F9-\u22FE\u2305\u2306\u2308-\u2310\u2312\u2313\u2315\u2316\u231C-\u231F\u2322\u2323\u232D\u232E\u2336\u233D\u233F\u237C\u23B0\u23B1\u23B4-\u23B6\u23DC-\u23DF\u23E2\u23E7\u2423\u24C8\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2550-\u256C\u2580\u2584\u2588\u2591-\u2593\u25A1\u25AA\u25AB\u25AD\u25AE\u25B1\u25B3-\u25B5\u25B8\u25B9\u25BD-\u25BF\u25C2\u25C3\u25CA\u25CB\u25EC\u25EF\u25F8-\u25FC\u2605\u2606\u260E\u2640\u2642\u2660\u2663\u2665\u2666\u266A\u266D-\u266F\u2713\u2717\u2720\u2736\u2758\u2772\u2773\u27C8\u27C9\u27E6-\u27ED\u27F5-\u27FA\u27FC\u27FF\u2902-\u2905\u290C-\u2913\u2916\u2919-\u2920\u2923-\u292A\u2933\u2935-\u2939\u293C\u293D\u2945\u2948-\u294B\u294E-\u2976\u2978\u2979\u297B-\u297F\u2985\u2986\u298B-\u2996\u299A\u299C\u299D\u29A4-\u29B7\u29B9\u29BB\u29BC\u29BE-\u29C5\u29C9\u29CD-\u29D0\u29DC-\u29DE\u29E3-\u29E5\u29EB\u29F4\u29F6\u2A00-\u2A02\u2A04\u2A06\u2A0C\u2A0D\u2A10-\u2A17\u2A22-\u2A27\u2A29\u2A2A\u2A2D-\u2A31\u2A33-\u2A3C\u2A3F\u2A40\u2A42-\u2A4D\u2A50\u2A53-\u2A58\u2A5A-\u2A5D\u2A5F\u2A66\u2A6A\u2A6D-\u2A75\u2A77-\u2A9A\u2A9D-\u2AA2\u2AA4-\u2AB0\u2AB3-\u2AC8\u2ACB\u2ACC\u2ACF-\u2ADB\u2AE4\u2AE6-\u2AE9\u2AEB-\u2AF3\u2AFD\uFB00-\uFB04]|\uD835[\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDCCF\uDD04\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDD6B]/g; - var encodeMap = {'\xAD':'shy','\u200C':'zwnj','\u200D':'zwj','\u200E':'lrm','\u2063':'ic','\u2062':'it','\u2061':'af','\u200F':'rlm','\u200B':'ZeroWidthSpace','\u2060':'NoBreak','\u0311':'DownBreve','\u20DB':'tdot','\u20DC':'DotDot','\t':'Tab','\n':'NewLine','\u2008':'puncsp','\u205F':'MediumSpace','\u2009':'thinsp','\u200A':'hairsp','\u2004':'emsp13','\u2002':'ensp','\u2005':'emsp14','\u2003':'emsp','\u2007':'numsp','\xA0':'nbsp','\u205F\u200A':'ThickSpace','\u203E':'oline','_':'lowbar','\u2010':'dash','\u2013':'ndash','\u2014':'mdash','\u2015':'horbar',',':'comma',';':'semi','\u204F':'bsemi',':':'colon','\u2A74':'Colone','!':'excl','\xA1':'iexcl','?':'quest','\xBF':'iquest','.':'period','\u2025':'nldr','\u2026':'mldr','\xB7':'middot','\'':'apos','\u2018':'lsquo','\u2019':'rsquo','\u201A':'sbquo','\u2039':'lsaquo','\u203A':'rsaquo','"':'quot','\u201C':'ldquo','\u201D':'rdquo','\u201E':'bdquo','\xAB':'laquo','\xBB':'raquo','(':'lpar',')':'rpar','[':'lsqb',']':'rsqb','{':'lcub','}':'rcub','\u2308':'lceil','\u2309':'rceil','\u230A':'lfloor','\u230B':'rfloor','\u2985':'lopar','\u2986':'ropar','\u298B':'lbrke','\u298C':'rbrke','\u298D':'lbrkslu','\u298E':'rbrksld','\u298F':'lbrksld','\u2990':'rbrkslu','\u2991':'langd','\u2992':'rangd','\u2993':'lparlt','\u2994':'rpargt','\u2995':'gtlPar','\u2996':'ltrPar','\u27E6':'lobrk','\u27E7':'robrk','\u27E8':'lang','\u27E9':'rang','\u27EA':'Lang','\u27EB':'Rang','\u27EC':'loang','\u27ED':'roang','\u2772':'lbbrk','\u2773':'rbbrk','\u2016':'Vert','\xA7':'sect','\xB6':'para','@':'commat','*':'ast','/':'sol','undefined':null,'&':'amp','#':'num','%':'percnt','\u2030':'permil','\u2031':'pertenk','\u2020':'dagger','\u2021':'Dagger','\u2022':'bull','\u2043':'hybull','\u2032':'prime','\u2033':'Prime','\u2034':'tprime','\u2057':'qprime','\u2035':'bprime','\u2041':'caret','`':'grave','\xB4':'acute','\u02DC':'tilde','^':'Hat','\xAF':'macr','\u02D8':'breve','\u02D9':'dot','\xA8':'die','\u02DA':'ring','\u02DD':'dblac','\xB8':'cedil','\u02DB':'ogon','\u02C6':'circ','\u02C7':'caron','\xB0':'deg','\xA9':'copy','\xAE':'reg','\u2117':'copysr','\u2118':'wp','\u211E':'rx','\u2127':'mho','\u2129':'iiota','\u2190':'larr','\u219A':'nlarr','\u2192':'rarr','\u219B':'nrarr','\u2191':'uarr','\u2193':'darr','\u2194':'harr','\u21AE':'nharr','\u2195':'varr','\u2196':'nwarr','\u2197':'nearr','\u2198':'searr','\u2199':'swarr','\u219D':'rarrw','\u219D\u0338':'nrarrw','\u219E':'Larr','\u219F':'Uarr','\u21A0':'Rarr','\u21A1':'Darr','\u21A2':'larrtl','\u21A3':'rarrtl','\u21A4':'mapstoleft','\u21A5':'mapstoup','\u21A6':'map','\u21A7':'mapstodown','\u21A9':'larrhk','\u21AA':'rarrhk','\u21AB':'larrlp','\u21AC':'rarrlp','\u21AD':'harrw','\u21B0':'lsh','\u21B1':'rsh','\u21B2':'ldsh','\u21B3':'rdsh','\u21B5':'crarr','\u21B6':'cularr','\u21B7':'curarr','\u21BA':'olarr','\u21BB':'orarr','\u21BC':'lharu','\u21BD':'lhard','\u21BE':'uharr','\u21BF':'uharl','\u21C0':'rharu','\u21C1':'rhard','\u21C2':'dharr','\u21C3':'dharl','\u21C4':'rlarr','\u21C5':'udarr','\u21C6':'lrarr','\u21C7':'llarr','\u21C8':'uuarr','\u21C9':'rrarr','\u21CA':'ddarr','\u21CB':'lrhar','\u21CC':'rlhar','\u21D0':'lArr','\u21CD':'nlArr','\u21D1':'uArr','\u21D2':'rArr','\u21CF':'nrArr','\u21D3':'dArr','\u21D4':'iff','\u21CE':'nhArr','\u21D5':'vArr','\u21D6':'nwArr','\u21D7':'neArr','\u21D8':'seArr','\u21D9':'swArr','\u21DA':'lAarr','\u21DB':'rAarr','\u21DD':'zigrarr','\u21E4':'larrb','\u21E5':'rarrb','\u21F5':'duarr','\u21FD':'loarr','\u21FE':'roarr','\u21FF':'hoarr','\u2200':'forall','\u2201':'comp','\u2202':'part','\u2202\u0338':'npart','\u2203':'exist','\u2204':'nexist','\u2205':'empty','\u2207':'Del','\u2208':'in','\u2209':'notin','\u220B':'ni','\u220C':'notni','\u03F6':'bepsi','\u220F':'prod','\u2210':'coprod','\u2211':'sum','+':'plus','\xB1':'pm','\xF7':'div','\xD7':'times','<':'lt','\u226E':'nlt','<\u20D2':'nvlt','=':'equals','\u2260':'ne','=\u20E5':'bne','\u2A75':'Equal','>':'gt','\u226F':'ngt','>\u20D2':'nvgt','\xAC':'not','|':'vert','\xA6':'brvbar','\u2212':'minus','\u2213':'mp','\u2214':'plusdo','\u2044':'frasl','\u2216':'setmn','\u2217':'lowast','\u2218':'compfn','\u221A':'Sqrt','\u221D':'prop','\u221E':'infin','\u221F':'angrt','\u2220':'ang','\u2220\u20D2':'nang','\u2221':'angmsd','\u2222':'angsph','\u2223':'mid','\u2224':'nmid','\u2225':'par','\u2226':'npar','\u2227':'and','\u2228':'or','\u2229':'cap','\u2229\uFE00':'caps','\u222A':'cup','\u222A\uFE00':'cups','\u222B':'int','\u222C':'Int','\u222D':'tint','\u2A0C':'qint','\u222E':'oint','\u222F':'Conint','\u2230':'Cconint','\u2231':'cwint','\u2232':'cwconint','\u2233':'awconint','\u2234':'there4','\u2235':'becaus','\u2236':'ratio','\u2237':'Colon','\u2238':'minusd','\u223A':'mDDot','\u223B':'homtht','\u223C':'sim','\u2241':'nsim','\u223C\u20D2':'nvsim','\u223D':'bsim','\u223D\u0331':'race','\u223E':'ac','\u223E\u0333':'acE','\u223F':'acd','\u2240':'wr','\u2242':'esim','\u2242\u0338':'nesim','\u2243':'sime','\u2244':'nsime','\u2245':'cong','\u2247':'ncong','\u2246':'simne','\u2248':'ap','\u2249':'nap','\u224A':'ape','\u224B':'apid','\u224B\u0338':'napid','\u224C':'bcong','\u224D':'CupCap','\u226D':'NotCupCap','\u224D\u20D2':'nvap','\u224E':'bump','\u224E\u0338':'nbump','\u224F':'bumpe','\u224F\u0338':'nbumpe','\u2250':'doteq','\u2250\u0338':'nedot','\u2251':'eDot','\u2252':'efDot','\u2253':'erDot','\u2254':'colone','\u2255':'ecolon','\u2256':'ecir','\u2257':'cire','\u2259':'wedgeq','\u225A':'veeeq','\u225C':'trie','\u225F':'equest','\u2261':'equiv','\u2262':'nequiv','\u2261\u20E5':'bnequiv','\u2264':'le','\u2270':'nle','\u2264\u20D2':'nvle','\u2265':'ge','\u2271':'nge','\u2265\u20D2':'nvge','\u2266':'lE','\u2266\u0338':'nlE','\u2267':'gE','\u2267\u0338':'ngE','\u2268\uFE00':'lvnE','\u2268':'lnE','\u2269':'gnE','\u2269\uFE00':'gvnE','\u226A':'ll','\u226A\u0338':'nLtv','\u226A\u20D2':'nLt','\u226B':'gg','\u226B\u0338':'nGtv','\u226B\u20D2':'nGt','\u226C':'twixt','\u2272':'lsim','\u2274':'nlsim','\u2273':'gsim','\u2275':'ngsim','\u2276':'lg','\u2278':'ntlg','\u2277':'gl','\u2279':'ntgl','\u227A':'pr','\u2280':'npr','\u227B':'sc','\u2281':'nsc','\u227C':'prcue','\u22E0':'nprcue','\u227D':'sccue','\u22E1':'nsccue','\u227E':'prsim','\u227F':'scsim','\u227F\u0338':'NotSucceedsTilde','\u2282':'sub','\u2284':'nsub','\u2282\u20D2':'vnsub','\u2283':'sup','\u2285':'nsup','\u2283\u20D2':'vnsup','\u2286':'sube','\u2288':'nsube','\u2287':'supe','\u2289':'nsupe','\u228A\uFE00':'vsubne','\u228A':'subne','\u228B\uFE00':'vsupne','\u228B':'supne','\u228D':'cupdot','\u228E':'uplus','\u228F':'sqsub','\u228F\u0338':'NotSquareSubset','\u2290':'sqsup','\u2290\u0338':'NotSquareSuperset','\u2291':'sqsube','\u22E2':'nsqsube','\u2292':'sqsupe','\u22E3':'nsqsupe','\u2293':'sqcap','\u2293\uFE00':'sqcaps','\u2294':'sqcup','\u2294\uFE00':'sqcups','\u2295':'oplus','\u2296':'ominus','\u2297':'otimes','\u2298':'osol','\u2299':'odot','\u229A':'ocir','\u229B':'oast','\u229D':'odash','\u229E':'plusb','\u229F':'minusb','\u22A0':'timesb','\u22A1':'sdotb','\u22A2':'vdash','\u22AC':'nvdash','\u22A3':'dashv','\u22A4':'top','\u22A5':'bot','\u22A7':'models','\u22A8':'vDash','\u22AD':'nvDash','\u22A9':'Vdash','\u22AE':'nVdash','\u22AA':'Vvdash','\u22AB':'VDash','\u22AF':'nVDash','\u22B0':'prurel','\u22B2':'vltri','\u22EA':'nltri','\u22B3':'vrtri','\u22EB':'nrtri','\u22B4':'ltrie','\u22EC':'nltrie','\u22B4\u20D2':'nvltrie','\u22B5':'rtrie','\u22ED':'nrtrie','\u22B5\u20D2':'nvrtrie','\u22B6':'origof','\u22B7':'imof','\u22B8':'mumap','\u22B9':'hercon','\u22BA':'intcal','\u22BB':'veebar','\u22BD':'barvee','\u22BE':'angrtvb','\u22BF':'lrtri','\u22C0':'Wedge','\u22C1':'Vee','\u22C2':'xcap','\u22C3':'xcup','\u22C4':'diam','\u22C5':'sdot','\u22C6':'Star','\u22C7':'divonx','\u22C8':'bowtie','\u22C9':'ltimes','\u22CA':'rtimes','\u22CB':'lthree','\u22CC':'rthree','\u22CD':'bsime','\u22CE':'cuvee','\u22CF':'cuwed','\u22D0':'Sub','\u22D1':'Sup','\u22D2':'Cap','\u22D3':'Cup','\u22D4':'fork','\u22D5':'epar','\u22D6':'ltdot','\u22D7':'gtdot','\u22D8':'Ll','\u22D8\u0338':'nLl','\u22D9':'Gg','\u22D9\u0338':'nGg','\u22DA\uFE00':'lesg','\u22DA':'leg','\u22DB':'gel','\u22DB\uFE00':'gesl','\u22DE':'cuepr','\u22DF':'cuesc','\u22E6':'lnsim','\u22E7':'gnsim','\u22E8':'prnsim','\u22E9':'scnsim','\u22EE':'vellip','\u22EF':'ctdot','\u22F0':'utdot','\u22F1':'dtdot','\u22F2':'disin','\u22F3':'isinsv','\u22F4':'isins','\u22F5':'isindot','\u22F5\u0338':'notindot','\u22F6':'notinvc','\u22F7':'notinvb','\u22F9':'isinE','\u22F9\u0338':'notinE','\u22FA':'nisd','\u22FB':'xnis','\u22FC':'nis','\u22FD':'notnivc','\u22FE':'notnivb','\u2305':'barwed','\u2306':'Barwed','\u230C':'drcrop','\u230D':'dlcrop','\u230E':'urcrop','\u230F':'ulcrop','\u2310':'bnot','\u2312':'profline','\u2313':'profsurf','\u2315':'telrec','\u2316':'target','\u231C':'ulcorn','\u231D':'urcorn','\u231E':'dlcorn','\u231F':'drcorn','\u2322':'frown','\u2323':'smile','\u232D':'cylcty','\u232E':'profalar','\u2336':'topbot','\u233D':'ovbar','\u233F':'solbar','\u237C':'angzarr','\u23B0':'lmoust','\u23B1':'rmoust','\u23B4':'tbrk','\u23B5':'bbrk','\u23B6':'bbrktbrk','\u23DC':'OverParenthesis','\u23DD':'UnderParenthesis','\u23DE':'OverBrace','\u23DF':'UnderBrace','\u23E2':'trpezium','\u23E7':'elinters','\u2423':'blank','\u2500':'boxh','\u2502':'boxv','\u250C':'boxdr','\u2510':'boxdl','\u2514':'boxur','\u2518':'boxul','\u251C':'boxvr','\u2524':'boxvl','\u252C':'boxhd','\u2534':'boxhu','\u253C':'boxvh','\u2550':'boxH','\u2551':'boxV','\u2552':'boxdR','\u2553':'boxDr','\u2554':'boxDR','\u2555':'boxdL','\u2556':'boxDl','\u2557':'boxDL','\u2558':'boxuR','\u2559':'boxUr','\u255A':'boxUR','\u255B':'boxuL','\u255C':'boxUl','\u255D':'boxUL','\u255E':'boxvR','\u255F':'boxVr','\u2560':'boxVR','\u2561':'boxvL','\u2562':'boxVl','\u2563':'boxVL','\u2564':'boxHd','\u2565':'boxhD','\u2566':'boxHD','\u2567':'boxHu','\u2568':'boxhU','\u2569':'boxHU','\u256A':'boxvH','\u256B':'boxVh','\u256C':'boxVH','\u2580':'uhblk','\u2584':'lhblk','\u2588':'block','\u2591':'blk14','\u2592':'blk12','\u2593':'blk34','\u25A1':'squ','\u25AA':'squf','\u25AB':'EmptyVerySmallSquare','\u25AD':'rect','\u25AE':'marker','\u25B1':'fltns','\u25B3':'xutri','\u25B4':'utrif','\u25B5':'utri','\u25B8':'rtrif','\u25B9':'rtri','\u25BD':'xdtri','\u25BE':'dtrif','\u25BF':'dtri','\u25C2':'ltrif','\u25C3':'ltri','\u25CA':'loz','\u25CB':'cir','\u25EC':'tridot','\u25EF':'xcirc','\u25F8':'ultri','\u25F9':'urtri','\u25FA':'lltri','\u25FB':'EmptySmallSquare','\u25FC':'FilledSmallSquare','\u2605':'starf','\u2606':'star','\u260E':'phone','\u2640':'female','\u2642':'male','\u2660':'spades','\u2663':'clubs','\u2665':'hearts','\u2666':'diams','\u266A':'sung','\u2713':'check','\u2717':'cross','\u2720':'malt','\u2736':'sext','\u2758':'VerticalSeparator','\u27C8':'bsolhsub','\u27C9':'suphsol','\u27F5':'xlarr','\u27F6':'xrarr','\u27F7':'xharr','\u27F8':'xlArr','\u27F9':'xrArr','\u27FA':'xhArr','\u27FC':'xmap','\u27FF':'dzigrarr','\u2902':'nvlArr','\u2903':'nvrArr','\u2904':'nvHarr','\u2905':'Map','\u290C':'lbarr','\u290D':'rbarr','\u290E':'lBarr','\u290F':'rBarr','\u2910':'RBarr','\u2911':'DDotrahd','\u2912':'UpArrowBar','\u2913':'DownArrowBar','\u2916':'Rarrtl','\u2919':'latail','\u291A':'ratail','\u291B':'lAtail','\u291C':'rAtail','\u291D':'larrfs','\u291E':'rarrfs','\u291F':'larrbfs','\u2920':'rarrbfs','\u2923':'nwarhk','\u2924':'nearhk','\u2925':'searhk','\u2926':'swarhk','\u2927':'nwnear','\u2928':'toea','\u2929':'tosa','\u292A':'swnwar','\u2933':'rarrc','\u2933\u0338':'nrarrc','\u2935':'cudarrr','\u2936':'ldca','\u2937':'rdca','\u2938':'cudarrl','\u2939':'larrpl','\u293C':'curarrm','\u293D':'cularrp','\u2945':'rarrpl','\u2948':'harrcir','\u2949':'Uarrocir','\u294A':'lurdshar','\u294B':'ldrushar','\u294E':'LeftRightVector','\u294F':'RightUpDownVector','\u2950':'DownLeftRightVector','\u2951':'LeftUpDownVector','\u2952':'LeftVectorBar','\u2953':'RightVectorBar','\u2954':'RightUpVectorBar','\u2955':'RightDownVectorBar','\u2956':'DownLeftVectorBar','\u2957':'DownRightVectorBar','\u2958':'LeftUpVectorBar','\u2959':'LeftDownVectorBar','\u295A':'LeftTeeVector','\u295B':'RightTeeVector','\u295C':'RightUpTeeVector','\u295D':'RightDownTeeVector','\u295E':'DownLeftTeeVector','\u295F':'DownRightTeeVector','\u2960':'LeftUpTeeVector','\u2961':'LeftDownTeeVector','\u2962':'lHar','\u2963':'uHar','\u2964':'rHar','\u2965':'dHar','\u2966':'luruhar','\u2967':'ldrdhar','\u2968':'ruluhar','\u2969':'rdldhar','\u296A':'lharul','\u296B':'llhard','\u296C':'rharul','\u296D':'lrhard','\u296E':'udhar','\u296F':'duhar','\u2970':'RoundImplies','\u2971':'erarr','\u2972':'simrarr','\u2973':'larrsim','\u2974':'rarrsim','\u2975':'rarrap','\u2976':'ltlarr','\u2978':'gtrarr','\u2979':'subrarr','\u297B':'suplarr','\u297C':'lfisht','\u297D':'rfisht','\u297E':'ufisht','\u297F':'dfisht','\u299A':'vzigzag','\u299C':'vangrt','\u299D':'angrtvbd','\u29A4':'ange','\u29A5':'range','\u29A6':'dwangle','\u29A7':'uwangle','\u29A8':'angmsdaa','\u29A9':'angmsdab','\u29AA':'angmsdac','\u29AB':'angmsdad','\u29AC':'angmsdae','\u29AD':'angmsdaf','\u29AE':'angmsdag','\u29AF':'angmsdah','\u29B0':'bemptyv','\u29B1':'demptyv','\u29B2':'cemptyv','\u29B3':'raemptyv','\u29B4':'laemptyv','\u29B5':'ohbar','\u29B6':'omid','\u29B7':'opar','\u29B9':'operp','\u29BB':'olcross','\u29BC':'odsold','\u29BE':'olcir','\u29BF':'ofcir','\u29C0':'olt','\u29C1':'ogt','\u29C2':'cirscir','\u29C3':'cirE','\u29C4':'solb','\u29C5':'bsolb','\u29C9':'boxbox','\u29CD':'trisb','\u29CE':'rtriltri','\u29CF':'LeftTriangleBar','\u29CF\u0338':'NotLeftTriangleBar','\u29D0':'RightTriangleBar','\u29D0\u0338':'NotRightTriangleBar','\u29DC':'iinfin','\u29DD':'infintie','\u29DE':'nvinfin','\u29E3':'eparsl','\u29E4':'smeparsl','\u29E5':'eqvparsl','\u29EB':'lozf','\u29F4':'RuleDelayed','\u29F6':'dsol','\u2A00':'xodot','\u2A01':'xoplus','\u2A02':'xotime','\u2A04':'xuplus','\u2A06':'xsqcup','\u2A0D':'fpartint','\u2A10':'cirfnint','\u2A11':'awint','\u2A12':'rppolint','\u2A13':'scpolint','\u2A14':'npolint','\u2A15':'pointint','\u2A16':'quatint','\u2A17':'intlarhk','\u2A22':'pluscir','\u2A23':'plusacir','\u2A24':'simplus','\u2A25':'plusdu','\u2A26':'plussim','\u2A27':'plustwo','\u2A29':'mcomma','\u2A2A':'minusdu','\u2A2D':'loplus','\u2A2E':'roplus','\u2A2F':'Cross','\u2A30':'timesd','\u2A31':'timesbar','\u2A33':'smashp','\u2A34':'lotimes','\u2A35':'rotimes','\u2A36':'otimesas','\u2A37':'Otimes','\u2A38':'odiv','\u2A39':'triplus','\u2A3A':'triminus','\u2A3B':'tritime','\u2A3C':'iprod','\u2A3F':'amalg','\u2A40':'capdot','\u2A42':'ncup','\u2A43':'ncap','\u2A44':'capand','\u2A45':'cupor','\u2A46':'cupcap','\u2A47':'capcup','\u2A48':'cupbrcap','\u2A49':'capbrcup','\u2A4A':'cupcup','\u2A4B':'capcap','\u2A4C':'ccups','\u2A4D':'ccaps','\u2A50':'ccupssm','\u2A53':'And','\u2A54':'Or','\u2A55':'andand','\u2A56':'oror','\u2A57':'orslope','\u2A58':'andslope','\u2A5A':'andv','\u2A5B':'orv','\u2A5C':'andd','\u2A5D':'ord','\u2A5F':'wedbar','\u2A66':'sdote','\u2A6A':'simdot','\u2A6D':'congdot','\u2A6D\u0338':'ncongdot','\u2A6E':'easter','\u2A6F':'apacir','\u2A70':'apE','\u2A70\u0338':'napE','\u2A71':'eplus','\u2A72':'pluse','\u2A73':'Esim','\u2A77':'eDDot','\u2A78':'equivDD','\u2A79':'ltcir','\u2A7A':'gtcir','\u2A7B':'ltquest','\u2A7C':'gtquest','\u2A7D':'les','\u2A7D\u0338':'nles','\u2A7E':'ges','\u2A7E\u0338':'nges','\u2A7F':'lesdot','\u2A80':'gesdot','\u2A81':'lesdoto','\u2A82':'gesdoto','\u2A83':'lesdotor','\u2A84':'gesdotol','\u2A85':'lap','\u2A86':'gap','\u2A87':'lne','\u2A88':'gne','\u2A89':'lnap','\u2A8A':'gnap','\u2A8B':'lEg','\u2A8C':'gEl','\u2A8D':'lsime','\u2A8E':'gsime','\u2A8F':'lsimg','\u2A90':'gsiml','\u2A91':'lgE','\u2A92':'glE','\u2A93':'lesges','\u2A94':'gesles','\u2A95':'els','\u2A96':'egs','\u2A97':'elsdot','\u2A98':'egsdot','\u2A99':'el','\u2A9A':'eg','\u2A9D':'siml','\u2A9E':'simg','\u2A9F':'simlE','\u2AA0':'simgE','\u2AA1':'LessLess','\u2AA1\u0338':'NotNestedLessLess','\u2AA2':'GreaterGreater','\u2AA2\u0338':'NotNestedGreaterGreater','\u2AA4':'glj','\u2AA5':'gla','\u2AA6':'ltcc','\u2AA7':'gtcc','\u2AA8':'lescc','\u2AA9':'gescc','\u2AAA':'smt','\u2AAB':'lat','\u2AAC':'smte','\u2AAC\uFE00':'smtes','\u2AAD':'late','\u2AAD\uFE00':'lates','\u2AAE':'bumpE','\u2AAF':'pre','\u2AAF\u0338':'npre','\u2AB0':'sce','\u2AB0\u0338':'nsce','\u2AB3':'prE','\u2AB4':'scE','\u2AB5':'prnE','\u2AB6':'scnE','\u2AB7':'prap','\u2AB8':'scap','\u2AB9':'prnap','\u2ABA':'scnap','\u2ABB':'Pr','\u2ABC':'Sc','\u2ABD':'subdot','\u2ABE':'supdot','\u2ABF':'subplus','\u2AC0':'supplus','\u2AC1':'submult','\u2AC2':'supmult','\u2AC3':'subedot','\u2AC4':'supedot','\u2AC5':'subE','\u2AC5\u0338':'nsubE','\u2AC6':'supE','\u2AC6\u0338':'nsupE','\u2AC7':'subsim','\u2AC8':'supsim','\u2ACB\uFE00':'vsubnE','\u2ACB':'subnE','\u2ACC\uFE00':'vsupnE','\u2ACC':'supnE','\u2ACF':'csub','\u2AD0':'csup','\u2AD1':'csube','\u2AD2':'csupe','\u2AD3':'subsup','\u2AD4':'supsub','\u2AD5':'subsub','\u2AD6':'supsup','\u2AD7':'suphsub','\u2AD8':'supdsub','\u2AD9':'forkv','\u2ADA':'topfork','\u2ADB':'mlcp','\u2AE4':'Dashv','\u2AE6':'Vdashl','\u2AE7':'Barv','\u2AE8':'vBar','\u2AE9':'vBarv','\u2AEB':'Vbar','\u2AEC':'Not','\u2AED':'bNot','\u2AEE':'rnmid','\u2AEF':'cirmid','\u2AF0':'midcir','\u2AF1':'topcir','\u2AF2':'nhpar','\u2AF3':'parsim','\u2AFD':'parsl','\u2AFD\u20E5':'nparsl','\u266D':'flat','\u266E':'natur','\u266F':'sharp','\xA4':'curren','\xA2':'cent','$':'dollar','\xA3':'pound','\xA5':'yen','\u20AC':'euro','\xB9':'sup1','\xBD':'half','\u2153':'frac13','\xBC':'frac14','\u2155':'frac15','\u2159':'frac16','\u215B':'frac18','\xB2':'sup2','\u2154':'frac23','\u2156':'frac25','\xB3':'sup3','\xBE':'frac34','\u2157':'frac35','\u215C':'frac38','\u2158':'frac45','\u215A':'frac56','\u215D':'frac58','\u215E':'frac78','\uD835\uDCB6':'ascr','\uD835\uDD52':'aopf','\uD835\uDD1E':'afr','\uD835\uDD38':'Aopf','\uD835\uDD04':'Afr','\uD835\uDC9C':'Ascr','\xAA':'ordf','\xE1':'aacute','\xC1':'Aacute','\xE0':'agrave','\xC0':'Agrave','\u0103':'abreve','\u0102':'Abreve','\xE2':'acirc','\xC2':'Acirc','\xE5':'aring','\xC5':'angst','\xE4':'auml','\xC4':'Auml','\xE3':'atilde','\xC3':'Atilde','\u0105':'aogon','\u0104':'Aogon','\u0101':'amacr','\u0100':'Amacr','\xE6':'aelig','\xC6':'AElig','\uD835\uDCB7':'bscr','\uD835\uDD53':'bopf','\uD835\uDD1F':'bfr','\uD835\uDD39':'Bopf','\u212C':'Bscr','\uD835\uDD05':'Bfr','\uD835\uDD20':'cfr','\uD835\uDCB8':'cscr','\uD835\uDD54':'copf','\u212D':'Cfr','\uD835\uDC9E':'Cscr','\u2102':'Copf','\u0107':'cacute','\u0106':'Cacute','\u0109':'ccirc','\u0108':'Ccirc','\u010D':'ccaron','\u010C':'Ccaron','\u010B':'cdot','\u010A':'Cdot','\xE7':'ccedil','\xC7':'Ccedil','\u2105':'incare','\uD835\uDD21':'dfr','\u2146':'dd','\uD835\uDD55':'dopf','\uD835\uDCB9':'dscr','\uD835\uDC9F':'Dscr','\uD835\uDD07':'Dfr','\u2145':'DD','\uD835\uDD3B':'Dopf','\u010F':'dcaron','\u010E':'Dcaron','\u0111':'dstrok','\u0110':'Dstrok','\xF0':'eth','\xD0':'ETH','\u2147':'ee','\u212F':'escr','\uD835\uDD22':'efr','\uD835\uDD56':'eopf','\u2130':'Escr','\uD835\uDD08':'Efr','\uD835\uDD3C':'Eopf','\xE9':'eacute','\xC9':'Eacute','\xE8':'egrave','\xC8':'Egrave','\xEA':'ecirc','\xCA':'Ecirc','\u011B':'ecaron','\u011A':'Ecaron','\xEB':'euml','\xCB':'Euml','\u0117':'edot','\u0116':'Edot','\u0119':'eogon','\u0118':'Eogon','\u0113':'emacr','\u0112':'Emacr','\uD835\uDD23':'ffr','\uD835\uDD57':'fopf','\uD835\uDCBB':'fscr','\uD835\uDD09':'Ffr','\uD835\uDD3D':'Fopf','\u2131':'Fscr','\uFB00':'fflig','\uFB03':'ffilig','\uFB04':'ffllig','\uFB01':'filig','fj':'fjlig','\uFB02':'fllig','\u0192':'fnof','\u210A':'gscr','\uD835\uDD58':'gopf','\uD835\uDD24':'gfr','\uD835\uDCA2':'Gscr','\uD835\uDD3E':'Gopf','\uD835\uDD0A':'Gfr','\u01F5':'gacute','\u011F':'gbreve','\u011E':'Gbreve','\u011D':'gcirc','\u011C':'Gcirc','\u0121':'gdot','\u0120':'Gdot','\u0122':'Gcedil','\uD835\uDD25':'hfr','\u210E':'planckh','\uD835\uDCBD':'hscr','\uD835\uDD59':'hopf','\u210B':'Hscr','\u210C':'Hfr','\u210D':'Hopf','\u0125':'hcirc','\u0124':'Hcirc','\u210F':'hbar','\u0127':'hstrok','\u0126':'Hstrok','\uD835\uDD5A':'iopf','\uD835\uDD26':'ifr','\uD835\uDCBE':'iscr','\u2148':'ii','\uD835\uDD40':'Iopf','\u2110':'Iscr','\u2111':'Im','\xED':'iacute','\xCD':'Iacute','\xEC':'igrave','\xCC':'Igrave','\xEE':'icirc','\xCE':'Icirc','\xEF':'iuml','\xCF':'Iuml','\u0129':'itilde','\u0128':'Itilde','\u0130':'Idot','\u012F':'iogon','\u012E':'Iogon','\u012B':'imacr','\u012A':'Imacr','\u0133':'ijlig','\u0132':'IJlig','\u0131':'imath','\uD835\uDCBF':'jscr','\uD835\uDD5B':'jopf','\uD835\uDD27':'jfr','\uD835\uDCA5':'Jscr','\uD835\uDD0D':'Jfr','\uD835\uDD41':'Jopf','\u0135':'jcirc','\u0134':'Jcirc','\u0237':'jmath','\uD835\uDD5C':'kopf','\uD835\uDCC0':'kscr','\uD835\uDD28':'kfr','\uD835\uDCA6':'Kscr','\uD835\uDD42':'Kopf','\uD835\uDD0E':'Kfr','\u0137':'kcedil','\u0136':'Kcedil','\uD835\uDD29':'lfr','\uD835\uDCC1':'lscr','\u2113':'ell','\uD835\uDD5D':'lopf','\u2112':'Lscr','\uD835\uDD0F':'Lfr','\uD835\uDD43':'Lopf','\u013A':'lacute','\u0139':'Lacute','\u013E':'lcaron','\u013D':'Lcaron','\u013C':'lcedil','\u013B':'Lcedil','\u0142':'lstrok','\u0141':'Lstrok','\u0140':'lmidot','\u013F':'Lmidot','\uD835\uDD2A':'mfr','\uD835\uDD5E':'mopf','\uD835\uDCC2':'mscr','\uD835\uDD10':'Mfr','\uD835\uDD44':'Mopf','\u2133':'Mscr','\uD835\uDD2B':'nfr','\uD835\uDD5F':'nopf','\uD835\uDCC3':'nscr','\u2115':'Nopf','\uD835\uDCA9':'Nscr','\uD835\uDD11':'Nfr','\u0144':'nacute','\u0143':'Nacute','\u0148':'ncaron','\u0147':'Ncaron','\xF1':'ntilde','\xD1':'Ntilde','\u0146':'ncedil','\u0145':'Ncedil','\u2116':'numero','\u014B':'eng','\u014A':'ENG','\uD835\uDD60':'oopf','\uD835\uDD2C':'ofr','\u2134':'oscr','\uD835\uDCAA':'Oscr','\uD835\uDD12':'Ofr','\uD835\uDD46':'Oopf','\xBA':'ordm','\xF3':'oacute','\xD3':'Oacute','\xF2':'ograve','\xD2':'Ograve','\xF4':'ocirc','\xD4':'Ocirc','\xF6':'ouml','\xD6':'Ouml','\u0151':'odblac','\u0150':'Odblac','\xF5':'otilde','\xD5':'Otilde','\xF8':'oslash','\xD8':'Oslash','\u014D':'omacr','\u014C':'Omacr','\u0153':'oelig','\u0152':'OElig','\uD835\uDD2D':'pfr','\uD835\uDCC5':'pscr','\uD835\uDD61':'popf','\u2119':'Popf','\uD835\uDD13':'Pfr','\uD835\uDCAB':'Pscr','\uD835\uDD62':'qopf','\uD835\uDD2E':'qfr','\uD835\uDCC6':'qscr','\uD835\uDCAC':'Qscr','\uD835\uDD14':'Qfr','\u211A':'Qopf','\u0138':'kgreen','\uD835\uDD2F':'rfr','\uD835\uDD63':'ropf','\uD835\uDCC7':'rscr','\u211B':'Rscr','\u211C':'Re','\u211D':'Ropf','\u0155':'racute','\u0154':'Racute','\u0159':'rcaron','\u0158':'Rcaron','\u0157':'rcedil','\u0156':'Rcedil','\uD835\uDD64':'sopf','\uD835\uDCC8':'sscr','\uD835\uDD30':'sfr','\uD835\uDD4A':'Sopf','\uD835\uDD16':'Sfr','\uD835\uDCAE':'Sscr','\u24C8':'oS','\u015B':'sacute','\u015A':'Sacute','\u015D':'scirc','\u015C':'Scirc','\u0161':'scaron','\u0160':'Scaron','\u015F':'scedil','\u015E':'Scedil','\xDF':'szlig','\uD835\uDD31':'tfr','\uD835\uDCC9':'tscr','\uD835\uDD65':'topf','\uD835\uDCAF':'Tscr','\uD835\uDD17':'Tfr','\uD835\uDD4B':'Topf','\u0165':'tcaron','\u0164':'Tcaron','\u0163':'tcedil','\u0162':'Tcedil','\u2122':'trade','\u0167':'tstrok','\u0166':'Tstrok','\uD835\uDCCA':'uscr','\uD835\uDD66':'uopf','\uD835\uDD32':'ufr','\uD835\uDD4C':'Uopf','\uD835\uDD18':'Ufr','\uD835\uDCB0':'Uscr','\xFA':'uacute','\xDA':'Uacute','\xF9':'ugrave','\xD9':'Ugrave','\u016D':'ubreve','\u016C':'Ubreve','\xFB':'ucirc','\xDB':'Ucirc','\u016F':'uring','\u016E':'Uring','\xFC':'uuml','\xDC':'Uuml','\u0171':'udblac','\u0170':'Udblac','\u0169':'utilde','\u0168':'Utilde','\u0173':'uogon','\u0172':'Uogon','\u016B':'umacr','\u016A':'Umacr','\uD835\uDD33':'vfr','\uD835\uDD67':'vopf','\uD835\uDCCB':'vscr','\uD835\uDD19':'Vfr','\uD835\uDD4D':'Vopf','\uD835\uDCB1':'Vscr','\uD835\uDD68':'wopf','\uD835\uDCCC':'wscr','\uD835\uDD34':'wfr','\uD835\uDCB2':'Wscr','\uD835\uDD4E':'Wopf','\uD835\uDD1A':'Wfr','\u0175':'wcirc','\u0174':'Wcirc','\uD835\uDD35':'xfr','\uD835\uDCCD':'xscr','\uD835\uDD69':'xopf','\uD835\uDD4F':'Xopf','\uD835\uDD1B':'Xfr','\uD835\uDCB3':'Xscr','\uD835\uDD36':'yfr','\uD835\uDCCE':'yscr','\uD835\uDD6A':'yopf','\uD835\uDCB4':'Yscr','\uD835\uDD1C':'Yfr','\uD835\uDD50':'Yopf','\xFD':'yacute','\xDD':'Yacute','\u0177':'ycirc','\u0176':'Ycirc','\xFF':'yuml','\u0178':'Yuml','\uD835\uDCCF':'zscr','\uD835\uDD37':'zfr','\uD835\uDD6B':'zopf','\u2128':'Zfr','\u2124':'Zopf','\uD835\uDCB5':'Zscr','\u017A':'zacute','\u0179':'Zacute','\u017E':'zcaron','\u017D':'Zcaron','\u017C':'zdot','\u017B':'Zdot','\u01B5':'imped','\xFE':'thorn','\xDE':'THORN','\u0149':'napos','\u03B1':'alpha','\u0391':'Alpha','\u03B2':'beta','\u0392':'Beta','\u03B3':'gamma','\u0393':'Gamma','\u03B4':'delta','\u0394':'Delta','\u03B5':'epsi','\u03F5':'epsiv','\u0395':'Epsilon','\u03DD':'gammad','\u03DC':'Gammad','\u03B6':'zeta','\u0396':'Zeta','\u03B7':'eta','\u0397':'Eta','\u03B8':'theta','\u03D1':'thetav','\u0398':'Theta','\u03B9':'iota','\u0399':'Iota','\u03BA':'kappa','\u03F0':'kappav','\u039A':'Kappa','\u03BB':'lambda','\u039B':'Lambda','\u03BC':'mu','\xB5':'micro','\u039C':'Mu','\u03BD':'nu','\u039D':'Nu','\u03BE':'xi','\u039E':'Xi','\u03BF':'omicron','\u039F':'Omicron','\u03C0':'pi','\u03D6':'piv','\u03A0':'Pi','\u03C1':'rho','\u03F1':'rhov','\u03A1':'Rho','\u03C3':'sigma','\u03A3':'Sigma','\u03C2':'sigmaf','\u03C4':'tau','\u03A4':'Tau','\u03C5':'upsi','\u03A5':'Upsilon','\u03D2':'Upsi','\u03C6':'phi','\u03D5':'phiv','\u03A6':'Phi','\u03C7':'chi','\u03A7':'Chi','\u03C8':'psi','\u03A8':'Psi','\u03C9':'omega','\u03A9':'ohm','\u0430':'acy','\u0410':'Acy','\u0431':'bcy','\u0411':'Bcy','\u0432':'vcy','\u0412':'Vcy','\u0433':'gcy','\u0413':'Gcy','\u0453':'gjcy','\u0403':'GJcy','\u0434':'dcy','\u0414':'Dcy','\u0452':'djcy','\u0402':'DJcy','\u0435':'iecy','\u0415':'IEcy','\u0451':'iocy','\u0401':'IOcy','\u0454':'jukcy','\u0404':'Jukcy','\u0436':'zhcy','\u0416':'ZHcy','\u0437':'zcy','\u0417':'Zcy','\u0455':'dscy','\u0405':'DScy','\u0438':'icy','\u0418':'Icy','\u0456':'iukcy','\u0406':'Iukcy','\u0457':'yicy','\u0407':'YIcy','\u0439':'jcy','\u0419':'Jcy','\u0458':'jsercy','\u0408':'Jsercy','\u043A':'kcy','\u041A':'Kcy','\u045C':'kjcy','\u040C':'KJcy','\u043B':'lcy','\u041B':'Lcy','\u0459':'ljcy','\u0409':'LJcy','\u043C':'mcy','\u041C':'Mcy','\u043D':'ncy','\u041D':'Ncy','\u045A':'njcy','\u040A':'NJcy','\u043E':'ocy','\u041E':'Ocy','\u043F':'pcy','\u041F':'Pcy','\u0440':'rcy','\u0420':'Rcy','\u0441':'scy','\u0421':'Scy','\u0442':'tcy','\u0422':'Tcy','\u045B':'tshcy','\u040B':'TSHcy','\u0443':'ucy','\u0423':'Ucy','\u045E':'ubrcy','\u040E':'Ubrcy','\u0444':'fcy','\u0424':'Fcy','\u0445':'khcy','\u0425':'KHcy','\u0446':'tscy','\u0426':'TScy','\u0447':'chcy','\u0427':'CHcy','\u045F':'dzcy','\u040F':'DZcy','\u0448':'shcy','\u0428':'SHcy','\u0449':'shchcy','\u0429':'SHCHcy','\u044A':'hardcy','\u042A':'HARDcy','\u044B':'ycy','\u042B':'Ycy','\u044C':'softcy','\u042C':'SOFTcy','\u044D':'ecy','\u042D':'Ecy','\u044E':'yucy','\u042E':'YUcy','\u044F':'yacy','\u042F':'YAcy','\u2135':'aleph','\u2136':'beth','\u2137':'gimel','\u2138':'daleth'}; - - var regexEscape = /["&'<>`]/g; - var escapeMap = { - '"': '"', - '&': '&', - '\'': ''', - '<': '<', - // See https://mathiasbynens.be/notes/ambiguous-ampersands: in HTML, the - // following is not strictly necessary unless it’s part of a tag or an - // unquoted attribute value. We’re only escaping it to support those - // situations, and for XML support. - '>': '>', - // In Internet Explorer ≤ 8, the backtick character can be used - // to break out of (un)quoted attribute values or HTML comments. - // See http://html5sec.org/#102, http://html5sec.org/#108, and - // http://html5sec.org/#133. - '`': '`' - }; - - var regexInvalidEntity = /&#(?:[xX][^a-fA-F0-9]|[^0-9xX])/; - var regexInvalidRawCodePoint = /[\0-\x08\x0B\x0E-\x1F\x7F-\x9F\uFDD0-\uFDEF\uFFFE\uFFFF]|[\uD83F\uD87F\uD8BF\uD8FF\uD93F\uD97F\uD9BF\uD9FF\uDA3F\uDA7F\uDABF\uDAFF\uDB3F\uDB7F\uDBBF\uDBFF][\uDFFE\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; - var regexDecode = /&#([0-9]+)(;?)|&#[xX]([a-fA-F0-9]+)(;?)|&([0-9a-zA-Z]+);|&(Aacute|Agrave|Atilde|Ccedil|Eacute|Egrave|Iacute|Igrave|Ntilde|Oacute|Ograve|Oslash|Otilde|Uacute|Ugrave|Yacute|aacute|agrave|atilde|brvbar|ccedil|curren|divide|eacute|egrave|frac12|frac14|frac34|iacute|igrave|iquest|middot|ntilde|oacute|ograve|oslash|otilde|plusmn|uacute|ugrave|yacute|AElig|Acirc|Aring|Ecirc|Icirc|Ocirc|THORN|Ucirc|acirc|acute|aelig|aring|cedil|ecirc|icirc|iexcl|laquo|micro|ocirc|pound|raquo|szlig|thorn|times|ucirc|Auml|COPY|Euml|Iuml|Ouml|QUOT|Uuml|auml|cent|copy|euml|iuml|macr|nbsp|ordf|ordm|ouml|para|quot|sect|sup1|sup2|sup3|uuml|yuml|AMP|ETH|REG|amp|deg|eth|not|reg|shy|uml|yen|GT|LT|gt|lt)([=a-zA-Z0-9])?/g; - var decodeMap = {'aacute':'\xE1','Aacute':'\xC1','abreve':'\u0103','Abreve':'\u0102','ac':'\u223E','acd':'\u223F','acE':'\u223E\u0333','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','acy':'\u0430','Acy':'\u0410','aelig':'\xE6','AElig':'\xC6','af':'\u2061','afr':'\uD835\uDD1E','Afr':'\uD835\uDD04','agrave':'\xE0','Agrave':'\xC0','alefsym':'\u2135','aleph':'\u2135','alpha':'\u03B1','Alpha':'\u0391','amacr':'\u0101','Amacr':'\u0100','amalg':'\u2A3F','amp':'&','AMP':'&','and':'\u2227','And':'\u2A53','andand':'\u2A55','andd':'\u2A5C','andslope':'\u2A58','andv':'\u2A5A','ang':'\u2220','ange':'\u29A4','angle':'\u2220','angmsd':'\u2221','angmsdaa':'\u29A8','angmsdab':'\u29A9','angmsdac':'\u29AA','angmsdad':'\u29AB','angmsdae':'\u29AC','angmsdaf':'\u29AD','angmsdag':'\u29AE','angmsdah':'\u29AF','angrt':'\u221F','angrtvb':'\u22BE','angrtvbd':'\u299D','angsph':'\u2222','angst':'\xC5','angzarr':'\u237C','aogon':'\u0105','Aogon':'\u0104','aopf':'\uD835\uDD52','Aopf':'\uD835\uDD38','ap':'\u2248','apacir':'\u2A6F','ape':'\u224A','apE':'\u2A70','apid':'\u224B','apos':'\'','ApplyFunction':'\u2061','approx':'\u2248','approxeq':'\u224A','aring':'\xE5','Aring':'\xC5','ascr':'\uD835\uDCB6','Ascr':'\uD835\uDC9C','Assign':'\u2254','ast':'*','asymp':'\u2248','asympeq':'\u224D','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','awconint':'\u2233','awint':'\u2A11','backcong':'\u224C','backepsilon':'\u03F6','backprime':'\u2035','backsim':'\u223D','backsimeq':'\u22CD','Backslash':'\u2216','Barv':'\u2AE7','barvee':'\u22BD','barwed':'\u2305','Barwed':'\u2306','barwedge':'\u2305','bbrk':'\u23B5','bbrktbrk':'\u23B6','bcong':'\u224C','bcy':'\u0431','Bcy':'\u0411','bdquo':'\u201E','becaus':'\u2235','because':'\u2235','Because':'\u2235','bemptyv':'\u29B0','bepsi':'\u03F6','bernou':'\u212C','Bernoullis':'\u212C','beta':'\u03B2','Beta':'\u0392','beth':'\u2136','between':'\u226C','bfr':'\uD835\uDD1F','Bfr':'\uD835\uDD05','bigcap':'\u22C2','bigcirc':'\u25EF','bigcup':'\u22C3','bigodot':'\u2A00','bigoplus':'\u2A01','bigotimes':'\u2A02','bigsqcup':'\u2A06','bigstar':'\u2605','bigtriangledown':'\u25BD','bigtriangleup':'\u25B3','biguplus':'\u2A04','bigvee':'\u22C1','bigwedge':'\u22C0','bkarow':'\u290D','blacklozenge':'\u29EB','blacksquare':'\u25AA','blacktriangle':'\u25B4','blacktriangledown':'\u25BE','blacktriangleleft':'\u25C2','blacktriangleright':'\u25B8','blank':'\u2423','blk12':'\u2592','blk14':'\u2591','blk34':'\u2593','block':'\u2588','bne':'=\u20E5','bnequiv':'\u2261\u20E5','bnot':'\u2310','bNot':'\u2AED','bopf':'\uD835\uDD53','Bopf':'\uD835\uDD39','bot':'\u22A5','bottom':'\u22A5','bowtie':'\u22C8','boxbox':'\u29C9','boxdl':'\u2510','boxdL':'\u2555','boxDl':'\u2556','boxDL':'\u2557','boxdr':'\u250C','boxdR':'\u2552','boxDr':'\u2553','boxDR':'\u2554','boxh':'\u2500','boxH':'\u2550','boxhd':'\u252C','boxhD':'\u2565','boxHd':'\u2564','boxHD':'\u2566','boxhu':'\u2534','boxhU':'\u2568','boxHu':'\u2567','boxHU':'\u2569','boxminus':'\u229F','boxplus':'\u229E','boxtimes':'\u22A0','boxul':'\u2518','boxuL':'\u255B','boxUl':'\u255C','boxUL':'\u255D','boxur':'\u2514','boxuR':'\u2558','boxUr':'\u2559','boxUR':'\u255A','boxv':'\u2502','boxV':'\u2551','boxvh':'\u253C','boxvH':'\u256A','boxVh':'\u256B','boxVH':'\u256C','boxvl':'\u2524','boxvL':'\u2561','boxVl':'\u2562','boxVL':'\u2563','boxvr':'\u251C','boxvR':'\u255E','boxVr':'\u255F','boxVR':'\u2560','bprime':'\u2035','breve':'\u02D8','Breve':'\u02D8','brvbar':'\xA6','bscr':'\uD835\uDCB7','Bscr':'\u212C','bsemi':'\u204F','bsim':'\u223D','bsime':'\u22CD','bsol':'\\','bsolb':'\u29C5','bsolhsub':'\u27C8','bull':'\u2022','bullet':'\u2022','bump':'\u224E','bumpe':'\u224F','bumpE':'\u2AAE','bumpeq':'\u224F','Bumpeq':'\u224E','cacute':'\u0107','Cacute':'\u0106','cap':'\u2229','Cap':'\u22D2','capand':'\u2A44','capbrcup':'\u2A49','capcap':'\u2A4B','capcup':'\u2A47','capdot':'\u2A40','CapitalDifferentialD':'\u2145','caps':'\u2229\uFE00','caret':'\u2041','caron':'\u02C7','Cayleys':'\u212D','ccaps':'\u2A4D','ccaron':'\u010D','Ccaron':'\u010C','ccedil':'\xE7','Ccedil':'\xC7','ccirc':'\u0109','Ccirc':'\u0108','Cconint':'\u2230','ccups':'\u2A4C','ccupssm':'\u2A50','cdot':'\u010B','Cdot':'\u010A','cedil':'\xB8','Cedilla':'\xB8','cemptyv':'\u29B2','cent':'\xA2','centerdot':'\xB7','CenterDot':'\xB7','cfr':'\uD835\uDD20','Cfr':'\u212D','chcy':'\u0447','CHcy':'\u0427','check':'\u2713','checkmark':'\u2713','chi':'\u03C7','Chi':'\u03A7','cir':'\u25CB','circ':'\u02C6','circeq':'\u2257','circlearrowleft':'\u21BA','circlearrowright':'\u21BB','circledast':'\u229B','circledcirc':'\u229A','circleddash':'\u229D','CircleDot':'\u2299','circledR':'\xAE','circledS':'\u24C8','CircleMinus':'\u2296','CirclePlus':'\u2295','CircleTimes':'\u2297','cire':'\u2257','cirE':'\u29C3','cirfnint':'\u2A10','cirmid':'\u2AEF','cirscir':'\u29C2','ClockwiseContourIntegral':'\u2232','CloseCurlyDoubleQuote':'\u201D','CloseCurlyQuote':'\u2019','clubs':'\u2663','clubsuit':'\u2663','colon':':','Colon':'\u2237','colone':'\u2254','Colone':'\u2A74','coloneq':'\u2254','comma':',','commat':'@','comp':'\u2201','compfn':'\u2218','complement':'\u2201','complexes':'\u2102','cong':'\u2245','congdot':'\u2A6D','Congruent':'\u2261','conint':'\u222E','Conint':'\u222F','ContourIntegral':'\u222E','copf':'\uD835\uDD54','Copf':'\u2102','coprod':'\u2210','Coproduct':'\u2210','copy':'\xA9','COPY':'\xA9','copysr':'\u2117','CounterClockwiseContourIntegral':'\u2233','crarr':'\u21B5','cross':'\u2717','Cross':'\u2A2F','cscr':'\uD835\uDCB8','Cscr':'\uD835\uDC9E','csub':'\u2ACF','csube':'\u2AD1','csup':'\u2AD0','csupe':'\u2AD2','ctdot':'\u22EF','cudarrl':'\u2938','cudarrr':'\u2935','cuepr':'\u22DE','cuesc':'\u22DF','cularr':'\u21B6','cularrp':'\u293D','cup':'\u222A','Cup':'\u22D3','cupbrcap':'\u2A48','cupcap':'\u2A46','CupCap':'\u224D','cupcup':'\u2A4A','cupdot':'\u228D','cupor':'\u2A45','cups':'\u222A\uFE00','curarr':'\u21B7','curarrm':'\u293C','curlyeqprec':'\u22DE','curlyeqsucc':'\u22DF','curlyvee':'\u22CE','curlywedge':'\u22CF','curren':'\xA4','curvearrowleft':'\u21B6','curvearrowright':'\u21B7','cuvee':'\u22CE','cuwed':'\u22CF','cwconint':'\u2232','cwint':'\u2231','cylcty':'\u232D','dagger':'\u2020','Dagger':'\u2021','daleth':'\u2138','darr':'\u2193','dArr':'\u21D3','Darr':'\u21A1','dash':'\u2010','dashv':'\u22A3','Dashv':'\u2AE4','dbkarow':'\u290F','dblac':'\u02DD','dcaron':'\u010F','Dcaron':'\u010E','dcy':'\u0434','Dcy':'\u0414','dd':'\u2146','DD':'\u2145','ddagger':'\u2021','ddarr':'\u21CA','DDotrahd':'\u2911','ddotseq':'\u2A77','deg':'\xB0','Del':'\u2207','delta':'\u03B4','Delta':'\u0394','demptyv':'\u29B1','dfisht':'\u297F','dfr':'\uD835\uDD21','Dfr':'\uD835\uDD07','dHar':'\u2965','dharl':'\u21C3','dharr':'\u21C2','DiacriticalAcute':'\xB4','DiacriticalDot':'\u02D9','DiacriticalDoubleAcute':'\u02DD','DiacriticalGrave':'`','DiacriticalTilde':'\u02DC','diam':'\u22C4','diamond':'\u22C4','Diamond':'\u22C4','diamondsuit':'\u2666','diams':'\u2666','die':'\xA8','DifferentialD':'\u2146','digamma':'\u03DD','disin':'\u22F2','div':'\xF7','divide':'\xF7','divideontimes':'\u22C7','divonx':'\u22C7','djcy':'\u0452','DJcy':'\u0402','dlcorn':'\u231E','dlcrop':'\u230D','dollar':'$','dopf':'\uD835\uDD55','Dopf':'\uD835\uDD3B','dot':'\u02D9','Dot':'\xA8','DotDot':'\u20DC','doteq':'\u2250','doteqdot':'\u2251','DotEqual':'\u2250','dotminus':'\u2238','dotplus':'\u2214','dotsquare':'\u22A1','doublebarwedge':'\u2306','DoubleContourIntegral':'\u222F','DoubleDot':'\xA8','DoubleDownArrow':'\u21D3','DoubleLeftArrow':'\u21D0','DoubleLeftRightArrow':'\u21D4','DoubleLeftTee':'\u2AE4','DoubleLongLeftArrow':'\u27F8','DoubleLongLeftRightArrow':'\u27FA','DoubleLongRightArrow':'\u27F9','DoubleRightArrow':'\u21D2','DoubleRightTee':'\u22A8','DoubleUpArrow':'\u21D1','DoubleUpDownArrow':'\u21D5','DoubleVerticalBar':'\u2225','downarrow':'\u2193','Downarrow':'\u21D3','DownArrow':'\u2193','DownArrowBar':'\u2913','DownArrowUpArrow':'\u21F5','DownBreve':'\u0311','downdownarrows':'\u21CA','downharpoonleft':'\u21C3','downharpoonright':'\u21C2','DownLeftRightVector':'\u2950','DownLeftTeeVector':'\u295E','DownLeftVector':'\u21BD','DownLeftVectorBar':'\u2956','DownRightTeeVector':'\u295F','DownRightVector':'\u21C1','DownRightVectorBar':'\u2957','DownTee':'\u22A4','DownTeeArrow':'\u21A7','drbkarow':'\u2910','drcorn':'\u231F','drcrop':'\u230C','dscr':'\uD835\uDCB9','Dscr':'\uD835\uDC9F','dscy':'\u0455','DScy':'\u0405','dsol':'\u29F6','dstrok':'\u0111','Dstrok':'\u0110','dtdot':'\u22F1','dtri':'\u25BF','dtrif':'\u25BE','duarr':'\u21F5','duhar':'\u296F','dwangle':'\u29A6','dzcy':'\u045F','DZcy':'\u040F','dzigrarr':'\u27FF','eacute':'\xE9','Eacute':'\xC9','easter':'\u2A6E','ecaron':'\u011B','Ecaron':'\u011A','ecir':'\u2256','ecirc':'\xEA','Ecirc':'\xCA','ecolon':'\u2255','ecy':'\u044D','Ecy':'\u042D','eDDot':'\u2A77','edot':'\u0117','eDot':'\u2251','Edot':'\u0116','ee':'\u2147','efDot':'\u2252','efr':'\uD835\uDD22','Efr':'\uD835\uDD08','eg':'\u2A9A','egrave':'\xE8','Egrave':'\xC8','egs':'\u2A96','egsdot':'\u2A98','el':'\u2A99','Element':'\u2208','elinters':'\u23E7','ell':'\u2113','els':'\u2A95','elsdot':'\u2A97','emacr':'\u0113','Emacr':'\u0112','empty':'\u2205','emptyset':'\u2205','EmptySmallSquare':'\u25FB','emptyv':'\u2205','EmptyVerySmallSquare':'\u25AB','emsp':'\u2003','emsp13':'\u2004','emsp14':'\u2005','eng':'\u014B','ENG':'\u014A','ensp':'\u2002','eogon':'\u0119','Eogon':'\u0118','eopf':'\uD835\uDD56','Eopf':'\uD835\uDD3C','epar':'\u22D5','eparsl':'\u29E3','eplus':'\u2A71','epsi':'\u03B5','epsilon':'\u03B5','Epsilon':'\u0395','epsiv':'\u03F5','eqcirc':'\u2256','eqcolon':'\u2255','eqsim':'\u2242','eqslantgtr':'\u2A96','eqslantless':'\u2A95','Equal':'\u2A75','equals':'=','EqualTilde':'\u2242','equest':'\u225F','Equilibrium':'\u21CC','equiv':'\u2261','equivDD':'\u2A78','eqvparsl':'\u29E5','erarr':'\u2971','erDot':'\u2253','escr':'\u212F','Escr':'\u2130','esdot':'\u2250','esim':'\u2242','Esim':'\u2A73','eta':'\u03B7','Eta':'\u0397','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','euro':'\u20AC','excl':'!','exist':'\u2203','Exists':'\u2203','expectation':'\u2130','exponentiale':'\u2147','ExponentialE':'\u2147','fallingdotseq':'\u2252','fcy':'\u0444','Fcy':'\u0424','female':'\u2640','ffilig':'\uFB03','fflig':'\uFB00','ffllig':'\uFB04','ffr':'\uD835\uDD23','Ffr':'\uD835\uDD09','filig':'\uFB01','FilledSmallSquare':'\u25FC','FilledVerySmallSquare':'\u25AA','fjlig':'fj','flat':'\u266D','fllig':'\uFB02','fltns':'\u25B1','fnof':'\u0192','fopf':'\uD835\uDD57','Fopf':'\uD835\uDD3D','forall':'\u2200','ForAll':'\u2200','fork':'\u22D4','forkv':'\u2AD9','Fouriertrf':'\u2131','fpartint':'\u2A0D','frac12':'\xBD','frac13':'\u2153','frac14':'\xBC','frac15':'\u2155','frac16':'\u2159','frac18':'\u215B','frac23':'\u2154','frac25':'\u2156','frac34':'\xBE','frac35':'\u2157','frac38':'\u215C','frac45':'\u2158','frac56':'\u215A','frac58':'\u215D','frac78':'\u215E','frasl':'\u2044','frown':'\u2322','fscr':'\uD835\uDCBB','Fscr':'\u2131','gacute':'\u01F5','gamma':'\u03B3','Gamma':'\u0393','gammad':'\u03DD','Gammad':'\u03DC','gap':'\u2A86','gbreve':'\u011F','Gbreve':'\u011E','Gcedil':'\u0122','gcirc':'\u011D','Gcirc':'\u011C','gcy':'\u0433','Gcy':'\u0413','gdot':'\u0121','Gdot':'\u0120','ge':'\u2265','gE':'\u2267','gel':'\u22DB','gEl':'\u2A8C','geq':'\u2265','geqq':'\u2267','geqslant':'\u2A7E','ges':'\u2A7E','gescc':'\u2AA9','gesdot':'\u2A80','gesdoto':'\u2A82','gesdotol':'\u2A84','gesl':'\u22DB\uFE00','gesles':'\u2A94','gfr':'\uD835\uDD24','Gfr':'\uD835\uDD0A','gg':'\u226B','Gg':'\u22D9','ggg':'\u22D9','gimel':'\u2137','gjcy':'\u0453','GJcy':'\u0403','gl':'\u2277','gla':'\u2AA5','glE':'\u2A92','glj':'\u2AA4','gnap':'\u2A8A','gnapprox':'\u2A8A','gne':'\u2A88','gnE':'\u2269','gneq':'\u2A88','gneqq':'\u2269','gnsim':'\u22E7','gopf':'\uD835\uDD58','Gopf':'\uD835\uDD3E','grave':'`','GreaterEqual':'\u2265','GreaterEqualLess':'\u22DB','GreaterFullEqual':'\u2267','GreaterGreater':'\u2AA2','GreaterLess':'\u2277','GreaterSlantEqual':'\u2A7E','GreaterTilde':'\u2273','gscr':'\u210A','Gscr':'\uD835\uDCA2','gsim':'\u2273','gsime':'\u2A8E','gsiml':'\u2A90','gt':'>','Gt':'\u226B','GT':'>','gtcc':'\u2AA7','gtcir':'\u2A7A','gtdot':'\u22D7','gtlPar':'\u2995','gtquest':'\u2A7C','gtrapprox':'\u2A86','gtrarr':'\u2978','gtrdot':'\u22D7','gtreqless':'\u22DB','gtreqqless':'\u2A8C','gtrless':'\u2277','gtrsim':'\u2273','gvertneqq':'\u2269\uFE00','gvnE':'\u2269\uFE00','Hacek':'\u02C7','hairsp':'\u200A','half':'\xBD','hamilt':'\u210B','hardcy':'\u044A','HARDcy':'\u042A','harr':'\u2194','hArr':'\u21D4','harrcir':'\u2948','harrw':'\u21AD','Hat':'^','hbar':'\u210F','hcirc':'\u0125','Hcirc':'\u0124','hearts':'\u2665','heartsuit':'\u2665','hellip':'\u2026','hercon':'\u22B9','hfr':'\uD835\uDD25','Hfr':'\u210C','HilbertSpace':'\u210B','hksearow':'\u2925','hkswarow':'\u2926','hoarr':'\u21FF','homtht':'\u223B','hookleftarrow':'\u21A9','hookrightarrow':'\u21AA','hopf':'\uD835\uDD59','Hopf':'\u210D','horbar':'\u2015','HorizontalLine':'\u2500','hscr':'\uD835\uDCBD','Hscr':'\u210B','hslash':'\u210F','hstrok':'\u0127','Hstrok':'\u0126','HumpDownHump':'\u224E','HumpEqual':'\u224F','hybull':'\u2043','hyphen':'\u2010','iacute':'\xED','Iacute':'\xCD','ic':'\u2063','icirc':'\xEE','Icirc':'\xCE','icy':'\u0438','Icy':'\u0418','Idot':'\u0130','iecy':'\u0435','IEcy':'\u0415','iexcl':'\xA1','iff':'\u21D4','ifr':'\uD835\uDD26','Ifr':'\u2111','igrave':'\xEC','Igrave':'\xCC','ii':'\u2148','iiiint':'\u2A0C','iiint':'\u222D','iinfin':'\u29DC','iiota':'\u2129','ijlig':'\u0133','IJlig':'\u0132','Im':'\u2111','imacr':'\u012B','Imacr':'\u012A','image':'\u2111','ImaginaryI':'\u2148','imagline':'\u2110','imagpart':'\u2111','imath':'\u0131','imof':'\u22B7','imped':'\u01B5','Implies':'\u21D2','in':'\u2208','incare':'\u2105','infin':'\u221E','infintie':'\u29DD','inodot':'\u0131','int':'\u222B','Int':'\u222C','intcal':'\u22BA','integers':'\u2124','Integral':'\u222B','intercal':'\u22BA','Intersection':'\u22C2','intlarhk':'\u2A17','intprod':'\u2A3C','InvisibleComma':'\u2063','InvisibleTimes':'\u2062','iocy':'\u0451','IOcy':'\u0401','iogon':'\u012F','Iogon':'\u012E','iopf':'\uD835\uDD5A','Iopf':'\uD835\uDD40','iota':'\u03B9','Iota':'\u0399','iprod':'\u2A3C','iquest':'\xBF','iscr':'\uD835\uDCBE','Iscr':'\u2110','isin':'\u2208','isindot':'\u22F5','isinE':'\u22F9','isins':'\u22F4','isinsv':'\u22F3','isinv':'\u2208','it':'\u2062','itilde':'\u0129','Itilde':'\u0128','iukcy':'\u0456','Iukcy':'\u0406','iuml':'\xEF','Iuml':'\xCF','jcirc':'\u0135','Jcirc':'\u0134','jcy':'\u0439','Jcy':'\u0419','jfr':'\uD835\uDD27','Jfr':'\uD835\uDD0D','jmath':'\u0237','jopf':'\uD835\uDD5B','Jopf':'\uD835\uDD41','jscr':'\uD835\uDCBF','Jscr':'\uD835\uDCA5','jsercy':'\u0458','Jsercy':'\u0408','jukcy':'\u0454','Jukcy':'\u0404','kappa':'\u03BA','Kappa':'\u039A','kappav':'\u03F0','kcedil':'\u0137','Kcedil':'\u0136','kcy':'\u043A','Kcy':'\u041A','kfr':'\uD835\uDD28','Kfr':'\uD835\uDD0E','kgreen':'\u0138','khcy':'\u0445','KHcy':'\u0425','kjcy':'\u045C','KJcy':'\u040C','kopf':'\uD835\uDD5C','Kopf':'\uD835\uDD42','kscr':'\uD835\uDCC0','Kscr':'\uD835\uDCA6','lAarr':'\u21DA','lacute':'\u013A','Lacute':'\u0139','laemptyv':'\u29B4','lagran':'\u2112','lambda':'\u03BB','Lambda':'\u039B','lang':'\u27E8','Lang':'\u27EA','langd':'\u2991','langle':'\u27E8','lap':'\u2A85','Laplacetrf':'\u2112','laquo':'\xAB','larr':'\u2190','lArr':'\u21D0','Larr':'\u219E','larrb':'\u21E4','larrbfs':'\u291F','larrfs':'\u291D','larrhk':'\u21A9','larrlp':'\u21AB','larrpl':'\u2939','larrsim':'\u2973','larrtl':'\u21A2','lat':'\u2AAB','latail':'\u2919','lAtail':'\u291B','late':'\u2AAD','lates':'\u2AAD\uFE00','lbarr':'\u290C','lBarr':'\u290E','lbbrk':'\u2772','lbrace':'{','lbrack':'[','lbrke':'\u298B','lbrksld':'\u298F','lbrkslu':'\u298D','lcaron':'\u013E','Lcaron':'\u013D','lcedil':'\u013C','Lcedil':'\u013B','lceil':'\u2308','lcub':'{','lcy':'\u043B','Lcy':'\u041B','ldca':'\u2936','ldquo':'\u201C','ldquor':'\u201E','ldrdhar':'\u2967','ldrushar':'\u294B','ldsh':'\u21B2','le':'\u2264','lE':'\u2266','LeftAngleBracket':'\u27E8','leftarrow':'\u2190','Leftarrow':'\u21D0','LeftArrow':'\u2190','LeftArrowBar':'\u21E4','LeftArrowRightArrow':'\u21C6','leftarrowtail':'\u21A2','LeftCeiling':'\u2308','LeftDoubleBracket':'\u27E6','LeftDownTeeVector':'\u2961','LeftDownVector':'\u21C3','LeftDownVectorBar':'\u2959','LeftFloor':'\u230A','leftharpoondown':'\u21BD','leftharpoonup':'\u21BC','leftleftarrows':'\u21C7','leftrightarrow':'\u2194','Leftrightarrow':'\u21D4','LeftRightArrow':'\u2194','leftrightarrows':'\u21C6','leftrightharpoons':'\u21CB','leftrightsquigarrow':'\u21AD','LeftRightVector':'\u294E','LeftTee':'\u22A3','LeftTeeArrow':'\u21A4','LeftTeeVector':'\u295A','leftthreetimes':'\u22CB','LeftTriangle':'\u22B2','LeftTriangleBar':'\u29CF','LeftTriangleEqual':'\u22B4','LeftUpDownVector':'\u2951','LeftUpTeeVector':'\u2960','LeftUpVector':'\u21BF','LeftUpVectorBar':'\u2958','LeftVector':'\u21BC','LeftVectorBar':'\u2952','leg':'\u22DA','lEg':'\u2A8B','leq':'\u2264','leqq':'\u2266','leqslant':'\u2A7D','les':'\u2A7D','lescc':'\u2AA8','lesdot':'\u2A7F','lesdoto':'\u2A81','lesdotor':'\u2A83','lesg':'\u22DA\uFE00','lesges':'\u2A93','lessapprox':'\u2A85','lessdot':'\u22D6','lesseqgtr':'\u22DA','lesseqqgtr':'\u2A8B','LessEqualGreater':'\u22DA','LessFullEqual':'\u2266','LessGreater':'\u2276','lessgtr':'\u2276','LessLess':'\u2AA1','lesssim':'\u2272','LessSlantEqual':'\u2A7D','LessTilde':'\u2272','lfisht':'\u297C','lfloor':'\u230A','lfr':'\uD835\uDD29','Lfr':'\uD835\uDD0F','lg':'\u2276','lgE':'\u2A91','lHar':'\u2962','lhard':'\u21BD','lharu':'\u21BC','lharul':'\u296A','lhblk':'\u2584','ljcy':'\u0459','LJcy':'\u0409','ll':'\u226A','Ll':'\u22D8','llarr':'\u21C7','llcorner':'\u231E','Lleftarrow':'\u21DA','llhard':'\u296B','lltri':'\u25FA','lmidot':'\u0140','Lmidot':'\u013F','lmoust':'\u23B0','lmoustache':'\u23B0','lnap':'\u2A89','lnapprox':'\u2A89','lne':'\u2A87','lnE':'\u2268','lneq':'\u2A87','lneqq':'\u2268','lnsim':'\u22E6','loang':'\u27EC','loarr':'\u21FD','lobrk':'\u27E6','longleftarrow':'\u27F5','Longleftarrow':'\u27F8','LongLeftArrow':'\u27F5','longleftrightarrow':'\u27F7','Longleftrightarrow':'\u27FA','LongLeftRightArrow':'\u27F7','longmapsto':'\u27FC','longrightarrow':'\u27F6','Longrightarrow':'\u27F9','LongRightArrow':'\u27F6','looparrowleft':'\u21AB','looparrowright':'\u21AC','lopar':'\u2985','lopf':'\uD835\uDD5D','Lopf':'\uD835\uDD43','loplus':'\u2A2D','lotimes':'\u2A34','lowast':'\u2217','lowbar':'_','LowerLeftArrow':'\u2199','LowerRightArrow':'\u2198','loz':'\u25CA','lozenge':'\u25CA','lozf':'\u29EB','lpar':'(','lparlt':'\u2993','lrarr':'\u21C6','lrcorner':'\u231F','lrhar':'\u21CB','lrhard':'\u296D','lrm':'\u200E','lrtri':'\u22BF','lsaquo':'\u2039','lscr':'\uD835\uDCC1','Lscr':'\u2112','lsh':'\u21B0','Lsh':'\u21B0','lsim':'\u2272','lsime':'\u2A8D','lsimg':'\u2A8F','lsqb':'[','lsquo':'\u2018','lsquor':'\u201A','lstrok':'\u0142','Lstrok':'\u0141','lt':'<','Lt':'\u226A','LT':'<','ltcc':'\u2AA6','ltcir':'\u2A79','ltdot':'\u22D6','lthree':'\u22CB','ltimes':'\u22C9','ltlarr':'\u2976','ltquest':'\u2A7B','ltri':'\u25C3','ltrie':'\u22B4','ltrif':'\u25C2','ltrPar':'\u2996','lurdshar':'\u294A','luruhar':'\u2966','lvertneqq':'\u2268\uFE00','lvnE':'\u2268\uFE00','macr':'\xAF','male':'\u2642','malt':'\u2720','maltese':'\u2720','map':'\u21A6','Map':'\u2905','mapsto':'\u21A6','mapstodown':'\u21A7','mapstoleft':'\u21A4','mapstoup':'\u21A5','marker':'\u25AE','mcomma':'\u2A29','mcy':'\u043C','Mcy':'\u041C','mdash':'\u2014','mDDot':'\u223A','measuredangle':'\u2221','MediumSpace':'\u205F','Mellintrf':'\u2133','mfr':'\uD835\uDD2A','Mfr':'\uD835\uDD10','mho':'\u2127','micro':'\xB5','mid':'\u2223','midast':'*','midcir':'\u2AF0','middot':'\xB7','minus':'\u2212','minusb':'\u229F','minusd':'\u2238','minusdu':'\u2A2A','MinusPlus':'\u2213','mlcp':'\u2ADB','mldr':'\u2026','mnplus':'\u2213','models':'\u22A7','mopf':'\uD835\uDD5E','Mopf':'\uD835\uDD44','mp':'\u2213','mscr':'\uD835\uDCC2','Mscr':'\u2133','mstpos':'\u223E','mu':'\u03BC','Mu':'\u039C','multimap':'\u22B8','mumap':'\u22B8','nabla':'\u2207','nacute':'\u0144','Nacute':'\u0143','nang':'\u2220\u20D2','nap':'\u2249','napE':'\u2A70\u0338','napid':'\u224B\u0338','napos':'\u0149','napprox':'\u2249','natur':'\u266E','natural':'\u266E','naturals':'\u2115','nbsp':'\xA0','nbump':'\u224E\u0338','nbumpe':'\u224F\u0338','ncap':'\u2A43','ncaron':'\u0148','Ncaron':'\u0147','ncedil':'\u0146','Ncedil':'\u0145','ncong':'\u2247','ncongdot':'\u2A6D\u0338','ncup':'\u2A42','ncy':'\u043D','Ncy':'\u041D','ndash':'\u2013','ne':'\u2260','nearhk':'\u2924','nearr':'\u2197','neArr':'\u21D7','nearrow':'\u2197','nedot':'\u2250\u0338','NegativeMediumSpace':'\u200B','NegativeThickSpace':'\u200B','NegativeThinSpace':'\u200B','NegativeVeryThinSpace':'\u200B','nequiv':'\u2262','nesear':'\u2928','nesim':'\u2242\u0338','NestedGreaterGreater':'\u226B','NestedLessLess':'\u226A','NewLine':'\n','nexist':'\u2204','nexists':'\u2204','nfr':'\uD835\uDD2B','Nfr':'\uD835\uDD11','nge':'\u2271','ngE':'\u2267\u0338','ngeq':'\u2271','ngeqq':'\u2267\u0338','ngeqslant':'\u2A7E\u0338','nges':'\u2A7E\u0338','nGg':'\u22D9\u0338','ngsim':'\u2275','ngt':'\u226F','nGt':'\u226B\u20D2','ngtr':'\u226F','nGtv':'\u226B\u0338','nharr':'\u21AE','nhArr':'\u21CE','nhpar':'\u2AF2','ni':'\u220B','nis':'\u22FC','nisd':'\u22FA','niv':'\u220B','njcy':'\u045A','NJcy':'\u040A','nlarr':'\u219A','nlArr':'\u21CD','nldr':'\u2025','nle':'\u2270','nlE':'\u2266\u0338','nleftarrow':'\u219A','nLeftarrow':'\u21CD','nleftrightarrow':'\u21AE','nLeftrightarrow':'\u21CE','nleq':'\u2270','nleqq':'\u2266\u0338','nleqslant':'\u2A7D\u0338','nles':'\u2A7D\u0338','nless':'\u226E','nLl':'\u22D8\u0338','nlsim':'\u2274','nlt':'\u226E','nLt':'\u226A\u20D2','nltri':'\u22EA','nltrie':'\u22EC','nLtv':'\u226A\u0338','nmid':'\u2224','NoBreak':'\u2060','NonBreakingSpace':'\xA0','nopf':'\uD835\uDD5F','Nopf':'\u2115','not':'\xAC','Not':'\u2AEC','NotCongruent':'\u2262','NotCupCap':'\u226D','NotDoubleVerticalBar':'\u2226','NotElement':'\u2209','NotEqual':'\u2260','NotEqualTilde':'\u2242\u0338','NotExists':'\u2204','NotGreater':'\u226F','NotGreaterEqual':'\u2271','NotGreaterFullEqual':'\u2267\u0338','NotGreaterGreater':'\u226B\u0338','NotGreaterLess':'\u2279','NotGreaterSlantEqual':'\u2A7E\u0338','NotGreaterTilde':'\u2275','NotHumpDownHump':'\u224E\u0338','NotHumpEqual':'\u224F\u0338','notin':'\u2209','notindot':'\u22F5\u0338','notinE':'\u22F9\u0338','notinva':'\u2209','notinvb':'\u22F7','notinvc':'\u22F6','NotLeftTriangle':'\u22EA','NotLeftTriangleBar':'\u29CF\u0338','NotLeftTriangleEqual':'\u22EC','NotLess':'\u226E','NotLessEqual':'\u2270','NotLessGreater':'\u2278','NotLessLess':'\u226A\u0338','NotLessSlantEqual':'\u2A7D\u0338','NotLessTilde':'\u2274','NotNestedGreaterGreater':'\u2AA2\u0338','NotNestedLessLess':'\u2AA1\u0338','notni':'\u220C','notniva':'\u220C','notnivb':'\u22FE','notnivc':'\u22FD','NotPrecedes':'\u2280','NotPrecedesEqual':'\u2AAF\u0338','NotPrecedesSlantEqual':'\u22E0','NotReverseElement':'\u220C','NotRightTriangle':'\u22EB','NotRightTriangleBar':'\u29D0\u0338','NotRightTriangleEqual':'\u22ED','NotSquareSubset':'\u228F\u0338','NotSquareSubsetEqual':'\u22E2','NotSquareSuperset':'\u2290\u0338','NotSquareSupersetEqual':'\u22E3','NotSubset':'\u2282\u20D2','NotSubsetEqual':'\u2288','NotSucceeds':'\u2281','NotSucceedsEqual':'\u2AB0\u0338','NotSucceedsSlantEqual':'\u22E1','NotSucceedsTilde':'\u227F\u0338','NotSuperset':'\u2283\u20D2','NotSupersetEqual':'\u2289','NotTilde':'\u2241','NotTildeEqual':'\u2244','NotTildeFullEqual':'\u2247','NotTildeTilde':'\u2249','NotVerticalBar':'\u2224','npar':'\u2226','nparallel':'\u2226','nparsl':'\u2AFD\u20E5','npart':'\u2202\u0338','npolint':'\u2A14','npr':'\u2280','nprcue':'\u22E0','npre':'\u2AAF\u0338','nprec':'\u2280','npreceq':'\u2AAF\u0338','nrarr':'\u219B','nrArr':'\u21CF','nrarrc':'\u2933\u0338','nrarrw':'\u219D\u0338','nrightarrow':'\u219B','nRightarrow':'\u21CF','nrtri':'\u22EB','nrtrie':'\u22ED','nsc':'\u2281','nsccue':'\u22E1','nsce':'\u2AB0\u0338','nscr':'\uD835\uDCC3','Nscr':'\uD835\uDCA9','nshortmid':'\u2224','nshortparallel':'\u2226','nsim':'\u2241','nsime':'\u2244','nsimeq':'\u2244','nsmid':'\u2224','nspar':'\u2226','nsqsube':'\u22E2','nsqsupe':'\u22E3','nsub':'\u2284','nsube':'\u2288','nsubE':'\u2AC5\u0338','nsubset':'\u2282\u20D2','nsubseteq':'\u2288','nsubseteqq':'\u2AC5\u0338','nsucc':'\u2281','nsucceq':'\u2AB0\u0338','nsup':'\u2285','nsupe':'\u2289','nsupE':'\u2AC6\u0338','nsupset':'\u2283\u20D2','nsupseteq':'\u2289','nsupseteqq':'\u2AC6\u0338','ntgl':'\u2279','ntilde':'\xF1','Ntilde':'\xD1','ntlg':'\u2278','ntriangleleft':'\u22EA','ntrianglelefteq':'\u22EC','ntriangleright':'\u22EB','ntrianglerighteq':'\u22ED','nu':'\u03BD','Nu':'\u039D','num':'#','numero':'\u2116','numsp':'\u2007','nvap':'\u224D\u20D2','nvdash':'\u22AC','nvDash':'\u22AD','nVdash':'\u22AE','nVDash':'\u22AF','nvge':'\u2265\u20D2','nvgt':'>\u20D2','nvHarr':'\u2904','nvinfin':'\u29DE','nvlArr':'\u2902','nvle':'\u2264\u20D2','nvlt':'<\u20D2','nvltrie':'\u22B4\u20D2','nvrArr':'\u2903','nvrtrie':'\u22B5\u20D2','nvsim':'\u223C\u20D2','nwarhk':'\u2923','nwarr':'\u2196','nwArr':'\u21D6','nwarrow':'\u2196','nwnear':'\u2927','oacute':'\xF3','Oacute':'\xD3','oast':'\u229B','ocir':'\u229A','ocirc':'\xF4','Ocirc':'\xD4','ocy':'\u043E','Ocy':'\u041E','odash':'\u229D','odblac':'\u0151','Odblac':'\u0150','odiv':'\u2A38','odot':'\u2299','odsold':'\u29BC','oelig':'\u0153','OElig':'\u0152','ofcir':'\u29BF','ofr':'\uD835\uDD2C','Ofr':'\uD835\uDD12','ogon':'\u02DB','ograve':'\xF2','Ograve':'\xD2','ogt':'\u29C1','ohbar':'\u29B5','ohm':'\u03A9','oint':'\u222E','olarr':'\u21BA','olcir':'\u29BE','olcross':'\u29BB','oline':'\u203E','olt':'\u29C0','omacr':'\u014D','Omacr':'\u014C','omega':'\u03C9','Omega':'\u03A9','omicron':'\u03BF','Omicron':'\u039F','omid':'\u29B6','ominus':'\u2296','oopf':'\uD835\uDD60','Oopf':'\uD835\uDD46','opar':'\u29B7','OpenCurlyDoubleQuote':'\u201C','OpenCurlyQuote':'\u2018','operp':'\u29B9','oplus':'\u2295','or':'\u2228','Or':'\u2A54','orarr':'\u21BB','ord':'\u2A5D','order':'\u2134','orderof':'\u2134','ordf':'\xAA','ordm':'\xBA','origof':'\u22B6','oror':'\u2A56','orslope':'\u2A57','orv':'\u2A5B','oS':'\u24C8','oscr':'\u2134','Oscr':'\uD835\uDCAA','oslash':'\xF8','Oslash':'\xD8','osol':'\u2298','otilde':'\xF5','Otilde':'\xD5','otimes':'\u2297','Otimes':'\u2A37','otimesas':'\u2A36','ouml':'\xF6','Ouml':'\xD6','ovbar':'\u233D','OverBar':'\u203E','OverBrace':'\u23DE','OverBracket':'\u23B4','OverParenthesis':'\u23DC','par':'\u2225','para':'\xB6','parallel':'\u2225','parsim':'\u2AF3','parsl':'\u2AFD','part':'\u2202','PartialD':'\u2202','pcy':'\u043F','Pcy':'\u041F','percnt':'%','period':'.','permil':'\u2030','perp':'\u22A5','pertenk':'\u2031','pfr':'\uD835\uDD2D','Pfr':'\uD835\uDD13','phi':'\u03C6','Phi':'\u03A6','phiv':'\u03D5','phmmat':'\u2133','phone':'\u260E','pi':'\u03C0','Pi':'\u03A0','pitchfork':'\u22D4','piv':'\u03D6','planck':'\u210F','planckh':'\u210E','plankv':'\u210F','plus':'+','plusacir':'\u2A23','plusb':'\u229E','pluscir':'\u2A22','plusdo':'\u2214','plusdu':'\u2A25','pluse':'\u2A72','PlusMinus':'\xB1','plusmn':'\xB1','plussim':'\u2A26','plustwo':'\u2A27','pm':'\xB1','Poincareplane':'\u210C','pointint':'\u2A15','popf':'\uD835\uDD61','Popf':'\u2119','pound':'\xA3','pr':'\u227A','Pr':'\u2ABB','prap':'\u2AB7','prcue':'\u227C','pre':'\u2AAF','prE':'\u2AB3','prec':'\u227A','precapprox':'\u2AB7','preccurlyeq':'\u227C','Precedes':'\u227A','PrecedesEqual':'\u2AAF','PrecedesSlantEqual':'\u227C','PrecedesTilde':'\u227E','preceq':'\u2AAF','precnapprox':'\u2AB9','precneqq':'\u2AB5','precnsim':'\u22E8','precsim':'\u227E','prime':'\u2032','Prime':'\u2033','primes':'\u2119','prnap':'\u2AB9','prnE':'\u2AB5','prnsim':'\u22E8','prod':'\u220F','Product':'\u220F','profalar':'\u232E','profline':'\u2312','profsurf':'\u2313','prop':'\u221D','Proportion':'\u2237','Proportional':'\u221D','propto':'\u221D','prsim':'\u227E','prurel':'\u22B0','pscr':'\uD835\uDCC5','Pscr':'\uD835\uDCAB','psi':'\u03C8','Psi':'\u03A8','puncsp':'\u2008','qfr':'\uD835\uDD2E','Qfr':'\uD835\uDD14','qint':'\u2A0C','qopf':'\uD835\uDD62','Qopf':'\u211A','qprime':'\u2057','qscr':'\uD835\uDCC6','Qscr':'\uD835\uDCAC','quaternions':'\u210D','quatint':'\u2A16','quest':'?','questeq':'\u225F','quot':'"','QUOT':'"','rAarr':'\u21DB','race':'\u223D\u0331','racute':'\u0155','Racute':'\u0154','radic':'\u221A','raemptyv':'\u29B3','rang':'\u27E9','Rang':'\u27EB','rangd':'\u2992','range':'\u29A5','rangle':'\u27E9','raquo':'\xBB','rarr':'\u2192','rArr':'\u21D2','Rarr':'\u21A0','rarrap':'\u2975','rarrb':'\u21E5','rarrbfs':'\u2920','rarrc':'\u2933','rarrfs':'\u291E','rarrhk':'\u21AA','rarrlp':'\u21AC','rarrpl':'\u2945','rarrsim':'\u2974','rarrtl':'\u21A3','Rarrtl':'\u2916','rarrw':'\u219D','ratail':'\u291A','rAtail':'\u291C','ratio':'\u2236','rationals':'\u211A','rbarr':'\u290D','rBarr':'\u290F','RBarr':'\u2910','rbbrk':'\u2773','rbrace':'}','rbrack':']','rbrke':'\u298C','rbrksld':'\u298E','rbrkslu':'\u2990','rcaron':'\u0159','Rcaron':'\u0158','rcedil':'\u0157','Rcedil':'\u0156','rceil':'\u2309','rcub':'}','rcy':'\u0440','Rcy':'\u0420','rdca':'\u2937','rdldhar':'\u2969','rdquo':'\u201D','rdquor':'\u201D','rdsh':'\u21B3','Re':'\u211C','real':'\u211C','realine':'\u211B','realpart':'\u211C','reals':'\u211D','rect':'\u25AD','reg':'\xAE','REG':'\xAE','ReverseElement':'\u220B','ReverseEquilibrium':'\u21CB','ReverseUpEquilibrium':'\u296F','rfisht':'\u297D','rfloor':'\u230B','rfr':'\uD835\uDD2F','Rfr':'\u211C','rHar':'\u2964','rhard':'\u21C1','rharu':'\u21C0','rharul':'\u296C','rho':'\u03C1','Rho':'\u03A1','rhov':'\u03F1','RightAngleBracket':'\u27E9','rightarrow':'\u2192','Rightarrow':'\u21D2','RightArrow':'\u2192','RightArrowBar':'\u21E5','RightArrowLeftArrow':'\u21C4','rightarrowtail':'\u21A3','RightCeiling':'\u2309','RightDoubleBracket':'\u27E7','RightDownTeeVector':'\u295D','RightDownVector':'\u21C2','RightDownVectorBar':'\u2955','RightFloor':'\u230B','rightharpoondown':'\u21C1','rightharpoonup':'\u21C0','rightleftarrows':'\u21C4','rightleftharpoons':'\u21CC','rightrightarrows':'\u21C9','rightsquigarrow':'\u219D','RightTee':'\u22A2','RightTeeArrow':'\u21A6','RightTeeVector':'\u295B','rightthreetimes':'\u22CC','RightTriangle':'\u22B3','RightTriangleBar':'\u29D0','RightTriangleEqual':'\u22B5','RightUpDownVector':'\u294F','RightUpTeeVector':'\u295C','RightUpVector':'\u21BE','RightUpVectorBar':'\u2954','RightVector':'\u21C0','RightVectorBar':'\u2953','ring':'\u02DA','risingdotseq':'\u2253','rlarr':'\u21C4','rlhar':'\u21CC','rlm':'\u200F','rmoust':'\u23B1','rmoustache':'\u23B1','rnmid':'\u2AEE','roang':'\u27ED','roarr':'\u21FE','robrk':'\u27E7','ropar':'\u2986','ropf':'\uD835\uDD63','Ropf':'\u211D','roplus':'\u2A2E','rotimes':'\u2A35','RoundImplies':'\u2970','rpar':')','rpargt':'\u2994','rppolint':'\u2A12','rrarr':'\u21C9','Rrightarrow':'\u21DB','rsaquo':'\u203A','rscr':'\uD835\uDCC7','Rscr':'\u211B','rsh':'\u21B1','Rsh':'\u21B1','rsqb':']','rsquo':'\u2019','rsquor':'\u2019','rthree':'\u22CC','rtimes':'\u22CA','rtri':'\u25B9','rtrie':'\u22B5','rtrif':'\u25B8','rtriltri':'\u29CE','RuleDelayed':'\u29F4','ruluhar':'\u2968','rx':'\u211E','sacute':'\u015B','Sacute':'\u015A','sbquo':'\u201A','sc':'\u227B','Sc':'\u2ABC','scap':'\u2AB8','scaron':'\u0161','Scaron':'\u0160','sccue':'\u227D','sce':'\u2AB0','scE':'\u2AB4','scedil':'\u015F','Scedil':'\u015E','scirc':'\u015D','Scirc':'\u015C','scnap':'\u2ABA','scnE':'\u2AB6','scnsim':'\u22E9','scpolint':'\u2A13','scsim':'\u227F','scy':'\u0441','Scy':'\u0421','sdot':'\u22C5','sdotb':'\u22A1','sdote':'\u2A66','searhk':'\u2925','searr':'\u2198','seArr':'\u21D8','searrow':'\u2198','sect':'\xA7','semi':';','seswar':'\u2929','setminus':'\u2216','setmn':'\u2216','sext':'\u2736','sfr':'\uD835\uDD30','Sfr':'\uD835\uDD16','sfrown':'\u2322','sharp':'\u266F','shchcy':'\u0449','SHCHcy':'\u0429','shcy':'\u0448','SHcy':'\u0428','ShortDownArrow':'\u2193','ShortLeftArrow':'\u2190','shortmid':'\u2223','shortparallel':'\u2225','ShortRightArrow':'\u2192','ShortUpArrow':'\u2191','shy':'\xAD','sigma':'\u03C3','Sigma':'\u03A3','sigmaf':'\u03C2','sigmav':'\u03C2','sim':'\u223C','simdot':'\u2A6A','sime':'\u2243','simeq':'\u2243','simg':'\u2A9E','simgE':'\u2AA0','siml':'\u2A9D','simlE':'\u2A9F','simne':'\u2246','simplus':'\u2A24','simrarr':'\u2972','slarr':'\u2190','SmallCircle':'\u2218','smallsetminus':'\u2216','smashp':'\u2A33','smeparsl':'\u29E4','smid':'\u2223','smile':'\u2323','smt':'\u2AAA','smte':'\u2AAC','smtes':'\u2AAC\uFE00','softcy':'\u044C','SOFTcy':'\u042C','sol':'/','solb':'\u29C4','solbar':'\u233F','sopf':'\uD835\uDD64','Sopf':'\uD835\uDD4A','spades':'\u2660','spadesuit':'\u2660','spar':'\u2225','sqcap':'\u2293','sqcaps':'\u2293\uFE00','sqcup':'\u2294','sqcups':'\u2294\uFE00','Sqrt':'\u221A','sqsub':'\u228F','sqsube':'\u2291','sqsubset':'\u228F','sqsubseteq':'\u2291','sqsup':'\u2290','sqsupe':'\u2292','sqsupset':'\u2290','sqsupseteq':'\u2292','squ':'\u25A1','square':'\u25A1','Square':'\u25A1','SquareIntersection':'\u2293','SquareSubset':'\u228F','SquareSubsetEqual':'\u2291','SquareSuperset':'\u2290','SquareSupersetEqual':'\u2292','SquareUnion':'\u2294','squarf':'\u25AA','squf':'\u25AA','srarr':'\u2192','sscr':'\uD835\uDCC8','Sscr':'\uD835\uDCAE','ssetmn':'\u2216','ssmile':'\u2323','sstarf':'\u22C6','star':'\u2606','Star':'\u22C6','starf':'\u2605','straightepsilon':'\u03F5','straightphi':'\u03D5','strns':'\xAF','sub':'\u2282','Sub':'\u22D0','subdot':'\u2ABD','sube':'\u2286','subE':'\u2AC5','subedot':'\u2AC3','submult':'\u2AC1','subne':'\u228A','subnE':'\u2ACB','subplus':'\u2ABF','subrarr':'\u2979','subset':'\u2282','Subset':'\u22D0','subseteq':'\u2286','subseteqq':'\u2AC5','SubsetEqual':'\u2286','subsetneq':'\u228A','subsetneqq':'\u2ACB','subsim':'\u2AC7','subsub':'\u2AD5','subsup':'\u2AD3','succ':'\u227B','succapprox':'\u2AB8','succcurlyeq':'\u227D','Succeeds':'\u227B','SucceedsEqual':'\u2AB0','SucceedsSlantEqual':'\u227D','SucceedsTilde':'\u227F','succeq':'\u2AB0','succnapprox':'\u2ABA','succneqq':'\u2AB6','succnsim':'\u22E9','succsim':'\u227F','SuchThat':'\u220B','sum':'\u2211','Sum':'\u2211','sung':'\u266A','sup':'\u2283','Sup':'\u22D1','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','supdot':'\u2ABE','supdsub':'\u2AD8','supe':'\u2287','supE':'\u2AC6','supedot':'\u2AC4','Superset':'\u2283','SupersetEqual':'\u2287','suphsol':'\u27C9','suphsub':'\u2AD7','suplarr':'\u297B','supmult':'\u2AC2','supne':'\u228B','supnE':'\u2ACC','supplus':'\u2AC0','supset':'\u2283','Supset':'\u22D1','supseteq':'\u2287','supseteqq':'\u2AC6','supsetneq':'\u228B','supsetneqq':'\u2ACC','supsim':'\u2AC8','supsub':'\u2AD4','supsup':'\u2AD6','swarhk':'\u2926','swarr':'\u2199','swArr':'\u21D9','swarrow':'\u2199','swnwar':'\u292A','szlig':'\xDF','Tab':'\t','target':'\u2316','tau':'\u03C4','Tau':'\u03A4','tbrk':'\u23B4','tcaron':'\u0165','Tcaron':'\u0164','tcedil':'\u0163','Tcedil':'\u0162','tcy':'\u0442','Tcy':'\u0422','tdot':'\u20DB','telrec':'\u2315','tfr':'\uD835\uDD31','Tfr':'\uD835\uDD17','there4':'\u2234','therefore':'\u2234','Therefore':'\u2234','theta':'\u03B8','Theta':'\u0398','thetasym':'\u03D1','thetav':'\u03D1','thickapprox':'\u2248','thicksim':'\u223C','ThickSpace':'\u205F\u200A','thinsp':'\u2009','ThinSpace':'\u2009','thkap':'\u2248','thksim':'\u223C','thorn':'\xFE','THORN':'\xDE','tilde':'\u02DC','Tilde':'\u223C','TildeEqual':'\u2243','TildeFullEqual':'\u2245','TildeTilde':'\u2248','times':'\xD7','timesb':'\u22A0','timesbar':'\u2A31','timesd':'\u2A30','tint':'\u222D','toea':'\u2928','top':'\u22A4','topbot':'\u2336','topcir':'\u2AF1','topf':'\uD835\uDD65','Topf':'\uD835\uDD4B','topfork':'\u2ADA','tosa':'\u2929','tprime':'\u2034','trade':'\u2122','TRADE':'\u2122','triangle':'\u25B5','triangledown':'\u25BF','triangleleft':'\u25C3','trianglelefteq':'\u22B4','triangleq':'\u225C','triangleright':'\u25B9','trianglerighteq':'\u22B5','tridot':'\u25EC','trie':'\u225C','triminus':'\u2A3A','TripleDot':'\u20DB','triplus':'\u2A39','trisb':'\u29CD','tritime':'\u2A3B','trpezium':'\u23E2','tscr':'\uD835\uDCC9','Tscr':'\uD835\uDCAF','tscy':'\u0446','TScy':'\u0426','tshcy':'\u045B','TSHcy':'\u040B','tstrok':'\u0167','Tstrok':'\u0166','twixt':'\u226C','twoheadleftarrow':'\u219E','twoheadrightarrow':'\u21A0','uacute':'\xFA','Uacute':'\xDA','uarr':'\u2191','uArr':'\u21D1','Uarr':'\u219F','Uarrocir':'\u2949','ubrcy':'\u045E','Ubrcy':'\u040E','ubreve':'\u016D','Ubreve':'\u016C','ucirc':'\xFB','Ucirc':'\xDB','ucy':'\u0443','Ucy':'\u0423','udarr':'\u21C5','udblac':'\u0171','Udblac':'\u0170','udhar':'\u296E','ufisht':'\u297E','ufr':'\uD835\uDD32','Ufr':'\uD835\uDD18','ugrave':'\xF9','Ugrave':'\xD9','uHar':'\u2963','uharl':'\u21BF','uharr':'\u21BE','uhblk':'\u2580','ulcorn':'\u231C','ulcorner':'\u231C','ulcrop':'\u230F','ultri':'\u25F8','umacr':'\u016B','Umacr':'\u016A','uml':'\xA8','UnderBar':'_','UnderBrace':'\u23DF','UnderBracket':'\u23B5','UnderParenthesis':'\u23DD','Union':'\u22C3','UnionPlus':'\u228E','uogon':'\u0173','Uogon':'\u0172','uopf':'\uD835\uDD66','Uopf':'\uD835\uDD4C','uparrow':'\u2191','Uparrow':'\u21D1','UpArrow':'\u2191','UpArrowBar':'\u2912','UpArrowDownArrow':'\u21C5','updownarrow':'\u2195','Updownarrow':'\u21D5','UpDownArrow':'\u2195','UpEquilibrium':'\u296E','upharpoonleft':'\u21BF','upharpoonright':'\u21BE','uplus':'\u228E','UpperLeftArrow':'\u2196','UpperRightArrow':'\u2197','upsi':'\u03C5','Upsi':'\u03D2','upsih':'\u03D2','upsilon':'\u03C5','Upsilon':'\u03A5','UpTee':'\u22A5','UpTeeArrow':'\u21A5','upuparrows':'\u21C8','urcorn':'\u231D','urcorner':'\u231D','urcrop':'\u230E','uring':'\u016F','Uring':'\u016E','urtri':'\u25F9','uscr':'\uD835\uDCCA','Uscr':'\uD835\uDCB0','utdot':'\u22F0','utilde':'\u0169','Utilde':'\u0168','utri':'\u25B5','utrif':'\u25B4','uuarr':'\u21C8','uuml':'\xFC','Uuml':'\xDC','uwangle':'\u29A7','vangrt':'\u299C','varepsilon':'\u03F5','varkappa':'\u03F0','varnothing':'\u2205','varphi':'\u03D5','varpi':'\u03D6','varpropto':'\u221D','varr':'\u2195','vArr':'\u21D5','varrho':'\u03F1','varsigma':'\u03C2','varsubsetneq':'\u228A\uFE00','varsubsetneqq':'\u2ACB\uFE00','varsupsetneq':'\u228B\uFE00','varsupsetneqq':'\u2ACC\uFE00','vartheta':'\u03D1','vartriangleleft':'\u22B2','vartriangleright':'\u22B3','vBar':'\u2AE8','Vbar':'\u2AEB','vBarv':'\u2AE9','vcy':'\u0432','Vcy':'\u0412','vdash':'\u22A2','vDash':'\u22A8','Vdash':'\u22A9','VDash':'\u22AB','Vdashl':'\u2AE6','vee':'\u2228','Vee':'\u22C1','veebar':'\u22BB','veeeq':'\u225A','vellip':'\u22EE','verbar':'|','Verbar':'\u2016','vert':'|','Vert':'\u2016','VerticalBar':'\u2223','VerticalLine':'|','VerticalSeparator':'\u2758','VerticalTilde':'\u2240','VeryThinSpace':'\u200A','vfr':'\uD835\uDD33','Vfr':'\uD835\uDD19','vltri':'\u22B2','vnsub':'\u2282\u20D2','vnsup':'\u2283\u20D2','vopf':'\uD835\uDD67','Vopf':'\uD835\uDD4D','vprop':'\u221D','vrtri':'\u22B3','vscr':'\uD835\uDCCB','Vscr':'\uD835\uDCB1','vsubne':'\u228A\uFE00','vsubnE':'\u2ACB\uFE00','vsupne':'\u228B\uFE00','vsupnE':'\u2ACC\uFE00','Vvdash':'\u22AA','vzigzag':'\u299A','wcirc':'\u0175','Wcirc':'\u0174','wedbar':'\u2A5F','wedge':'\u2227','Wedge':'\u22C0','wedgeq':'\u2259','weierp':'\u2118','wfr':'\uD835\uDD34','Wfr':'\uD835\uDD1A','wopf':'\uD835\uDD68','Wopf':'\uD835\uDD4E','wp':'\u2118','wr':'\u2240','wreath':'\u2240','wscr':'\uD835\uDCCC','Wscr':'\uD835\uDCB2','xcap':'\u22C2','xcirc':'\u25EF','xcup':'\u22C3','xdtri':'\u25BD','xfr':'\uD835\uDD35','Xfr':'\uD835\uDD1B','xharr':'\u27F7','xhArr':'\u27FA','xi':'\u03BE','Xi':'\u039E','xlarr':'\u27F5','xlArr':'\u27F8','xmap':'\u27FC','xnis':'\u22FB','xodot':'\u2A00','xopf':'\uD835\uDD69','Xopf':'\uD835\uDD4F','xoplus':'\u2A01','xotime':'\u2A02','xrarr':'\u27F6','xrArr':'\u27F9','xscr':'\uD835\uDCCD','Xscr':'\uD835\uDCB3','xsqcup':'\u2A06','xuplus':'\u2A04','xutri':'\u25B3','xvee':'\u22C1','xwedge':'\u22C0','yacute':'\xFD','Yacute':'\xDD','yacy':'\u044F','YAcy':'\u042F','ycirc':'\u0177','Ycirc':'\u0176','ycy':'\u044B','Ycy':'\u042B','yen':'\xA5','yfr':'\uD835\uDD36','Yfr':'\uD835\uDD1C','yicy':'\u0457','YIcy':'\u0407','yopf':'\uD835\uDD6A','Yopf':'\uD835\uDD50','yscr':'\uD835\uDCCE','Yscr':'\uD835\uDCB4','yucy':'\u044E','YUcy':'\u042E','yuml':'\xFF','Yuml':'\u0178','zacute':'\u017A','Zacute':'\u0179','zcaron':'\u017E','Zcaron':'\u017D','zcy':'\u0437','Zcy':'\u0417','zdot':'\u017C','Zdot':'\u017B','zeetrf':'\u2128','ZeroWidthSpace':'\u200B','zeta':'\u03B6','Zeta':'\u0396','zfr':'\uD835\uDD37','Zfr':'\u2128','zhcy':'\u0436','ZHcy':'\u0416','zigrarr':'\u21DD','zopf':'\uD835\uDD6B','Zopf':'\u2124','zscr':'\uD835\uDCCF','Zscr':'\uD835\uDCB5','zwj':'\u200D','zwnj':'\u200C'}; - var decodeMapLegacy = {'aacute':'\xE1','Aacute':'\xC1','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','aelig':'\xE6','AElig':'\xC6','agrave':'\xE0','Agrave':'\xC0','amp':'&','AMP':'&','aring':'\xE5','Aring':'\xC5','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','brvbar':'\xA6','ccedil':'\xE7','Ccedil':'\xC7','cedil':'\xB8','cent':'\xA2','copy':'\xA9','COPY':'\xA9','curren':'\xA4','deg':'\xB0','divide':'\xF7','eacute':'\xE9','Eacute':'\xC9','ecirc':'\xEA','Ecirc':'\xCA','egrave':'\xE8','Egrave':'\xC8','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','frac12':'\xBD','frac14':'\xBC','frac34':'\xBE','gt':'>','GT':'>','iacute':'\xED','Iacute':'\xCD','icirc':'\xEE','Icirc':'\xCE','iexcl':'\xA1','igrave':'\xEC','Igrave':'\xCC','iquest':'\xBF','iuml':'\xEF','Iuml':'\xCF','laquo':'\xAB','lt':'<','LT':'<','macr':'\xAF','micro':'\xB5','middot':'\xB7','nbsp':'\xA0','not':'\xAC','ntilde':'\xF1','Ntilde':'\xD1','oacute':'\xF3','Oacute':'\xD3','ocirc':'\xF4','Ocirc':'\xD4','ograve':'\xF2','Ograve':'\xD2','ordf':'\xAA','ordm':'\xBA','oslash':'\xF8','Oslash':'\xD8','otilde':'\xF5','Otilde':'\xD5','ouml':'\xF6','Ouml':'\xD6','para':'\xB6','plusmn':'\xB1','pound':'\xA3','quot':'"','QUOT':'"','raquo':'\xBB','reg':'\xAE','REG':'\xAE','sect':'\xA7','shy':'\xAD','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','szlig':'\xDF','thorn':'\xFE','THORN':'\xDE','times':'\xD7','uacute':'\xFA','Uacute':'\xDA','ucirc':'\xFB','Ucirc':'\xDB','ugrave':'\xF9','Ugrave':'\xD9','uml':'\xA8','uuml':'\xFC','Uuml':'\xDC','yacute':'\xFD','Yacute':'\xDD','yen':'\xA5','yuml':'\xFF'}; - var decodeMapNumeric = {'0':'\uFFFD','128':'\u20AC','130':'\u201A','131':'\u0192','132':'\u201E','133':'\u2026','134':'\u2020','135':'\u2021','136':'\u02C6','137':'\u2030','138':'\u0160','139':'\u2039','140':'\u0152','142':'\u017D','145':'\u2018','146':'\u2019','147':'\u201C','148':'\u201D','149':'\u2022','150':'\u2013','151':'\u2014','152':'\u02DC','153':'\u2122','154':'\u0161','155':'\u203A','156':'\u0153','158':'\u017E','159':'\u0178'}; - var invalidReferenceCodePoints = [1,2,3,4,5,6,7,8,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,64976,64977,64978,64979,64980,64981,64982,64983,64984,64985,64986,64987,64988,64989,64990,64991,64992,64993,64994,64995,64996,64997,64998,64999,65000,65001,65002,65003,65004,65005,65006,65007,65534,65535,131070,131071,196606,196607,262142,262143,327678,327679,393214,393215,458750,458751,524286,524287,589822,589823,655358,655359,720894,720895,786430,786431,851966,851967,917502,917503,983038,983039,1048574,1048575,1114110,1114111]; - - /*--------------------------------------------------------------------------*/ - - var stringFromCharCode = String.fromCharCode; - - var object = {}; - var hasOwnProperty = object.hasOwnProperty; - var has = function(object, propertyName) { - return hasOwnProperty.call(object, propertyName); - }; - - var contains = function(array, value) { - var index = -1; - var length = array.length; - while (++index < length) { - if (array[index] == value) { - return true; - } - } - return false; - }; - - var merge = function(options, defaults) { - if (!options) { - return defaults; - } - var result = {}; - var key; - for (key in defaults) { - // A `hasOwnProperty` check is not needed here, since only recognized - // option names are used anyway. Any others are ignored. - result[key] = has(options, key) ? options[key] : defaults[key]; - } - return result; - }; - - // Modified version of `ucs2encode`; see https://mths.be/punycode. - var codePointToSymbol = function(codePoint, strict) { - var output = ''; - if ((codePoint >= 0xD800 && codePoint <= 0xDFFF) || codePoint > 0x10FFFF) { - // See issue #4: - // “Otherwise, if the number is in the range 0xD800 to 0xDFFF or is - // greater than 0x10FFFF, then this is a parse error. Return a U+FFFD - // REPLACEMENT CHARACTER.” - if (strict) { - parseError('character reference outside the permissible Unicode range'); - } - return '\uFFFD'; - } - if (has(decodeMapNumeric, codePoint)) { - if (strict) { - parseError('disallowed character reference'); - } - return decodeMapNumeric[codePoint]; - } - if (strict && contains(invalidReferenceCodePoints, codePoint)) { - parseError('disallowed character reference'); - } - if (codePoint > 0xFFFF) { - codePoint -= 0x10000; - output += stringFromCharCode(codePoint >>> 10 & 0x3FF | 0xD800); - codePoint = 0xDC00 | codePoint & 0x3FF; - } - output += stringFromCharCode(codePoint); - return output; - }; - - var hexEscape = function(codePoint) { - return '&#x' + codePoint.toString(16).toUpperCase() + ';'; - }; - - var decEscape = function(codePoint) { - return '&#' + codePoint + ';'; - }; - - var parseError = function(message) { - throw Error('Parse error: ' + message); - }; - - /*--------------------------------------------------------------------------*/ - - var encode = function(string, options) { - options = merge(options, encode.options); - var strict = options.strict; - if (strict && regexInvalidRawCodePoint.test(string)) { - parseError('forbidden code point'); - } - var encodeEverything = options.encodeEverything; - var useNamedReferences = options.useNamedReferences; - var allowUnsafeSymbols = options.allowUnsafeSymbols; - var escapeCodePoint = options.decimal ? decEscape : hexEscape; - - var escapeBmpSymbol = function(symbol) { - return escapeCodePoint(symbol.charCodeAt(0)); - }; - - if (encodeEverything) { - // Encode ASCII symbols. - string = string.replace(regexAsciiWhitelist, function(symbol) { - // Use named references if requested & possible. - if (useNamedReferences && has(encodeMap, symbol)) { - return '&' + encodeMap[symbol] + ';'; - } - return escapeBmpSymbol(symbol); - }); - // Shorten a few escapes that represent two symbols, of which at least one - // is within the ASCII range. - if (useNamedReferences) { - string = string - .replace(/>\u20D2/g, '>⃒') - .replace(/<\u20D2/g, '<⃒') - .replace(/fj/g, 'fj'); - } - // Encode non-ASCII symbols. - if (useNamedReferences) { - // Encode non-ASCII symbols that can be replaced with a named reference. - string = string.replace(regexEncodeNonAscii, function(string) { - // Note: there is no need to check `has(encodeMap, string)` here. - return '&' + encodeMap[string] + ';'; - }); - } - // Note: any remaining non-ASCII symbols are handled outside of the `if`. - } else if (useNamedReferences) { - // Apply named character references. - // Encode `<>"'&` using named character references. - if (!allowUnsafeSymbols) { - string = string.replace(regexEscape, function(string) { - return '&' + encodeMap[string] + ';'; // no need to check `has()` here - }); - } - // Shorten escapes that represent two symbols, of which at least one is - // `<>"'&`. - string = string - .replace(/>\u20D2/g, '>⃒') - .replace(/<\u20D2/g, '<⃒'); - // Encode non-ASCII symbols that can be replaced with a named reference. - string = string.replace(regexEncodeNonAscii, function(string) { - // Note: there is no need to check `has(encodeMap, string)` here. - return '&' + encodeMap[string] + ';'; - }); - } else if (!allowUnsafeSymbols) { - // Encode `<>"'&` using hexadecimal escapes, now that they’re not handled - // using named character references. - string = string.replace(regexEscape, escapeBmpSymbol); - } - return string - // Encode astral symbols. - .replace(regexAstralSymbols, function($0) { - // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae - var high = $0.charCodeAt(0); - var low = $0.charCodeAt(1); - var codePoint = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000; - return escapeCodePoint(codePoint); - }) - // Encode any remaining BMP symbols that are not printable ASCII symbols - // using a hexadecimal escape. - .replace(regexBmpWhitelist, escapeBmpSymbol); - }; - // Expose default options (so they can be overridden globally). - encode.options = { - 'allowUnsafeSymbols': false, - 'encodeEverything': false, - 'strict': false, - 'useNamedReferences': false, - 'decimal' : false - }; - - var decode = function(html, options) { - options = merge(options, decode.options); - var strict = options.strict; - if (strict && regexInvalidEntity.test(html)) { - parseError('malformed character reference'); - } - return html.replace(regexDecode, function($0, $1, $2, $3, $4, $5, $6, $7) { - var codePoint; - var semicolon; - var decDigits; - var hexDigits; - var reference; - var next; - if ($1) { - // Decode decimal escapes, e.g. `𝌆`. - decDigits = $1; - semicolon = $2; - if (strict && !semicolon) { - parseError('character reference was not terminated by a semicolon'); - } - codePoint = parseInt(decDigits, 10); - return codePointToSymbol(codePoint, strict); - } - if ($3) { - // Decode hexadecimal escapes, e.g. `𝌆`. - hexDigits = $3; - semicolon = $4; - if (strict && !semicolon) { - parseError('character reference was not terminated by a semicolon'); - } - codePoint = parseInt(hexDigits, 16); - return codePointToSymbol(codePoint, strict); - } - if ($5) { - // Decode named character references with trailing `;`, e.g. `©`. - reference = $5; - if (has(decodeMap, reference)) { - return decodeMap[reference]; - } else { - // Ambiguous ampersand. https://mths.be/notes/ambiguous-ampersands - if (strict) { - parseError( - 'named character reference was not terminated by a semicolon' - ); - } - return $0; - } - } - // If we’re still here, it’s a legacy reference for sure. No need for an - // extra `if` check. - // Decode named character references without trailing `;`, e.g. `&` - // This is only a parse error if it gets converted to `&`, or if it is - // followed by `=` in an attribute context. - reference = $6; - next = $7; - if (next && options.isAttributeValue) { - if (strict && next == '=') { - parseError('`&` did not start a character reference'); - } - return $0; - } else { - if (strict) { - parseError( - 'named character reference was not terminated by a semicolon' - ); - } - // Note: there is no need to check `has(decodeMapLegacy, reference)`. - return decodeMapLegacy[reference] + (next || ''); - } - }); - }; - // Expose default options (so they can be overridden globally). - decode.options = { - 'isAttributeValue': false, - 'strict': false - }; - - var escape = function(string) { - return string.replace(regexEscape, function($0) { - // Note: there is no need to check `has(escapeMap, $0)` here. - return escapeMap[$0]; - }); - }; - - /*--------------------------------------------------------------------------*/ - - var he = { - 'version': '1.1.1', - 'encode': encode, - 'decode': decode, - 'escape': escape, - 'unescape': decode - }; - - // Some AMD build optimizers, like r.js, check for specific condition patterns - // like the following: - if ( - typeof undefined == 'function' && - typeof undefined.amd == 'object' && - undefined.amd - ) { - undefined(function() { - return he; - }); - } else if (freeExports && !freeExports.nodeType) { - if (freeModule) { // in Node.js, io.js, or RingoJS v0.8.0+ - freeModule.exports = he; - } else { // in Narwhal or RingoJS v0.7.0- - for (var key in he) { - has(he, key) && (freeExports[key] = he[key]); - } - } - } else { // in Rhino or a web browser - root.he = he; - } - -}(commonjsGlobal)); -}); - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if ("development" !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href' - ? options.shouldDecodeNewlinesForHref - : options.shouldDecodeNewlines; - attrs[i] = { - name: args[1], - value: decodeAttr(value, shouldDecodeNewlines) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if ("development" !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - 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 () { - return str.charCodeAt(++index) -} - -function eof () { - return index >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var 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) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn$1; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn$1 = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn$1(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn$1, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - "development" !== 'production' && warn$1( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - 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.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if ("development" !== 'production' && el.tag === 'template') { - warn$1("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - "development" !== 'production' && warn$1( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else { - warn$1( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if ("development" !== 'production' && children[i].text !== ' ') { - warn$1( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if ("development" !== 'production' && el.key) { - warn$1( - "`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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if ("development" !== 'production' && slotScope) { - warn$1( - "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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // 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); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn$1); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if ("development" !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - { - var expression = parseText(value, delimiters); - if (expression) { - warn$1( - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - // #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'); - } - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - "development" !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn$1('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _el = el; - while (_el) { - if (_el.for && _el.alias === value) { - warn$1( - "<" + (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 = _el.parent; - } -} - -/* */ - -/** - * Expand input[v-model] with dyanmic 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]"> - */ - -function preTransformNode (el, options) { - if (el.tag === 'input') { - var map = el.attrsMap; - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - var typeBinding = getBindingAttr(el, 'type'); - var ifCondition = getAndRemoveAttr(el, 'v-if', true); - var ifConditionExtra = ifCondition ? ("&&(" + ifCondition + ")") : ""; - var hasElse = getAndRemoveAttr(el, 'v-else', true) != null; - var elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true); - // 1. checkbox - var 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 - var 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 - var 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value; - el.attrsList.push({ name: name, value: value }); -} - -var model$1 = { - preTransformNode: preTransformNode -}; - -var modules$1 = [ - klass, - style, - model$1 -]; - -/* */ - -var warn$2; - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; - - -function model$2 ( - el, - dir, - _warn -) { - warn$2 = _warn; - var value = dir.value; - var modifiers = dir.modifiers; - var tag = el.tag; - var type = el.attrsMap.type; - - { - // inputs with type="file" are read only and setting the input's - // value will throw an error. - if (tag === 'input' && type === 'file') { - warn$2( - "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" + - "File inputs are read only. Use a v-on:change listener instead." - ); - } - } - - 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 { - warn$2( - "<" + (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.' - ); - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var valueBinding = getBindingAttr(el, 'value') || 'null'; - var trueValueBinding = getBindingAttr(el, 'true-value') || 'true'; - var 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&&(" + value + "=$$a.concat([$$v]))}" + - "else{$$i>-1&&(" + value + "=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}" + - "}else{" + (genAssignmentCode(value, '$$c')) + "}", - null, true - ); -} - -function genRadioModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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') + "})"; - - var assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'; - var code = "var $$selectedVal = " + selectedVal + ";"; - code = code + " " + (genAssignmentCode(value, assignment)); - addHandler(el, 'change', code, null, true); -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var type = el.attrsMap.type; - var ref = modifiers || {}; - var lazy = ref.lazy; - var number = ref.number; - var trim = ref.trim; - var needCompositionGuard = !lazy && type !== 'range'; - var event = lazy - ? 'change' - : type === 'range' - ? RANGE_TOKEN - : 'input'; - - var valueExpression = '$event.target.value'; - if (trim) { - valueExpression = "$event.target.value.trim()"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var 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()'); - } -} - -/* */ - -function text (el, dir) { - if (dir.value) { - addProp(el, 'textContent', ("_s(" + (dir.value) + ")")); - } -} - -/* */ - -function html (el, dir) { - if (dir.value) { - addProp(el, 'innerHTML', ("_s(" + (dir.value) + ")")); - } -} - -var directives$1 = { - model: model$2, - text: text, - html: html -}; - -/* */ - -var baseOptions = { - expectHTML: true, - modules: modules$1, - directives: directives$1, - isPreTag: isPreTag, - isUnaryTag: isUnaryTag, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - staticKeys: genStaticKeys(modules$1) -}; - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if ("development" !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -function on (el, dir) { - if ("development" !== 'production' && dir.modifiers) { - warn("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate$1 ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData$2(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - "development" !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if ("development" !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData$2 (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if ("development" !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate$1(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -var plainStringRE = /^"(?:[^"\\]|\\.)*"$|^'(?:[^'\\]|\\.)*'$/; - -// let the model AST transform translate v-model into appropriate -// props bindings -function applyModelTransform (el, state) { - if (el.directives) { - for (var i = 0; i < el.directives.length; i++) { - var 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(function (p) { return p.name !== 'value'; }); - } - break - } - } - } -} - -function genAttrSegments ( - attrs -) { - return attrs.map(function (ref) { - var name = ref.name; - var value = ref.value; - - return genAttrSegment(name, value); - }) -} - -function genDOMPropSegments ( - props, - attrs -) { - var segments = []; - props.forEach(function (ref) { - var name = ref.name; - var value = ref.value; - - name = propsToAttrMap[name] || name.toLowerCase(); - if (isRenderableAttr(name) && - !(attrs && attrs.some(function (a) { return a.name === name; })) - ) { - segments.push(genAttrSegment(name, value)); - } - }); - return segments -} - -function genAttrSegment (name, value) { - 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 + "=" + value) - } - } else { - return { - type: EXPRESSION, - value: ("_ssrAttr(" + (JSON.stringify(name)) + "," + value + ")") - } - } -} - -function genClassSegments ( - staticClass, - classBinding -) { - if (staticClass && !classBinding) { - return [{ type: RAW, value: (" class=" + staticClass) }] - } else { - return [{ - type: EXPRESSION, - value: ("_ssrClass(" + (staticClass || 'null') + "," + (classBinding || 'null') + ")") - }] - } -} - -function genStyleSegments ( - staticStyle, - parsedStaticStyle, - styleBinding, - vShowExpression -) { - 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') + ")") - }] - } -} - -/* */ - -/** - * 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. - */ - -// optimizability constants -var 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 -}; - -var isPlatformReservedTag; - -function optimize (root, options) { - if (!root) { return } - isPlatformReservedTag = options.isReservedTag || no; - walk(root, true); -} - -function walk (node, isRoot) { - if (isUnOptimizableTree(node)) { - node.ssrOptimizability = optimizability.FALSE; - return - } - // root node or nodes with custom directives should always be a VNode - var selfUnoptimizable = isRoot || hasCustomDirective(node); - var check = function (child) { - if (child.ssrOptimizability !== optimizability.FULL) { - node.ssrOptimizability = selfUnoptimizable - ? optimizability.PARTIAL - : optimizability.SELF; - } - }; - if (selfUnoptimizable) { - node.ssrOptimizability = optimizability.CHILDREN; - } - if (node.type === 1) { - for (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - walk(child); - check(child); - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].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) { - var children = el.children; - var optimizedChildren = []; - - var currentOptimizableGroup = []; - var pushGroup = function () { - if (currentOptimizableGroup.length) { - optimizedChildren.push({ - type: 1, - parent: el, - tag: 'template', - attrsList: [], - attrsMap: {}, - children: currentOptimizableGroup, - ssrOptimizability: optimizability.FULL - }); - } - currentOptimizableGroup = []; - }; - - for (var i = 0; i < children.length; i++) { - var 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) { - 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 - ) -} - -var isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once'); - -function hasCustomDirective (node) { - return ( - node.type === 1 && - node.directives && - node.directives.some(function (d) { return !isBuiltInDir(d.name); }) - ) -} - -// <select v-model> cannot be optimized because it requires a runtime check -// to determine proper selected option -function isSelectWithModel (node) { - return ( - node.type === 1 && - node.tag === 'select' && - node.directives != null && - node.directives.some(function (d) { return d.name === 'model'; }) - ) -} - -/* */ - -// 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. - -// segment types -var RAW = 0; -var INTERPOLATION = 1; -var EXPRESSION = 2; - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genSSRElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genSSRElement (el, state) { - 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) { - var data = el.plain ? undefined : genData$2(el, state); - var children = stringifyChildren - ? ("[" + (genChildrenAsStringNode(el, state)) + "]") - : genSSRChildren(el, state, true); - return ("_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")") -} - -function genSSRChildren (el, state, checkSkip) { - 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) { - var 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) { - // 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) - } - - var openSegments = elementToOpenTagSegments(el, state); - var childrenSegments = childrenToSegments(el, state); - var ref = state.options; - var isUnaryTag = ref.isUnaryTag; - var close = (isUnaryTag && isUnaryTag(el.tag)) - ? [] - : [{ type: RAW, value: ("</" + (el.tag) + ">") }]; - return openSegments.concat(childrenSegments, close) -} - -function elementToOpenTagSegments (el, state) { - applyModelTransform(el, state); - var binding; - var 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) { - var 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, - state -) { - var segments = []; - for (var i = 0; i < children.length; i++) { - var 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) { - segments.push({ type: RAW, value: escape(c.text) }); - } - } - return segments -} - -function flattenSegments (segments) { - var mergedSegments = []; - var textBuffer = ''; - - var pushBuffer = function () { - if (textBuffer) { - mergedSegments.push(JSON.stringify(textBuffer)); - textBuffer = ''; - } - }; - - for (var i = 0; i < segments.length; i++) { - var 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('+') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn; - delete options.warn; - - /* istanbul ignore if */ - { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref = createCompiler(baseOptions); -var compileToFunctions = ref.compileToFunctions; - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function installSSRHelpers (vm) { - if (vm._ssrNode) { return } - var Ctor = vm.constructor; - while (Ctor.super) { - Ctor = Ctor.super; - } - extend(Ctor.prototype, { - _ssrEscape: escape, - _ssrNode: renderStringNode$1, - _ssrList: renderStringList, - _ssrAttr: renderAttr, - _ssrAttrs: renderAttrs$1, - _ssrDOMProps: renderDOMProps$1, - _ssrClass: renderSSRClass, - _ssrStyle: renderSSRStyle - }); -} - -var StringNode = function StringNode ( - open, - close, - children, - normalizationType -) { - 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$1 ( - open, - close, - children, - normalizationType -) { - return new StringNode(open, close, children, normalizationType) -} - -function renderStringList ( - val, - render -) { - var ret = ''; - var 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$1 (obj) { - var res = ''; - for (var key in obj) { - res += renderAttr(key, obj[key]); - } - return res -} - -function renderDOMProps$1 (obj) { - var res = ''; - for (var key in obj) { - var attr = propsToAttrMap[key] || key.toLowerCase(); - if (isRenderableAttr(attr)) { - res += renderAttr(attr, obj[key]); - } - } - return res -} - -function renderSSRClass ( - staticClass, - dynamic -) { - var res = renderClass$1(staticClass, dynamic); - return res === '' ? res : (" class=\"" + (escape(res)) + "\"") -} - -function renderSSRStyle ( - staticStyle, - dynamic, - extra -) { - var style = {}; - if (staticStyle) { extend(style, staticStyle); } - if (dynamic) { extend(style, normalizeStyleBinding(dynamic)); } - if (extra) { extend(style, extra); } - var res = genStyle(style); - return res === '' ? res : (" style=" + (JSON.stringify(escape(res)))) -} - -/* not type checking this file because flow doesn't play well with Proxy */ - -{ - var 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,' + - 'require' // for Webpack/Browserify - ); - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - -} - -{ - -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - "development" !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - "development" !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - "timeout (" + (res.timeout) + "ms)" - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -/* */ - -/* */ - -/* */ - - - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; - - - - - - - - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - { - - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - { - - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index$1 = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index$1 = queue.length = activatedChildren.length = 0; - has = {}; - { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // do not cache length because more watchers might be pushed - // as we run existing watchers - for (index$1 = 0; index$1 < queue.length; index$1++) { - watcher = queue[index$1]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var i = queue.length - 1; - while (i > index$1 && queue[i].id > watcher.id) { - i--; - } - queue.splice(i + 1, 0, watcher); - } - // queue the flush - if (!waiting) { - waiting = true; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = expOrFn.toString(); - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - "development" !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - "development" !== 'production' && 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 ("development" !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if ("development" !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if ("development" !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - "development" !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - "development" !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -/* */ - - - - - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - - - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var warned = Object.create(null); -var warnOnce = function (msg) { - if (!warned[msg]) { - warned[msg] = true; - console.warn(("\n\u001b[31m" + msg + "\u001b[39m\n")); - } -}; - -var onCompilationError = function (err, vm) { - var trace = vm ? generateComponentTrace(vm) : ''; - throw new Error(("\n\u001b[31m" + err + trace + "\u001b[39m\n")) -}; - -var normalizeRender = function (vm) { - var ref = vm.$options; - var render = ref.render; - var template = ref.template; - var _scopeId = ref._scopeId; - if (isUndef(render)) { - if (template) { - var compiled = compileToFunctions(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 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. - var register = options._ssrRegister; - if (write.caching && isDef(register)) { - write.componentBuffer[write.componentBuffer.length - 1].add(register); - } - return register -} - -function renderComponent (node, isRoot, context) { - var write = context.write; - var next = context.next; - var userContext = context.userContext; - - // check cache hit - var Ctor = node.componentOptions.Ctor; - var getKey = Ctor.options.serverCacheKey; - var name = Ctor.options.name; - var cache = context.cache; - var registerComponent = registerComponentForCache(Ctor.options, write); - - if (isDef(getKey) && isDef(cache) && isDef(name)) { - var key = name + '::' + getKey(node.componentOptions.propsData); - var has = context.has; - var get = context.get; - if (isDef(has)) { - has(key, function (hit) { - if (hit === true && isDef(get)) { - get(key, function (res) { - if (isDef(registerComponent)) { - registerComponent(userContext); - } - res.components.forEach(function (register) { return register(userContext); }); - write(res.html, next); - }); - } else { - renderComponentWithCache(node, isRoot, key, context); - } - }); - } else if (isDef(get)) { - get(key, function (res) { - if (isDef(res)) { - if (isDef(registerComponent)) { - registerComponent(userContext); - } - res.components.forEach(function (register) { return 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) { - var write = context.write; - write.caching = true; - var buffer = write.cacheBuffer; - var bufferIndex = buffer.push('') - 1; - var componentBuffer = write.componentBuffer; - componentBuffer.push(new Set()); - context.renderStates.push({ - type: 'ComponentWithCache', - key: key, - buffer: buffer, - bufferIndex: bufferIndex, - componentBuffer: componentBuffer - }); - renderComponentInner(node, isRoot, context); -} - -function renderComponentInner (node, isRoot, context) { - var prevActive = context.activeInstance; - // expose userContext on vnode - node.ssrContext = context.userContext; - var child = context.activeInstance = createComponentInstanceForVnode( - node, - context.activeInstance - ); - normalizeRender(child); - var childNode = child._render(); - childNode.parent = node; - context.renderStates.push({ - type: 'Component', - prevActive: prevActive - }); - renderNode(childNode, isRoot, context); -} - -function renderAsyncComponent (node, isRoot, context) { - var factory = node.asyncFactory; - - var resolve = function (comp) { - if (comp.__esModule && comp.default) { - comp = comp.default; - } - var ref = node.asyncMeta; - var data = ref.data; - var children = ref.children; - var tag = ref.tag; - var nodeContext = node.asyncMeta.context; - var resolvedNode = createComponent( - comp, - data, - nodeContext, - children, - tag - ); - if (resolvedNode) { - renderComponent(resolvedNode, isRoot, context); - } else { - reject(); - } - }; - - var reject = function (err) { - console.error("[vue-server-renderer] error when rendering async component:\n"); - if (err) { console.error(err.stack); } - context.write(("<!--" + (node.text) + "-->"), context.next); - }; - - if (factory.resolved) { - resolve(factory.resolved); - return - } - - var res; - try { - res = factory(resolve, reject); - } catch (e) { - reject(e); - } - if (res) { - if (typeof res.then === 'function') { - res.then(resolve, reject).catch(reject); - } else { - // new syntax in 2.3 - var comp = res.component; - if (comp && typeof comp.then === 'function') { - comp.then(resolve, reject).catch(reject); - } - } - } -} - -function renderStringNode (el, context) { - var write = context.write; - var next = context.next; - if (isUndef(el.children) || el.children.length === 0) { - write(el.open + (el.close || ''), next); - } else { - var children = el.children; - context.renderStates.push({ - type: 'Element', - rendered: 0, - total: children.length, - endTag: el.close, children: children - }); - write(el.open, next); - } -} - -function renderElement (el, isRoot, context) { - var write = context.write; - var next = context.next; - - if (isTrue(isRoot)) { - if (!el.data) { el.data = {}; } - if (!el.data.attrs) { el.data.attrs = {}; } - el.data.attrs[SSR_ATTR] = 'true'; - } - - if (el.functionalOptions) { - registerComponentForCache(el.functionalOptions, write); - } - - var startTag = renderStartingTag(el, context); - var 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 { - var children = el.children; - context.renderStates.push({ - type: 'Element', - rendered: 0, - total: children.length, - endTag: endTag, children: children - }); - write(startTag, next); - } -} - -function hasAncestorData (node) { - var parentNode = node.parent; - return isDef(parentNode) && (isDef(parentNode.data) || hasAncestorData(parentNode)) -} - -function getVShowDirectiveInfo (node) { - var dir; - var tmp; - - while (isDef(node)) { - if (node.data && node.data.directives) { - tmp = node.data.directives.find(function (dir) { return dir.name === 'show'; }); - if (tmp) { - dir = tmp; - } - } - node = node.parent; - } - return dir -} - -function renderStartingTag (node, context) { - var markup = "<" + (node.tag); - var directives = context.directives; - var modules = context.modules; - - // 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 - var dirs = node.data.directives; - if (dirs) { - for (var i = 0; i < dirs.length; i++) { - var name = dirs[i].name; - var dirRenderer = directives[name]; - if (dirRenderer && name !== 'show') { - // directives mutate the node's data - // which then gets rendered by modules - dirRenderer(node, dirs[i]); - } - } - } - - // v-show directive needs to be merged from parent to child - var vshowDirectiveInfo = getVShowDirectiveInfo(node); - if (vshowDirectiveInfo) { - directives.show(node, vshowDirectiveInfo); - } - - // apply other modules - for (var i$1 = 0; i$1 < modules.length; i$1++) { - var res = modules[i$1](node); - if (res) { - markup += res; - } - } - } - // attach scoped CSS ID - var scopeId; - var activeInstance = context.activeInstance; - if (isDef(activeInstance) && - activeInstance !== node.context && - isDef(scopeId = activeInstance.$options._scopeId) - ) { - markup += " " + ((scopeId)); - } - if (isDef(node.fnScopeId)) { - markup += " " + (node.fnScopeId); - } else { - while (isDef(node)) { - if (isDef(scopeId = node.context.$options._scopeId)) { - markup += " " + scopeId; - } - node = node.parent; - } - } - return markup + '>' -} - -function createRenderFunction ( - modules, - directives, - isUnaryTag, - cache -) { - return function render ( - component, - write, - userContext, - done - ) { - warned = Object.create(null); - var context = new RenderContext({ - activeInstance: component, - userContext: userContext, - write: write, done: done, renderNode: renderNode, - isUnaryTag: isUnaryTag, modules: modules, directives: directives, - cache: cache - }); - installSSRHelpers(component); - normalizeRender(component); - renderNode(component._render(), true, context); - } -} - -/* */ - -function createBasicRenderer (ref) { - if ( ref === void 0 ) ref = {}; - var modules = ref.modules; if ( modules === void 0 ) modules = []; - var directives = ref.directives; if ( directives === void 0 ) directives = {}; - var isUnaryTag = ref.isUnaryTag; if ( isUnaryTag === void 0 ) isUnaryTag = (function () { return false; }); - var cache = ref.cache; - - var render = createRenderFunction(modules, directives, isUnaryTag, cache); - - return function renderToString ( - component, - context, - done - ) { - if (typeof context === 'function') { - done = context; - context = {}; - } - var result = ''; - var write = createWriteFunction(function (text) { - result += text; - return false - }, done); - try { - render(component, write, context, function () { - done(null, result); - }); - } catch (e) { - done(e); - } - } -} - -/* */ - -var entryServerBasicRenderer = createBasicRenderer({ - modules: modules, - directives: directives, - isUnaryTag: isUnaryTag, - canBeLeftOpenTag: canBeLeftOpenTag -}); - -return entryServerBasicRenderer; - -}))); diff --git a/packages/vue-server-renderer/build.js b/packages/vue-server-renderer/build.js deleted file mode 100644 index dbbf5154af2..00000000000 --- a/packages/vue-server-renderer/build.js +++ /dev/null @@ -1,8744 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var he = _interopDefault(require('he')); - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - - - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ - - -/** - * Convert an Array-like object to a real Array. - */ - - -/** - * Mix properties into target object. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -/* */ - -var 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,http-equiv,' + - 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + - 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,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,type,usemap,value,width,wrap' -); - -/* istanbul ignore next */ -var isRenderableAttr = function (name) { - return ( - isAttr(name) || - name.indexOf('data-') === 0 || - name.indexOf('aria-') === 0 - ) -}; -var propsToAttrMap = { - acceptCharset: 'accept-charset', - className: 'class', - htmlFor: 'for', - httpEquiv: 'http-equiv' -}; - -var ESC = { - '<': '<', - '>': '>', - '"': '"', - '&': '&' -}; - -function escape (s) { - return s.replace(/[<>"&]/g, escapeChar) -} - -function escapeChar (a) { - return ESC[a] || a -} - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - - - - - - - -var isFalsyAttrValue = function (val) { - return val == null || val === false -}; - -/* */ - -function renderAttrs (node) { - var attrs = node.data.attrs; - var res = ''; - - var opts = node.parent && node.parent.componentOptions; - if (isUndef(opts) || opts.Ctor.options.inheritAttrs !== false) { - var parent = node.parent; - while (isDef(parent)) { - if (isDef(parent.data) && isDef(parent.data.attrs)) { - attrs = extend(extend({}, attrs), parent.data.attrs); - } - parent = parent.parent; - } - } - - if (isUndef(attrs)) { - return res - } - - for (var key in attrs) { - if (key === 'style') { - // leave it to the style module - continue - } - res += renderAttr(key, attrs[key]); - } - return res -} - -function renderAttr (key, value) { - if (isBooleanAttr(key)) { - if (!isFalsyAttrValue(value)) { - return (" " + key + "=\"" + key + "\"") - } - } else if (isEnumeratedAttr(key)) { - return (" " + key + "=\"" + (isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true') + "\"") - } else if (!isFalsyAttrValue(value)) { - return (" " + key + "=\"" + (escape(String(value))) + "\"") - } - return '' -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* */ - -function renderDOMProps (node) { - var props = node.data.domProps; - var res = ''; - - var 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 - } - - var attrs = node.data.attrs; - for (var 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, props[key], false); - } else { - // $flow-disable-line (WTF?) - var 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) { - var child = new VNode(undefined, undefined, undefined, text); - child.raw = raw; - node.children = [child]; -} - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ - - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - - -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ - - -/** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ -function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else if (process.env.NODE_ENV !== 'production') { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else if (process.env.NODE_ENV !== 'production') { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if (process.env.NODE_ENV !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (process.env.NODE_ENV !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - 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); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ - - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -/* */ - -function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode.data) { - data = mergeClassData(data, parentNode.data); - } - } - return renderClass$1(data.staticClass, data.class) -} - -function mergeClassData (child, parent) { - return { - staticClass: concat(child.staticClass, parent.staticClass), - class: isDef(child.class) - ? [child.class, parent.class] - : parent.class - } -} - -function renderClass$1 ( - staticClass, - dynamicClass -) { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -function concat (a, b) { - return a ? b ? (a + ' ' + b) : a : (b || '') -} - -function stringifyClass (value) { - 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) { - var res = ''; - var stringified; - for (var 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) { - var res = ''; - for (var key in value) { - if (value[key]) { - if (res) { res += ' '; } - res += key; - } - } - return res -} - -/* */ - - - -var 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. -var 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 -); - -var isPreTag = function (tag) { return tag === 'pre'; }; - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - - - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ - -/* */ - -function renderClass (node) { - var classList = genClassForVnode(node); - if (classList !== '') { - return (" class=\"" + (escape(classList)) + "\"") - } -} - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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) { - var 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 -function normalizeStyleBinding (bindingStyle) { - 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 - */ -function getStyle (vnode, checkChild) { - var res = {}; - var styleData; - - if (checkChild) { - var childNode = vnode; - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode; - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData); - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData); - } - - var parentNode = vnode; - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData); - } - } - return res -} - -/* */ - -function genStyle (style) { - var styleText = ''; - for (var key in style) { - var value = style[key]; - var hyphenatedKey = hyphenate(key); - if (Array.isArray(value)) { - for (var i = 0, len = value.length; i < len; i++) { - styleText += hyphenatedKey + ":" + (value[i]) + ";"; - } - } else { - styleText += hyphenatedKey + ":" + value + ";"; - } - } - return styleText -} - -function renderStyle (vnode) { - var styleText = genStyle(getStyle(vnode, false)); - if (styleText !== '') { - return (" style=" + (JSON.stringify(escape(styleText)))) - } -} - -var modules = [ - renderAttrs, - renderDOMProps, - renderClass, - renderStyle -]; - -/* */ - -function show (node, dir) { - if (!dir.value) { - var style = node.data.style || (node.data.style = {}); - style.display = 'none'; - } -} - -/* */ - -// 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. -function model (node, dir) { - if (!node.children) { return } - var value = dir.value; - var isMultiple = node.data.attrs && node.data.attrs.multiple; - for (var i = 0, l = node.children.length; i < l; i++) { - var option = node.children[i]; - if (option.tag === 'option') { - if (isMultiple) { - var 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) { - var 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) { - var data = option.data || (option.data = {}); - var attrs = data.attrs || (data.attrs = {}); - attrs.selected = ''; -} - -var baseDirectives = { - show: show, - model: model -}; - -/* */ - -var 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) -var 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 -var 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' -); - -/* */ - -var MAX_STACK_DEPTH = 1000; -var noop$1 = function (_) { return _; }; - -var defer = typeof process !== 'undefined' && process.nextTick - ? process.nextTick - : typeof Promise !== 'undefined' - ? function (fn) { return Promise.resolve().then(fn); } - : typeof setTimeout !== 'undefined' - ? setTimeout - : noop$1; - -if (defer === noop$1) { - 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.' - ) -} - -function createWriteFunction ( - write, - onError -) { - var stackDepth = 0; - var cachedWrite = function (text, next) { - if (text && cachedWrite.caching) { - cachedWrite.cacheBuffer[cachedWrite.cacheBuffer.length - 1] += text; - } - var waitForNext = write(text, next); - if (waitForNext !== true) { - if (stackDepth >= MAX_STACK_DEPTH) { - defer(function () { - try { next(); } catch (e) { - onError(e); - } - }); - } else { - stackDepth++; - next(); - stackDepth--; - } - } - }; - cachedWrite.caching = false; - cachedWrite.cacheBuffer = []; - cachedWrite.componentBuffer = []; - return cachedWrite -} - -/* */ - -/** - * 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) - */ - -var stream = require('stream'); - -var RenderStream = (function (superclass) { - function RenderStream (render) { - var this$1 = this; - - superclass.call(this); - this.buffer = ''; - this.render = render; - this.expectedSize = 0; - - this.write = createWriteFunction(function (text, next) { - var n = this$1.expectedSize; - this$1.buffer += text; - if (this$1.buffer.length >= n) { - this$1.next = next; - this$1.pushBySize(n); - return true // we will decide when to call next - } - return false - }, function (err) { - this$1.emit('error', err); - }); - - this.end = function () { - // the rendering is finished; we should push out the last of the buffer. - this$1.done = true; - this$1.push(this$1.buffer); - }; - } - - if ( superclass ) RenderStream.__proto__ = superclass; - RenderStream.prototype = Object.create( superclass && superclass.prototype ); - RenderStream.prototype.constructor = RenderStream; - - RenderStream.prototype.pushBySize = function pushBySize (n) { - var bufferToPush = this.buffer.substring(0, n); - this.buffer = this.buffer.substring(n); - this.push(bufferToPush); - }; - - RenderStream.prototype.tryRender = function tryRender () { - try { - this.render(this.write, this.end); - } catch (e) { - this.emit('error', e); - } - }; - - RenderStream.prototype.tryNext = function tryNext () { - try { - this.next(); - } catch (e) { - this.emit('error', e); - } - }; - - RenderStream.prototype._read = function _read (n) { - 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(); - } - }; - - return RenderStream; -}(stream.Readable)); - -/* */ - -var RenderContext = function RenderContext (options) { - 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; - - var 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); -}; - -RenderContext.prototype.next = function next () { - var lastState = this.renderStates[this.renderStates.length - 1]; - if (isUndef(lastState)) { - return this.done() - } - switch (lastState.type) { - case 'Element': - var children = lastState.children; - var total = lastState.total; - var rendered = lastState.rendered++; - if (rendered < total) { - this.renderNode(children[rendered], false, this); - } else { - this.renderStates.pop(); - this.write(lastState.endTag, this.next); - } - break - case 'Component': - this.renderStates.pop(); - this.activeInstance = lastState.prevActive; - this.next(); - break - case 'ComponentWithCache': - this.renderStates.pop(); - var buffer = lastState.buffer; - var bufferIndex = lastState.bufferIndex; - var componentBuffer = lastState.componentBuffer; - var key = lastState.key; - var 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. - this.write.caching = false; - } else { - // parent component is also being cached, - // merge self into parent's result - buffer[bufferIndex - 1] += result.html; - var prev = componentBuffer[bufferIndex - 1]; - result.components.forEach(function (c) { return prev.add(c); }); - } - buffer.length = bufferIndex; - componentBuffer.length = bufferIndex; - this.next(); - break - } -}; - -function normalizeAsync (cache, method) { - var fn = cache[method]; - if (isUndef(fn)) { - return - } else if (fn.length > 1) { - return function (key, cb) { return fn.call(cache, key, cb); } - } else { - return function (key, cb) { return cb(fn.call(cache, key)); } - } -} - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - process.env.NODE_ENV !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - if (process.env.NODE_ENV !== 'production' && staticClass) { - var expression = parseText(staticClass, options.delimiters); - if (expression) { - 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">.' - ); - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass); - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } -} - -function genData (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -var klass = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData -}; - -/* */ - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - if (staticStyle) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(staticStyle, options.delimiters); - if (expression) { - 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.staticStyle = JSON.stringify(parseStyleText(staticStyle)); - } - - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:(" + (el.styleBinding) + "),"; - } - return data -} - -var style = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$1 -}; - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if (process.env.NODE_ENV !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href' - ? options.shouldDecodeNewlinesForHref - : options.shouldDecodeNewlines; - attrs[i] = { - name: args[1], - value: decodeAttr(value, shouldDecodeNewlines) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if (process.env.NODE_ENV !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - 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 () { - return str.charCodeAt(++index) -} - -function eof () { - return index >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var 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) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn$1; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn$1 = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn$1(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn$1, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - process.env.NODE_ENV !== 'production' && warn$1( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - function checkRootConstraints (el) { - if (process.env.NODE_ENV !== 'production') { - if (el.tag === 'slot' || el.tag === 'template') { - warnOnce( - "Cannot use <" + (el.tag) + "> as component root element because it may " + - 'contain multiple nodes.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else if (process.env.NODE_ENV !== 'production') { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - if (process.env.NODE_ENV !== 'production') { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if (process.env.NODE_ENV !== 'production' && el.tag === 'template') { - warn$1("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - process.env.NODE_ENV !== 'production' && warn$1( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else if (process.env.NODE_ENV !== 'production') { - warn$1( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if (process.env.NODE_ENV !== 'production' && children[i].text !== ' ') { - warn$1( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if (process.env.NODE_ENV !== 'production' && el.key) { - warn$1( - "`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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && slotScope) { - warn$1( - "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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // 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); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn$1); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if (process.env.NODE_ENV !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(value, delimiters); - if (expression) { - warn$1( - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - // #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'); - } - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - process.env.NODE_ENV !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn$1('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _el = el; - while (_el) { - if (_el.for && _el.alias === value) { - warn$1( - "<" + (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 = _el.parent; - } -} - -/* */ - -/** - * Expand input[v-model] with dyanmic 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]"> - */ - -function preTransformNode (el, options) { - if (el.tag === 'input') { - var map = el.attrsMap; - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - var typeBinding = getBindingAttr(el, 'type'); - var ifCondition = getAndRemoveAttr(el, 'v-if', true); - var ifConditionExtra = ifCondition ? ("&&(" + ifCondition + ")") : ""; - var hasElse = getAndRemoveAttr(el, 'v-else', true) != null; - var elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true); - // 1. checkbox - var 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 - var 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 - var 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value; - el.attrsList.push({ name: name, value: value }); -} - -var model$1 = { - preTransformNode: preTransformNode -}; - -var modules$1 = [ - klass, - style, - model$1 -]; - -/* */ - -var warn$2; - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; - - -function model$2 ( - el, - dir, - _warn -) { - warn$2 = _warn; - var value = dir.value; - var modifiers = dir.modifiers; - var tag = el.tag; - var type = el.attrsMap.type; - - if (process.env.NODE_ENV !== 'production') { - // inputs with type="file" are read only and setting the input's - // value will throw an error. - if (tag === 'input' && type === 'file') { - warn$2( - "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" + - "File inputs are read only. Use a v-on:change listener instead." - ); - } - } - - 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 (process.env.NODE_ENV !== 'production') { - warn$2( - "<" + (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.' - ); - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var valueBinding = getBindingAttr(el, 'value') || 'null'; - var trueValueBinding = getBindingAttr(el, 'true-value') || 'true'; - var 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&&(" + value + "=$$a.concat([$$v]))}" + - "else{$$i>-1&&(" + value + "=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}" + - "}else{" + (genAssignmentCode(value, '$$c')) + "}", - null, true - ); -} - -function genRadioModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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') + "})"; - - var assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'; - var code = "var $$selectedVal = " + selectedVal + ";"; - code = code + " " + (genAssignmentCode(value, assignment)); - addHandler(el, 'change', code, null, true); -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var type = el.attrsMap.type; - var ref = modifiers || {}; - var lazy = ref.lazy; - var number = ref.number; - var trim = ref.trim; - var needCompositionGuard = !lazy && type !== 'range'; - var event = lazy - ? 'change' - : type === 'range' - ? RANGE_TOKEN - : 'input'; - - var valueExpression = '$event.target.value'; - if (trim) { - valueExpression = "$event.target.value.trim()"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var 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()'); - } -} - -/* */ - -function text (el, dir) { - if (dir.value) { - addProp(el, 'textContent', ("_s(" + (dir.value) + ")")); - } -} - -/* */ - -function html (el, dir) { - if (dir.value) { - addProp(el, 'innerHTML', ("_s(" + (dir.value) + ")")); - } -} - -var directives = { - model: model$2, - text: text, - html: html -}; - -/* */ - -var baseOptions = { - expectHTML: true, - modules: modules$1, - directives: directives, - isPreTag: isPreTag, - isUnaryTag: isUnaryTag, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - staticKeys: genStaticKeys(modules$1) -}; - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if (process.env.NODE_ENV !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -function on (el, dir) { - if (process.env.NODE_ENV !== 'production' && dir.modifiers) { - warn("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives$1 = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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$1), options.directives); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate$1 ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData$2(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - process.env.NODE_ENV !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if (process.env.NODE_ENV !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData$2 (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if (process.env.NODE_ENV !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate$1(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -var plainStringRE = /^"(?:[^"\\]|\\.)*"$|^'(?:[^'\\]|\\.)*'$/; - -// let the model AST transform translate v-model into appropriate -// props bindings -function applyModelTransform (el, state) { - if (el.directives) { - for (var i = 0; i < el.directives.length; i++) { - var 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(function (p) { return p.name !== 'value'; }); - } - break - } - } - } -} - -function genAttrSegments ( - attrs -) { - return attrs.map(function (ref) { - var name = ref.name; - var value = ref.value; - - return genAttrSegment(name, value); - }) -} - -function genDOMPropSegments ( - props, - attrs -) { - var segments = []; - props.forEach(function (ref) { - var name = ref.name; - var value = ref.value; - - name = propsToAttrMap[name] || name.toLowerCase(); - if (isRenderableAttr(name) && - !(attrs && attrs.some(function (a) { return a.name === name; })) - ) { - segments.push(genAttrSegment(name, value)); - } - }); - return segments -} - -function genAttrSegment (name, value) { - 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 + "=" + value) - } - } else { - return { - type: EXPRESSION, - value: ("_ssrAttr(" + (JSON.stringify(name)) + "," + value + ")") - } - } -} - -function genClassSegments ( - staticClass, - classBinding -) { - if (staticClass && !classBinding) { - return [{ type: RAW, value: (" class=" + staticClass) }] - } else { - return [{ - type: EXPRESSION, - value: ("_ssrClass(" + (staticClass || 'null') + "," + (classBinding || 'null') + ")") - }] - } -} - -function genStyleSegments ( - staticStyle, - parsedStaticStyle, - styleBinding, - vShowExpression -) { - 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') + ")") - }] - } -} - -/* */ - -/** - * 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. - */ - -// optimizability constants -var 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 -}; - -var isPlatformReservedTag; - -function optimize (root, options) { - if (!root) { return } - isPlatformReservedTag = options.isReservedTag || no; - walk(root, true); -} - -function walk (node, isRoot) { - if (isUnOptimizableTree(node)) { - node.ssrOptimizability = optimizability.FALSE; - return - } - // root node or nodes with custom directives should always be a VNode - var selfUnoptimizable = isRoot || hasCustomDirective(node); - var check = function (child) { - if (child.ssrOptimizability !== optimizability.FULL) { - node.ssrOptimizability = selfUnoptimizable - ? optimizability.PARTIAL - : optimizability.SELF; - } - }; - if (selfUnoptimizable) { - node.ssrOptimizability = optimizability.CHILDREN; - } - if (node.type === 1) { - for (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - walk(child); - check(child); - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].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) { - var children = el.children; - var optimizedChildren = []; - - var currentOptimizableGroup = []; - var pushGroup = function () { - if (currentOptimizableGroup.length) { - optimizedChildren.push({ - type: 1, - parent: el, - tag: 'template', - attrsList: [], - attrsMap: {}, - children: currentOptimizableGroup, - ssrOptimizability: optimizability.FULL - }); - } - currentOptimizableGroup = []; - }; - - for (var i = 0; i < children.length; i++) { - var 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) { - 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 - ) -} - -var isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once'); - -function hasCustomDirective (node) { - return ( - node.type === 1 && - node.directives && - node.directives.some(function (d) { return !isBuiltInDir(d.name); }) - ) -} - -// <select v-model> cannot be optimized because it requires a runtime check -// to determine proper selected option -function isSelectWithModel (node) { - return ( - node.type === 1 && - node.tag === 'select' && - node.directives != null && - node.directives.some(function (d) { return d.name === 'model'; }) - ) -} - -/* */ - -// 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. - -// segment types -var RAW = 0; -var INTERPOLATION = 1; -var EXPRESSION = 2; - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genSSRElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genSSRElement (el, state) { - 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) { - var data = el.plain ? undefined : genData$2(el, state); - var children = stringifyChildren - ? ("[" + (genChildrenAsStringNode(el, state)) + "]") - : genSSRChildren(el, state, true); - return ("_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")") -} - -function genSSRChildren (el, state, checkSkip) { - 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) { - var 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) { - // 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) - } - - var openSegments = elementToOpenTagSegments(el, state); - var childrenSegments = childrenToSegments(el, state); - var ref = state.options; - var isUnaryTag = ref.isUnaryTag; - var close = (isUnaryTag && isUnaryTag(el.tag)) - ? [] - : [{ type: RAW, value: ("</" + (el.tag) + ">") }]; - return openSegments.concat(childrenSegments, close) -} - -function elementToOpenTagSegments (el, state) { - applyModelTransform(el, state); - var binding; - var 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) { - var 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, - state -) { - var segments = []; - for (var i = 0; i < children.length; i++) { - var 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) { - segments.push({ type: RAW, value: escape(c.text) }); - } - } - return segments -} - -function flattenSegments (segments) { - var mergedSegments = []; - var textBuffer = ''; - - var pushBuffer = function () { - if (textBuffer) { - mergedSegments.push(JSON.stringify(textBuffer)); - textBuffer = ''; - } - }; - - for (var i = 0; i < segments.length; i++) { - var 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('+') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn; - delete options.warn; - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - if (process.env.NODE_ENV !== 'production') { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 (process.env.NODE_ENV !== 'production') { - if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref = createCompiler(baseOptions); -var compileToFunctions = ref.compileToFunctions; - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function installSSRHelpers (vm) { - if (vm._ssrNode) { return } - var Ctor = vm.constructor; - while (Ctor.super) { - Ctor = Ctor.super; - } - extend(Ctor.prototype, { - _ssrEscape: escape, - _ssrNode: renderStringNode$1, - _ssrList: renderStringList, - _ssrAttr: renderAttr, - _ssrAttrs: renderAttrs$1, - _ssrDOMProps: renderDOMProps$1, - _ssrClass: renderSSRClass, - _ssrStyle: renderSSRStyle - }); -} - -var StringNode = function StringNode ( - open, - close, - children, - normalizationType -) { - 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$1 ( - open, - close, - children, - normalizationType -) { - return new StringNode(open, close, children, normalizationType) -} - -function renderStringList ( - val, - render -) { - var ret = ''; - var 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$1 (obj) { - var res = ''; - for (var key in obj) { - res += renderAttr(key, obj[key]); - } - return res -} - -function renderDOMProps$1 (obj) { - var res = ''; - for (var key in obj) { - var attr = propsToAttrMap[key] || key.toLowerCase(); - if (isRenderableAttr(attr)) { - res += renderAttr(attr, obj[key]); - } - } - return res -} - -function renderSSRClass ( - staticClass, - dynamic -) { - var res = renderClass$1(staticClass, dynamic); - return res === '' ? res : (" class=\"" + (escape(res)) + "\"") -} - -function renderSSRStyle ( - staticStyle, - dynamic, - extra -) { - var style = {}; - if (staticStyle) { extend(style, staticStyle); } - if (dynamic) { extend(style, normalizeStyleBinding(dynamic)); } - if (extra) { extend(style, extra); } - var res = genStyle(style); - return res === '' ? res : (" style=" + (JSON.stringify(escape(res)))) -} - -/* not type checking this file because flow doesn't play well with Proxy */ - -if (process.env.NODE_ENV !== 'production') { - var 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,' + - 'require' // for Webpack/Browserify - ); - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - if (process.env.NODE_ENV !== 'production') { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - process.env.NODE_ENV !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - process.env.NODE_ENV !== 'production' - ? ("timeout (" + (res.timeout) + "ms)") - : null - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -/* */ - -/* */ - -/* */ - - - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); - target = undefined; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var 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 (var name$1 in slots) { - if (slots[name$1].every(isWhitespace)) { - delete slots[name$1]; - } - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; - - - - - - - - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index$1 = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index$1 = queue.length = activatedChildren.length = 0; - has = {}; - if (process.env.NODE_ENV !== 'production') { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // do not cache length because more watchers might be pushed - // as we run existing watchers - for (index$1 = 0; index$1 < queue.length; index$1++) { - watcher = queue[index$1]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var i = queue.length - 1; - while (i > index$1 && queue[i].id > watcher.id) { - i--; - } - queue.splice(i + 1, 0, watcher); - } - // queue the flush - if (!waiting) { - waiting = true; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = process.env.NODE_ENV !== 'production' - ? expOrFn.toString() - : ''; - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - process.env.NODE_ENV !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - var nodes; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - nodes = scopedSlotFn(props) || fallback; - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes) { - if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { - warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - } - slotNodes._rendered = true; - } - nodes = slotNodes || fallback; - } - - var target = props && props.slot; - if (target) { - return this.$createElement('template', { slot: target }, nodes) - } else { - return nodes - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var options = this.$options; - var cached = options.cached || (options.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -/* */ - - - - - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else if (process.env.NODE_ENV !== 'production') { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - - - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - if (process.env.NODE_ENV !== 'production') { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var warned = Object.create(null); -var warnOnce = function (msg) { - if (!warned[msg]) { - warned[msg] = true; - console.warn(("\n\u001b[31m" + msg + "\u001b[39m\n")); - } -}; - -var onCompilationError = function (err, vm) { - var trace = vm ? generateComponentTrace(vm) : ''; - throw new Error(("\n\u001b[31m" + err + trace + "\u001b[39m\n")) -}; - -var normalizeRender = function (vm) { - var ref = vm.$options; - var render = ref.render; - var template = ref.template; - var _scopeId = ref._scopeId; - if (isUndef(render)) { - if (template) { - var compiled = compileToFunctions(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 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. - var register = options._ssrRegister; - if (write.caching && isDef(register)) { - write.componentBuffer[write.componentBuffer.length - 1].add(register); - } - return register -} - -function renderComponent (node, isRoot, context) { - var write = context.write; - var next = context.next; - var userContext = context.userContext; - - // check cache hit - var Ctor = node.componentOptions.Ctor; - var getKey = Ctor.options.serverCacheKey; - var name = Ctor.options.name; - var cache = context.cache; - var registerComponent = registerComponentForCache(Ctor.options, write); - - if (isDef(getKey) && isDef(cache) && isDef(name)) { - var key = name + '::' + getKey(node.componentOptions.propsData); - var has = context.has; - var get = context.get; - if (isDef(has)) { - has(key, function (hit) { - if (hit === true && isDef(get)) { - get(key, function (res) { - if (isDef(registerComponent)) { - registerComponent(userContext); - } - res.components.forEach(function (register) { return register(userContext); }); - write(res.html, next); - }); - } else { - renderComponentWithCache(node, isRoot, key, context); - } - }); - } else if (isDef(get)) { - get(key, function (res) { - if (isDef(res)) { - if (isDef(registerComponent)) { - registerComponent(userContext); - } - res.components.forEach(function (register) { return 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) { - var write = context.write; - write.caching = true; - var buffer = write.cacheBuffer; - var bufferIndex = buffer.push('') - 1; - var componentBuffer = write.componentBuffer; - componentBuffer.push(new Set()); - context.renderStates.push({ - type: 'ComponentWithCache', - key: key, - buffer: buffer, - bufferIndex: bufferIndex, - componentBuffer: componentBuffer - }); - renderComponentInner(node, isRoot, context); -} - -function renderComponentInner (node, isRoot, context) { - var prevActive = context.activeInstance; - // expose userContext on vnode - node.ssrContext = context.userContext; - var child = context.activeInstance = createComponentInstanceForVnode( - node, - context.activeInstance - ); - normalizeRender(child); - var childNode = child._render(); - childNode.parent = node; - context.renderStates.push({ - type: 'Component', - prevActive: prevActive - }); - renderNode(childNode, isRoot, context); -} - -function renderAsyncComponent (node, isRoot, context) { - var factory = node.asyncFactory; - - var resolve = function (comp) { - if (comp.__esModule && comp.default) { - comp = comp.default; - } - var ref = node.asyncMeta; - var data = ref.data; - var children = ref.children; - var tag = ref.tag; - var nodeContext = node.asyncMeta.context; - var resolvedNode = createComponent( - comp, - data, - nodeContext, - children, - tag - ); - if (resolvedNode) { - renderComponent(resolvedNode, isRoot, context); - } else { - reject(); - } - }; - - var reject = function (err) { - console.error("[vue-server-renderer] error when rendering async component:\n"); - if (err) { console.error(err.stack); } - context.write(("<!--" + (node.text) + "-->"), context.next); - }; - - if (factory.resolved) { - resolve(factory.resolved); - return - } - - var res; - try { - res = factory(resolve, reject); - } catch (e) { - reject(e); - } - if (res) { - if (typeof res.then === 'function') { - res.then(resolve, reject).catch(reject); - } else { - // new syntax in 2.3 - var comp = res.component; - if (comp && typeof comp.then === 'function') { - comp.then(resolve, reject).catch(reject); - } - } - } -} - -function renderStringNode (el, context) { - var write = context.write; - var next = context.next; - if (isUndef(el.children) || el.children.length === 0) { - write(el.open + (el.close || ''), next); - } else { - var children = el.children; - context.renderStates.push({ - type: 'Element', - rendered: 0, - total: children.length, - endTag: el.close, children: children - }); - write(el.open, next); - } -} - -function renderElement (el, isRoot, context) { - var write = context.write; - var next = context.next; - - if (isTrue(isRoot)) { - if (!el.data) { el.data = {}; } - if (!el.data.attrs) { el.data.attrs = {}; } - el.data.attrs[SSR_ATTR] = 'true'; - } - - if (el.functionalOptions) { - registerComponentForCache(el.functionalOptions, write); - } - - var startTag = renderStartingTag(el, context); - var 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 { - var children = el.children; - context.renderStates.push({ - type: 'Element', - rendered: 0, - total: children.length, - endTag: endTag, children: children - }); - write(startTag, next); - } -} - -function hasAncestorData (node) { - var parentNode = node.parent; - return isDef(parentNode) && (isDef(parentNode.data) || hasAncestorData(parentNode)) -} - -function getVShowDirectiveInfo (node) { - var dir; - var tmp; - - while (isDef(node)) { - if (node.data && node.data.directives) { - tmp = node.data.directives.find(function (dir) { return dir.name === 'show'; }); - if (tmp) { - dir = tmp; - } - } - node = node.parent; - } - return dir -} - -function renderStartingTag (node, context) { - var markup = "<" + (node.tag); - var directives = context.directives; - var modules = context.modules; - - // 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 - var dirs = node.data.directives; - if (dirs) { - for (var i = 0; i < dirs.length; i++) { - var name = dirs[i].name; - var dirRenderer = directives[name]; - if (dirRenderer && name !== 'show') { - // directives mutate the node's data - // which then gets rendered by modules - dirRenderer(node, dirs[i]); - } - } - } - - // v-show directive needs to be merged from parent to child - var vshowDirectiveInfo = getVShowDirectiveInfo(node); - if (vshowDirectiveInfo) { - directives.show(node, vshowDirectiveInfo); - } - - // apply other modules - for (var i$1 = 0; i$1 < modules.length; i$1++) { - var res = modules[i$1](node); - if (res) { - markup += res; - } - } - } - // attach scoped CSS ID - var scopeId; - var activeInstance = context.activeInstance; - if (isDef(activeInstance) && - activeInstance !== node.context && - isDef(scopeId = activeInstance.$options._scopeId) - ) { - markup += " " + ((scopeId)); - } - if (isDef(node.fnScopeId)) { - markup += " " + (node.fnScopeId); - } else { - while (isDef(node)) { - if (isDef(scopeId = node.context.$options._scopeId)) { - markup += " " + scopeId; - } - node = node.parent; - } - } - return markup + '>' -} - -function createRenderFunction ( - modules, - directives, - isUnaryTag, - cache -) { - return function render ( - component, - write, - userContext, - done - ) { - warned = Object.create(null); - var context = new RenderContext({ - activeInstance: component, - userContext: userContext, - write: write, done: done, renderNode: renderNode, - isUnaryTag: isUnaryTag, modules: modules, directives: directives, - cache: cache - }); - installSSRHelpers(component); - normalizeRender(component); - renderNode(component._render(), true, context); - } -} - -/* */ - -var isJS = function (file) { return /\.js(\?[^.]+)?$/.test(file); }; - -var isCSS = function (file) { return /\.css(\?[^.]+)?$/.test(file); }; - -function createPromiseCallback () { - var resolve, reject; - var promise = new Promise(function (_resolve, _reject) { - resolve = _resolve; - reject = _reject; - }); - var cb = function (err, res) { - if (err) { return reject(err) } - resolve(res || ''); - }; - return { promise: promise, cb: cb } -} - -/* */ - -var Transform = require('stream').Transform; - - - -var TemplateStream = (function (Transform) { - function TemplateStream ( - renderer, - template, - context - ) { - Transform.call(this); - this.started = false; - this.renderer = renderer; - this.template = template; - this.context = context || {}; - this.inject = renderer.inject; - } - - if ( Transform ) TemplateStream.__proto__ = Transform; - TemplateStream.prototype = Object.create( Transform && Transform.prototype ); - TemplateStream.prototype.constructor = TemplateStream; - - TemplateStream.prototype._transform = function _transform (data, encoding, done) { - if (!this.started) { - this.emit('beforeStart'); - this.start(); - } - this.push(data); - done(); - }; - - TemplateStream.prototype.start = function 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 - var links = this.renderer.renderResourceHints(this.context); - if (links) { - this.push(links); - } - - // CSS files and inline server-rendered CSS collected by vue-style-loader - var styles = this.renderer.renderStyles(this.context); - if (styles) { - this.push(styles); - } - } - - this.push(this.template.neck(this.context)); - }; - - TemplateStream.prototype._flush = function _flush (done) { - this.emit('beforeEnd'); - - if (this.inject) { - // inline initial store state - var state = this.renderer.renderState(this.context); - if (state) { - this.push(state); - } - - // embed scripts needed - var scripts = this.renderer.renderScripts(this.context); - if (scripts) { - this.push(scripts); - } - } - - this.push(this.template.tail(this.context)); - done(); - }; - - return TemplateStream; -}(Transform)); - -/* */ - -var compile$1 = require('lodash.template'); -var compileOptions = { - escape: /{{([^{][\s\S]+?[^}])}}/g, - interpolate: /{{{([\s\S]+?)}}}/g -}; - - - -function parseTemplate ( - template, - contentPlaceholder -) { - if ( contentPlaceholder === void 0 ) contentPlaceholder = '<!--vue-ssr-outlet-->'; - - if (typeof template === 'object') { - return template - } - - var i = template.indexOf('</head>'); - var 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$1(template.slice(0, i), compileOptions), - neck: compile$1(template.slice(i, j), compileOptions), - tail: compile$1(template.slice(j + contentPlaceholder.length), compileOptions) - } -} - -/* */ - -/** - * 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. - */ - - - - - -function createMapper ( - clientManifest -) { - var map = createMap(clientManifest); - // map server-side moduleIds to client-side files - return function mapper (moduleIds) { - var res = new Set(); - for (var i = 0; i < moduleIds.length; i++) { - var mapped = map.get(moduleIds[i]); - if (mapped) { - for (var j = 0; j < mapped.length; j++) { - res.add(mapped[j]); - } - } - } - return Array.from(res) - } -} - -function createMap (clientManifest) { - var map = new Map(); - Object.keys(clientManifest.modules).forEach(function (id) { - map.set(id, mapIdToFile(id, clientManifest)); - }); - return map -} - -function mapIdToFile (id, clientManifest) { - var files = []; - var fileIndices = clientManifest.modules[id]; - if (fileIndices) { - fileIndices.forEach(function (index) { - var file = clientManifest.all[index]; - // only include async files or non-js assets - if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) { - files.push(file); - } - }); - } - return files -} - -/* */ - -var path = require('path'); -var serialize = require('serialize-javascript'); - -var TemplateRenderer = function TemplateRenderer (options) { - 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. - this.parsedTemplate = options.template - ? parseTemplate(options.template) - : null; - - // extra functionality with client manifest - if (options.clientManifest) { - var clientManifest = this.clientManifest = options.clientManifest; - this.publicPath = clientManifest.publicPath.replace(/\/$/, ''); - // preload/prefetch directives - this.preloadFiles = (clientManifest.initial || []).map(normalizeFile); - this.prefetchFiles = (clientManifest.async || []).map(normalizeFile); - // initial async chunk mapping - this.mapFiles = createMapper(clientManifest); - } -}; - -TemplateRenderer.prototype.bindRenderFns = function bindRenderFns (context) { - var renderer = this;['ResourceHints', 'State', 'Scripts', 'Styles'].forEach(function (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 -TemplateRenderer.prototype.renderSync = function renderSync (content, context) { - var template = this.parsedTemplate; - if (!template) { - throw new Error('renderSync cannot be called without a template.') - } - context = 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) - ) - } -}; - -TemplateRenderer.prototype.renderStyles = function renderStyles (context) { - var this$1 = this; - - var cssFiles = this.clientManifest - ? this.clientManifest.all.filter(isCSS) - : []; - return ( - // render links for css files - (cssFiles.length - ? cssFiles.map(function (file) { return ("<link rel=\"stylesheet\" href=\"" + (this$1.publicPath) + "/" + file + "\">"); }).join('') - : '') + - // context.styles is a getter exposed by vue-style-loader which contains - // the inline component styles collected during SSR - (context.styles || '') - ) -}; - -TemplateRenderer.prototype.renderResourceHints = function renderResourceHints (context) { - return this.renderPreloadLinks(context) + this.renderPrefetchLinks(context) -}; - -TemplateRenderer.prototype.getPreloadFiles = function getPreloadFiles (context) { - var usedAsyncFiles = this.getUsedAsyncFiles(context); - if (this.preloadFiles || usedAsyncFiles) { - return (this.preloadFiles || []).concat(usedAsyncFiles || []) - } else { - return [] - } -}; - -TemplateRenderer.prototype.renderPreloadLinks = function renderPreloadLinks (context) { - var this$1 = this; - - var files = this.getPreloadFiles(context); - var shouldPreload = this.options.shouldPreload; - if (files.length) { - return files.map(function (ref) { - var file = ref.file; - var extension = ref.extension; - var fileWithoutQuery = ref.fileWithoutQuery; - var asType = ref.asType; - - var 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$1.publicPath) + "/" + file + "\"" + (asType !== '' ? (" as=\"" + asType + "\"") : '') + extra + ">") - }).join('') - } else { - return '' - } -}; - -TemplateRenderer.prototype.renderPrefetchLinks = function renderPrefetchLinks (context) { - var this$1 = this; - - var shouldPrefetch = this.options.shouldPrefetch; - if (this.prefetchFiles) { - var usedAsyncFiles = this.getUsedAsyncFiles(context); - var alreadyRendered = function (file) { - return usedAsyncFiles && usedAsyncFiles.some(function (f) { return f.file === file; }) - }; - return this.prefetchFiles.map(function (ref) { - var file = ref.file; - var fileWithoutQuery = ref.fileWithoutQuery; - var asType = ref.asType; - - if (shouldPrefetch && !shouldPrefetch(fileWithoutQuery, asType)) { - return '' - } - if (alreadyRendered(file)) { - return '' - } - return ("<link rel=\"prefetch\" href=\"" + (this$1.publicPath) + "/" + file + "\">") - }).join('') - } else { - return '' - } -}; - -TemplateRenderer.prototype.renderState = function renderState (context, options) { - var ref = options || {}; - var contextKey = ref.contextKey; if ( contextKey === void 0 ) contextKey = 'state'; - var windowKey = ref.windowKey; if ( windowKey === void 0 ) windowKey = '__INITIAL_STATE__'; - var autoRemove = process.env.NODE_ENV === 'production' - ? ';(function(){var s;(s=document.currentScript||document.scripts[document.scripts.length-1]).parentNode.removeChild(s);}());' - : ''; - return context[contextKey] - ? ("<script>window." + windowKey + "=" + (serialize(context[contextKey], { isJSON: true })) + autoRemove + "</script>") - : '' -}; - -TemplateRenderer.prototype.renderScripts = function renderScripts (context) { - var this$1 = this; - - if (this.clientManifest) { - var initial = this.preloadFiles; - var async = this.getUsedAsyncFiles(context); - var needed = [initial[0]].concat(async || [], initial.slice(1)); - return needed.filter(function (ref) { - var file = ref.file; - - return isJS(file); - }).map(function (ref) { - var file = ref.file; - - return ("<script src=\"" + (this$1.publicPath) + "/" + file + "\" defer></script>") - }).join('') - } else { - return '' - } -}; - -TemplateRenderer.prototype.getUsedAsyncFiles = function getUsedAsyncFiles (context) { - if (!context._mappedFiles && context._registeredComponents && this.mapFiles) { - var registered = Array.from(context._registeredComponents); - context._mappedFiles = this.mapFiles(registered).map(normalizeFile); - } - return context._mappedFiles -}; - -// create a transform stream -TemplateRenderer.prototype.createStream = function createStream (context) { - if (!this.parsedTemplate) { - throw new Error('createStream cannot be called without a template.') - } - return new TemplateStream(this, this.parsedTemplate, context || {}) -}; - -function normalizeFile (file) { - var withoutQuery = file.replace(/\?.*/, ''); - var extension = path.extname(withoutQuery).slice(1); - return { - file: file, - extension: extension, - fileWithoutQuery: withoutQuery, - asType: getPreloadType(extension) - } -} - -function getPreloadType (ext) { - 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 '' - } -} - -/* */ - -function createRenderer$1 (ref) { - if ( ref === void 0 ) ref = {}; - var modules = ref.modules; if ( modules === void 0 ) modules = []; - var directives = ref.directives; if ( directives === void 0 ) directives = {}; - var isUnaryTag = ref.isUnaryTag; if ( isUnaryTag === void 0 ) isUnaryTag = (function () { return false; }); - var template = ref.template; - var inject = ref.inject; - var cache = ref.cache; - var shouldPreload = ref.shouldPreload; - var shouldPrefetch = ref.shouldPrefetch; - var clientManifest = ref.clientManifest; - - var render = createRenderFunction(modules, directives, isUnaryTag, cache); - var templateRenderer = new TemplateRenderer({ - template: template, - inject: inject, - shouldPreload: shouldPreload, - shouldPrefetch: shouldPrefetch, - clientManifest: clientManifest - }); - - return { - renderToString: function renderToString ( - component, - context, - cb - ) { - if (typeof context === 'function') { - cb = context; - context = {}; - } - if (context) { - templateRenderer.bindRenderFns(context); - } - - // no callback, return Promise - var promise; - if (!cb) { - var assign; - ((assign = createPromiseCallback(), promise = assign.promise, cb = assign.cb)); - } - - var result = ''; - var write = createWriteFunction(function (text) { - result += text; - return false - }, cb); - try { - render(component, write, context, function () { - if (template) { - result = templateRenderer.renderSync(result, context); - } - cb(null, result); - }); - } catch (e) { - cb(e); - } - - return promise - }, - - renderToStream: function renderToStream ( - component, - context - ) { - if (context) { - templateRenderer.bindRenderFns(context); - } - var renderStream = new RenderStream(function (write, done) { - render(component, write, context, done); - }); - if (!template) { - return renderStream - } else { - var templateStream = templateRenderer.createStream(context); - renderStream.on('error', function (err) { - templateStream.emit('error', err); - }); - renderStream.pipe(templateStream); - return templateStream - } - } - } -} - -var vm = require('vm'); -var path$2 = require('path'); -var resolve = require('resolve'); -var NativeModule = require('module'); - -function createSandbox (context) { - var sandbox = { - Buffer: Buffer, - console: console, - process: process, - setTimeout: setTimeout, - setInterval: setInterval, - setImmediate: setImmediate, - clearTimeout: clearTimeout, - clearInterval: clearInterval, - clearImmediate: clearImmediate, - __VUE_SSR_CONTEXT__: context - }; - sandbox.global = sandbox; - return sandbox -} - -function compileModule (files, basedir, runInNewContext) { - var compiledScripts = {}; - var resolvedModules = {}; - - function getCompiledScript (filename) { - if (compiledScripts[filename]) { - return compiledScripts[filename] - } - var code = files[filename]; - var wrapper = NativeModule.wrap(code); - var script = new vm.Script(wrapper, { - filename: filename, - displayErrors: true - }); - compiledScripts[filename] = script; - return script - } - - function evaluateModule (filename, sandbox, evaluatedFiles) { - if ( evaluatedFiles === void 0 ) evaluatedFiles = {}; - - if (evaluatedFiles[filename]) { - return evaluatedFiles[filename] - } - - var script = getCompiledScript(filename); - var compiledWrapper = runInNewContext === false - ? script.runInThisContext() - : script.runInNewContext(sandbox); - var m = { exports: {}}; - var r = function (file) { - file = path$2.join('.', file); - if (files[file]) { - return evaluateModule(file, sandbox, evaluatedFiles) - } else if (basedir) { - return require( - resolvedModules[file] || - (resolvedModules[file] = resolve.sync(file, { basedir: basedir })) - ) - } else { - return require(file) - } - }; - compiledWrapper.call(m.exports, m.exports, r, m); - - var res = Object.prototype.hasOwnProperty.call(m.exports, 'default') - ? m.exports.default - : m.exports; - evaluatedFiles[filename] = res; - return res - } - return evaluateModule -} - -function deepClone (val) { - if (isPlainObject(val)) { - var res = {}; - for (var key in val) { - res[key] = deepClone(val[key]); - } - return res - } else if (Array.isArray(val)) { - return val.slice() - } else { - return val - } -} - -function createBundleRunner (entry, files, basedir, runInNewContext) { - var 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 function (userContext) { - if ( userContext === void 0 ) userContext = {}; - - return new Promise(function (resolve) { - userContext._registeredComponents = new Set(); - var 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. - var runner; // lazy creation so that errors can be caught by user - var initialContext; - return function (userContext) { - if ( userContext === void 0 ) userContext = {}; - - return new Promise(function (resolve) { - if (!runner) { - var sandbox = runInNewContext === 'once' - ? createSandbox() - : global; - // the initial context is only used for collecting possible non-component - // styles injected by vue-style-loader. - initialContext = sandbox.__VUE_SSR_CONTEXT__ = {}; - runner = evaluate(entry, sandbox); - // On subsequent renders, __VUE_SSR_CONTEXT__ will not be available - // to prevent cross-request pollution. - delete sandbox.__VUE_SSR_CONTEXT__; - if (typeof runner !== 'function') { - throw new Error( - 'bundle export should be a function when using ' + - '{ runInNewContext: false }.' - ) - } - } - userContext._registeredComponents = new Set(); - - // vue-style-loader styles imported outside of component lifecycle hooks - if (initialContext._styles) { - 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 - var renderStyles = initialContext._renderStyles; - if (renderStyles) { - Object.defineProperty(userContext, 'styles', { - enumerable: true, - get: function get () { - return renderStyles(userContext._styles) - } - }); - } - } - - resolve(runner(userContext)); - }); - } - } -} - -/* */ - -var SourceMapConsumer = require('source-map').SourceMapConsumer; - -var filenameRE = /\(([^)]+\.js):(\d+):(\d+)\)$/; - -function createSourceMapConsumers (rawMaps) { - var maps = {}; - Object.keys(rawMaps).forEach(function (file) { - maps[file] = new SourceMapConsumer(rawMaps[file]); - }); - return maps -} - -function rewriteErrorTrace (e, mapConsumers) { - if (e && typeof e.stack === 'string') { - e.stack = e.stack.split('\n').map(function (line) { - return rewriteTraceLine(line, mapConsumers) - }).join('\n'); - } -} - -function rewriteTraceLine (trace, mapConsumers) { - var m = trace.match(filenameRE); - var map = m && mapConsumers[m[1]]; - if (m != null && map) { - var originalPosition = map.originalPositionFor({ - line: Number(m[2]), - column: Number(m[3]) - }); - if (originalPosition.source != null) { - var source = originalPosition.source; - var line = originalPosition.line; - var column = originalPosition.column; - var mappedPosition = "(" + (source.replace(/^webpack:\/\/\//, '')) + ":" + (String(line)) + ":" + (String(column)) + ")"; - return trace.replace(filenameRE, mappedPosition) - } else { - return trace - } - } else { - return trace - } -} - -/* */ - -var fs = require('fs'); -var path$1 = require('path'); -var PassThrough = require('stream').PassThrough; - -var INVALID_MSG = - 'Invalid server-rendering bundle format. Should be a string ' + - 'or a bundle Object of type:\n\n' + -"{\n entry: string;\n files: { [filename: string]: string; };\n maps: { [filename: string]: string; };\n}\n"; - -// The render bundle can either be a string (single bundled file) -// or a bundle manifest object generated by vue-ssr-webpack-plugin. - - -function createBundleRendererCreator ( - createRenderer -) { - return function createBundleRenderer ( - bundle, - rendererOptions - ) { - if ( rendererOptions === void 0 ) rendererOptions = {}; - - var files, entry, maps; - var basedir = rendererOptions.basedir; - - // load bundle if given filepath - if ( - typeof bundle === 'string' && - /\.js(on)?$/.test(bundle) && - path$1.isAbsolute(bundle) - ) { - if (fs.existsSync(bundle)) { - var isJSON = /\.json$/.test(bundle); - basedir = basedir || path$1.dirname(bundle); - bundle = fs.readFileSync(bundle, 'utf-8'); - if (isJSON) { - try { - bundle = JSON.parse(bundle); - } catch (e) { - 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) - } - - var renderer = createRenderer(rendererOptions); - - var run = createBundleRunner( - entry, - files, - basedir, - rendererOptions.runInNewContext - ); - - return { - renderToString: function (context, cb) { - if (typeof context === 'function') { - cb = context; - context = {}; - } - - var promise; - if (!cb) { - var assign; - ((assign = createPromiseCallback(), promise = assign.promise, cb = assign.cb)); - } - - run(context).catch(function (err) { - rewriteErrorTrace(err, maps); - cb(err); - }).then(function (app) { - if (app) { - renderer.renderToString(app, context, function (err, res) { - rewriteErrorTrace(err, maps); - cb(err, res); - }); - } - }); - - return promise - }, - - renderToStream: function (context) { - var res = new PassThrough(); - run(context).catch(function (err) { - rewriteErrorTrace(err, maps); - // avoid emitting synchronously before user can - // attach error listener - process.nextTick(function () { - res.emit('error', err); - }); - }).then(function (app) { - if (app) { - var renderStream = renderer.renderToStream(app, context); - - renderStream.on('error', function (err) { - rewriteErrorTrace(err, maps); - res.emit('error', err); - }); - - // relay HTMLStream special events - if (rendererOptions && rendererOptions.template) { - renderStream.on('beforeStart', function () { - res.emit('beforeStart'); - }); - renderStream.on('beforeEnd', function () { - res.emit('beforeEnd'); - }); - } - - renderStream.pipe(res); - } - }); - - return res - } - } - } -} - -/* */ - -process.env.VUE_ENV = 'server'; - -function createRenderer$$1 (options) { - if ( options === void 0 ) options = {}; - - return createRenderer$1(extend(extend({}, options), { - isUnaryTag: isUnaryTag, - canBeLeftOpenTag: canBeLeftOpenTag, - modules: modules, - // user can provide server-side implementations for custom directives - // when creating the renderer. - directives: extend(baseDirectives, options.directives) - })) -} - -var createBundleRenderer = createBundleRendererCreator(createRenderer$$1); - -exports.createRenderer = createRenderer$$1; -exports.createBundleRenderer = createBundleRenderer; diff --git a/packages/vue-server-renderer/client-plugin.d.ts b/packages/vue-server-renderer/client-plugin.d.ts deleted file mode 100644 index 005c0f619e9..00000000000 --- a/packages/vue-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/vue-server-renderer/client-plugin.js b/packages/vue-server-renderer/client-plugin.js deleted file mode 100644 index 9916bcfb5f0..00000000000 --- a/packages/vue-server-renderer/client-plugin.js +++ /dev/null @@ -1,86 +0,0 @@ -'use strict'; - -/* */ - -var isJS = function (file) { return /\.js(\?[^.]+)?$/.test(file); }; - -var ref = require('chalk'); -var red = ref.red; -var yellow = ref.yellow; - -var prefix = "[vue-server-renderer-webpack-plugin]"; -var warn = exports.warn = function (msg) { return console.error(red((prefix + " " + msg + "\n"))); }; -var tip = exports.tip = function (msg) { return console.log(yellow((prefix + " " + msg + "\n"))); }; - -var hash = require('hash-sum'); -var uniq = require('lodash.uniq'); -var VueSSRClientPlugin = function VueSSRClientPlugin (options) { - if ( options === void 0 ) options = {}; - - this.options = Object.assign({ - filename: 'vue-ssr-client-manifest.json' - }, options); -}; - -VueSSRClientPlugin.prototype.apply = function apply (compiler) { - var this$1 = this; - - compiler.plugin('emit', function (compilation, cb) { - var stats = compilation.getStats().toJson(); - - var allFiles = uniq(stats.assets - .map(function (a) { return a.name; })); - - var initialFiles = uniq(Object.keys(stats.entrypoints) - .map(function (name) { return stats.entrypoints[name].assets; }) - .reduce(function (assets, all) { return all.concat(assets); }, []) - .filter(isJS)); - - var asyncFiles = allFiles - .filter(isJS) - .filter(function (file) { return initialFiles.indexOf(file) < 0; }); - - var manifest = { - publicPath: stats.publicPath, - all: allFiles, - initial: initialFiles, - async: asyncFiles, - modules: { /* [identifier: string]: Array<index: number> */ } - }; - - var assetModules = stats.modules.filter(function (m) { return m.assets.length; }); - var fileToIndex = function (file) { return manifest.all.indexOf(file); }; - stats.modules.forEach(function (m) { - // ignore modules duplicated in multiple chunks - if (m.chunks.length === 1) { - var cid = m.chunks[0]; - var chunk = stats.chunks.find(function (c) { return c.id === cid; }); - if (!chunk || !chunk.files) { - return - } - var files = manifest.modules[hash(m.identifier)] = chunk.files.map(fileToIndex); - // find all asset modules associated with the same chunk - assetModules.forEach(function (m) { - if (m.chunks.some(function (id) { return id === cid; })) { - files.push.apply(files, m.assets.map(fileToIndex)); - } - }); - } - }); - - // const debug = (file, obj) => { - // require('fs').writeFileSync(__dirname + '/' + file, JSON.stringify(obj, null, 2)) - // } - // debug('stats.json', stats) - // debug('client-manifest.json', manifest) - - var json = JSON.stringify(manifest, null, 2); - compilation.assets[this$1.options.filename] = { - source: function () { return json; }, - size: function () { return json.length; } - }; - cb(); - }); -}; - -module.exports = VueSSRClientPlugin; diff --git a/packages/vue-server-renderer/index.js b/packages/vue-server-renderer/index.js deleted file mode 100644 index 4a8ddd87f5f..00000000000 --- a/packages/vue-server-renderer/index.js +++ /dev/null @@ -1,16 +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' - ) -} - -module.exports = require('./build') diff --git a/packages/vue-server-renderer/package.json b/packages/vue-server-renderer/package.json deleted file mode 100644 index 38309a24fd0..00000000000 --- a/packages/vue-server-renderer/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "vue-server-renderer", - "version": "2.5.3", - "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" - ], - "author": "Evan You", - "license": "MIT", - "bugs": { - "url": "https://github.com/vuejs/vue/issues" - }, - "dependencies": { - "chalk": "^1.1.3", - "hash-sum": "^1.0.2", - "he": "^1.1.0", - "lodash.template": "^4.4.0", - "lodash.uniq": "^4.5.0", - "resolve": "^1.2.0", - "serialize-javascript": "^1.3.0", - "source-map": "0.5.6" - }, - "devDependencies": { - "vue": "file:../.." - }, - "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer#readme" -} diff --git a/packages/vue-server-renderer/server-plugin.d.ts b/packages/vue-server-renderer/server-plugin.d.ts deleted file mode 100644 index 005c0f619e9..00000000000 --- a/packages/vue-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/vue-server-renderer/server-plugin.js b/packages/vue-server-renderer/server-plugin.js deleted file mode 100644 index f494c888aa8..00000000000 --- a/packages/vue-server-renderer/server-plugin.js +++ /dev/null @@ -1,99 +0,0 @@ -'use strict'; - -/* */ - -var isJS = function (file) { return /\.js(\?[^.]+)?$/.test(file); }; - -var ref = require('chalk'); -var red = ref.red; -var yellow = ref.yellow; - -var prefix = "[vue-server-renderer-webpack-plugin]"; -var warn = exports.warn = function (msg) { return console.error(red((prefix + " " + msg + "\n"))); }; -var tip = exports.tip = function (msg) { return console.log(yellow((prefix + " " + msg + "\n"))); }; - -var validate = function (compiler) { - if (compiler.options.target !== 'node') { - warn('webpack config `target` should be "node".'); - } - - if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') { - 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.' - ); - } -}; - -var VueSSRServerPlugin = function VueSSRServerPlugin (options) { - if ( options === void 0 ) options = {}; - - this.options = Object.assign({ - filename: 'vue-ssr-server-bundle.json' - }, options); -}; - -VueSSRServerPlugin.prototype.apply = function apply (compiler) { - var this$1 = this; - - validate(compiler); - - compiler.plugin('emit', function (compilation, cb) { - var stats = compilation.getStats().toJson(); - var entryName = Object.keys(stats.entrypoints)[0]; - var entryInfo = stats.entrypoints[entryName]; - - if (!entryInfo) { - // #5553 - return cb() - } - - var entryAssets = entryInfo.assets.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." - ) - } - - var entry = entryAssets[0]; - if (!entry || typeof entry !== 'string') { - throw new Error( - ("Entry \"" + entryName + "\" not found. Did you specify the correct entry option?") - ) - } - - var bundle = { - entry: entry, - files: {}, - maps: {} - }; - - stats.assets.forEach(function (asset) { - if (asset.name.match(/\.js$/)) { - bundle.files[asset.name] = compilation.assets[asset.name].source(); - } else if (asset.name.match(/\.js\.map$/)) { - bundle.maps[asset.name.replace(/\.map$/, '')] = JSON.parse(compilation.assets[asset.name].source()); - } - // do not emit anything else for server - delete compilation.assets[asset.name]; - }); - - var json = JSON.stringify(bundle, null, 2); - var filename = this$1.options.filename; - - compilation.assets[filename] = { - source: function () { return json; }, - size: function () { return json.length; } - }; - - cb(); - }); -}; - -module.exports = VueSSRServerPlugin; diff --git a/packages/vue-server-renderer/types/index.d.ts b/packages/vue-server-renderer/types/index.d.ts deleted file mode 100644 index e6c8d62dde9..00000000000 --- a/packages/vue-server-renderer/types/index.d.ts +++ /dev/null @@ -1,46 +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>; - - renderToStream(vm: Vue, context?: object): Readable; -} - -interface BundleRenderer { - renderToString(callback: RenderCallback): void; - renderToString(context: object, callback: RenderCallback): void; - - 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; - 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/vue-server-renderer/types/plugin.d.ts b/packages/vue-server-renderer/types/plugin.d.ts deleted file mode 100644 index 50b642290da..00000000000 --- a/packages/vue-server-renderer/types/plugin.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Plugin } from 'webpack'; - -interface WebpackPluginOptions { - filename?: string; -} - -export interface WebpackPlugin { - new (options?: WebpackPluginOptions): Plugin; -} diff --git a/packages/vue-server-renderer/types/tsconfig.json b/packages/vue-server-renderer/types/tsconfig.json deleted file mode 100644 index 084dd317ea1..00000000000 --- a/packages/vue-server-renderer/types/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "strict": true, - "noEmit": true - }, - "compileOnSave": false, - "include": [ - "**/*.ts" - ] -} diff --git a/packages/vue-template-compiler/README.md b/packages/vue-template-compiler/README.md deleted file mode 100644 index 41d9b58b858..00000000000 --- a/packages/vue-template-compiler/README.md +++ /dev/null @@ -1,112 +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. You will only need it if you are writing build tools with very specific needs. In most cases you should be using [`vue-loader`](https://github.com/vuejs/vue-loader) or [`vueify`](https://github.com/vuejs/vueify) instead, both of which use this package internally. - -## 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 - -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`.** - -The optional `options` object can contain the following: - -- `modules` - - 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#L38-L45) 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` - - Defaults to `true`. This means 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/en/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`. - -#### 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/vue-template-compiler/browser.js b/packages/vue-template-compiler/browser.js deleted file mode 100644 index 5a049cf353f..00000000000 --- a/packages/vue-template-compiler/browser.js +++ /dev/null @@ -1,5099 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (factory((global.VueTemplateCompiler = {}))); -}(this, (function (exports) { 'use strict'; - -var splitRE$1 = /\r?\n/g; -var emptyRE = /^\s*$/; -var needFixRE = /^(\r?\n)*[\t\s]/; - -var deIndent = function deindent (str) { - if (!needFixRE.test(str)) { - return str - } - var lines = str.split(splitRE$1); - var min = Infinity; - var type, cur, c; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (!emptyRE.test(line)) { - if (!type) { - c = line.charAt(0); - if (c === ' ' || c === '\t') { - type = c; - cur = count(line, type); - if (cur < min) { - min = cur; - } - } else { - return str - } - } else { - cur = count(line, type); - if (cur < min) { - min = cur; - } - } - } - } - return lines.map(function (line) { - return line.slice(min) - }).join('\n') -}; - -function count (line, type) { - var i = 0; - while (line.charAt(i) === type) { - i++; - } - return i -} - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining - - - - - - - - -/** - * Check if value is primitive - */ - - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - - - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ - - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ - - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ - - -/** - * Convert an Array-like object to a real Array. - */ - - -/** - * Mix properties into target object. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ - - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ - - - - -/** - * Ensure a function is called only once. - */ - -/* */ - -var 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) -var 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 -var 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' -); - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if ("development" !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href' - ? options.shouldDecodeNewlinesForHref - : options.shouldDecodeNewlines; - attrs[i] = { - name: args[1], - value: decodeAttr(value, shouldDecodeNewlines) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if ("development" !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -var splitRE = /\r?\n/g; -var replaceRE = /./g; -var isSpecialTag = makeMap('script,style,template', true); - - - -/** - * Parse a single-file component (*.vue) file into an SFC Descriptor Object. - */ -function parseComponent ( - content, - options - ) { - if ( options === void 0 ) options = {}; - - var sfc = { - template: null, - script: null, - styles: [], - customBlocks: [] - }; - var depth = 0; - var currentBlock = null; - - function start ( - tag, - attrs, - unary, - start, - end - ) { - if (depth === 0) { - currentBlock = { - type: tag, - content: '', - start: end, - attrs: attrs.reduce(function (cumulated, ref) { - var name = ref.name; - var value = ref.value; - - cumulated[name] = value || true; - return cumulated - }, Object.create(null)) - }; - if (isSpecialTag(tag)) { - checkAttrs(currentBlock, attrs); - if (tag === 'style') { - sfc.styles.push(currentBlock); - } else { - sfc[tag] = currentBlock; - } - } else { // custom blocks - sfc.customBlocks.push(currentBlock); - } - } - if (!unary) { - depth++; - } - } - - function checkAttrs (block, attrs) { - for (var i = 0; i < attrs.length; i++) { - var 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; - } - if (attr.name === 'src') { - block.src = attr.value; - } - } - } - - function end (tag, start, end) { - if (depth === 1 && currentBlock) { - currentBlock.end = start; - var text = deIndent(content.slice(currentBlock.start, currentBlock.end)); - // 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, pad) { - if (pad === 'space') { - return content.slice(0, block.start).replace(replaceRE, ' ') - } else { - var offset = content.slice(0, block.start).split(splitRE).length; - var padChar = block.type === 'script' && !block.lang - ? '//\n' - : '\n'; - return Array(offset).join(padChar) - } - } - - parseHTML(content, { - start: start, - end: end - }); - - return sfc -} - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ - - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - - -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools - - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: "development" !== 'production', - - /** - * Whether to enable devtools - */ - devtools: "development" !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -{ - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - - - - - -// 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. - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if ("development" !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ - - -/** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ -function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -{ - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - "development" !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && "development" !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -function assertObjectType (name, value, vm) { - 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. - */ - - -/** - * Resolve an asset. - * This function is used because child instances need access - * to assets defined in its ancestor chain. - */ - -/* */ - -/* */ - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -function flushCallbacks () { - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - channel.port1.onmessage = flushCallbacks; - -} else { - /* istanbul ignore next */ - -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - -} else { - // fallback to macro - -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ - -/* */ - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -/* */ - -/* */ - - - -var 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. -var 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 -); - -var isPreTag = function (tag) { return tag === 'pre'; }; - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - - - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - "development" !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - if ("development" !== 'production' && staticClass) { - var expression = parseText(staticClass, options.delimiters); - if (expression) { - 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">.' - ); - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass); - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } -} - -function genData (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -var klass = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var tmp = item.split(propertyDelimiter); - tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim()); - } - }); - return res -}); - -// normalize possible array / string values into Object - - -/** - * parent component style should be after child's - * so that parent component's style could override it - */ - -/* */ - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - if (staticStyle) { - /* istanbul ignore if */ - { - var expression = parseText(staticStyle, options.delimiters); - if (expression) { - 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.staticStyle = JSON.stringify(parseStyleText(staticStyle)); - } - - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:(" + (el.styleBinding) + "),"; - } - return data -} - -var style = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$1 -}; - -var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - - - - - -function createCommonjsModule(fn, module) { - return module = { exports: {} }, fn(module, module.exports), module.exports; -} - -var he = createCommonjsModule(function (module, exports) { -/*! https://mths.be/he v1.1.1 by @mathias | MIT license */ -(function(root) { - - // Detect free variables `exports`. - var freeExports = 'object' == 'object' && exports; - - // Detect free variable `module`. - var freeModule = 'object' == 'object' && module && - module.exports == freeExports && module; - - // Detect free variable `global`, from Node.js or Browserified code, - // and use it as `root`. - var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal; - if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { - root = freeGlobal; - } - - /*--------------------------------------------------------------------------*/ - - // All astral symbols. - var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; - // All ASCII symbols (not just printable ASCII) except those listed in the - // first column of the overrides table. - // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides - var regexAsciiWhitelist = /[\x01-\x7F]/g; - // All BMP symbols that are not ASCII newlines, printable ASCII symbols, or - // code points listed in the first column of the overrides table on - // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides. - var regexBmpWhitelist = /[\x01-\t\x0B\f\x0E-\x1F\x7F\x81\x8D\x8F\x90\x9D\xA0-\uFFFF]/g; - - var regexEncodeNonAscii = /<\u20D2|=\u20E5|>\u20D2|\u205F\u200A|\u219D\u0338|\u2202\u0338|\u2220\u20D2|\u2229\uFE00|\u222A\uFE00|\u223C\u20D2|\u223D\u0331|\u223E\u0333|\u2242\u0338|\u224B\u0338|\u224D\u20D2|\u224E\u0338|\u224F\u0338|\u2250\u0338|\u2261\u20E5|\u2264\u20D2|\u2265\u20D2|\u2266\u0338|\u2267\u0338|\u2268\uFE00|\u2269\uFE00|\u226A\u0338|\u226A\u20D2|\u226B\u0338|\u226B\u20D2|\u227F\u0338|\u2282\u20D2|\u2283\u20D2|\u228A\uFE00|\u228B\uFE00|\u228F\u0338|\u2290\u0338|\u2293\uFE00|\u2294\uFE00|\u22B4\u20D2|\u22B5\u20D2|\u22D8\u0338|\u22D9\u0338|\u22DA\uFE00|\u22DB\uFE00|\u22F5\u0338|\u22F9\u0338|\u2933\u0338|\u29CF\u0338|\u29D0\u0338|\u2A6D\u0338|\u2A70\u0338|\u2A7D\u0338|\u2A7E\u0338|\u2AA1\u0338|\u2AA2\u0338|\u2AAC\uFE00|\u2AAD\uFE00|\u2AAF\u0338|\u2AB0\u0338|\u2AC5\u0338|\u2AC6\u0338|\u2ACB\uFE00|\u2ACC\uFE00|\u2AFD\u20E5|[\xA0-\u0113\u0116-\u0122\u0124-\u012B\u012E-\u014D\u0150-\u017E\u0192\u01B5\u01F5\u0237\u02C6\u02C7\u02D8-\u02DD\u0311\u0391-\u03A1\u03A3-\u03A9\u03B1-\u03C9\u03D1\u03D2\u03D5\u03D6\u03DC\u03DD\u03F0\u03F1\u03F5\u03F6\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E\u045F\u2002-\u2005\u2007-\u2010\u2013-\u2016\u2018-\u201A\u201C-\u201E\u2020-\u2022\u2025\u2026\u2030-\u2035\u2039\u203A\u203E\u2041\u2043\u2044\u204F\u2057\u205F-\u2063\u20AC\u20DB\u20DC\u2102\u2105\u210A-\u2113\u2115-\u211E\u2122\u2124\u2127-\u2129\u212C\u212D\u212F-\u2131\u2133-\u2138\u2145-\u2148\u2153-\u215E\u2190-\u219B\u219D-\u21A7\u21A9-\u21AE\u21B0-\u21B3\u21B5-\u21B7\u21BA-\u21DB\u21DD\u21E4\u21E5\u21F5\u21FD-\u2205\u2207-\u2209\u220B\u220C\u220F-\u2214\u2216-\u2218\u221A\u221D-\u2238\u223A-\u2257\u2259\u225A\u225C\u225F-\u2262\u2264-\u228B\u228D-\u229B\u229D-\u22A5\u22A7-\u22B0\u22B2-\u22BB\u22BD-\u22DB\u22DE-\u22E3\u22E6-\u22F7\u22F9-\u22FE\u2305\u2306\u2308-\u2310\u2312\u2313\u2315\u2316\u231C-\u231F\u2322\u2323\u232D\u232E\u2336\u233D\u233F\u237C\u23B0\u23B1\u23B4-\u23B6\u23DC-\u23DF\u23E2\u23E7\u2423\u24C8\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2550-\u256C\u2580\u2584\u2588\u2591-\u2593\u25A1\u25AA\u25AB\u25AD\u25AE\u25B1\u25B3-\u25B5\u25B8\u25B9\u25BD-\u25BF\u25C2\u25C3\u25CA\u25CB\u25EC\u25EF\u25F8-\u25FC\u2605\u2606\u260E\u2640\u2642\u2660\u2663\u2665\u2666\u266A\u266D-\u266F\u2713\u2717\u2720\u2736\u2758\u2772\u2773\u27C8\u27C9\u27E6-\u27ED\u27F5-\u27FA\u27FC\u27FF\u2902-\u2905\u290C-\u2913\u2916\u2919-\u2920\u2923-\u292A\u2933\u2935-\u2939\u293C\u293D\u2945\u2948-\u294B\u294E-\u2976\u2978\u2979\u297B-\u297F\u2985\u2986\u298B-\u2996\u299A\u299C\u299D\u29A4-\u29B7\u29B9\u29BB\u29BC\u29BE-\u29C5\u29C9\u29CD-\u29D0\u29DC-\u29DE\u29E3-\u29E5\u29EB\u29F4\u29F6\u2A00-\u2A02\u2A04\u2A06\u2A0C\u2A0D\u2A10-\u2A17\u2A22-\u2A27\u2A29\u2A2A\u2A2D-\u2A31\u2A33-\u2A3C\u2A3F\u2A40\u2A42-\u2A4D\u2A50\u2A53-\u2A58\u2A5A-\u2A5D\u2A5F\u2A66\u2A6A\u2A6D-\u2A75\u2A77-\u2A9A\u2A9D-\u2AA2\u2AA4-\u2AB0\u2AB3-\u2AC8\u2ACB\u2ACC\u2ACF-\u2ADB\u2AE4\u2AE6-\u2AE9\u2AEB-\u2AF3\u2AFD\uFB00-\uFB04]|\uD835[\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDCCF\uDD04\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDD6B]/g; - var encodeMap = {'\xAD':'shy','\u200C':'zwnj','\u200D':'zwj','\u200E':'lrm','\u2063':'ic','\u2062':'it','\u2061':'af','\u200F':'rlm','\u200B':'ZeroWidthSpace','\u2060':'NoBreak','\u0311':'DownBreve','\u20DB':'tdot','\u20DC':'DotDot','\t':'Tab','\n':'NewLine','\u2008':'puncsp','\u205F':'MediumSpace','\u2009':'thinsp','\u200A':'hairsp','\u2004':'emsp13','\u2002':'ensp','\u2005':'emsp14','\u2003':'emsp','\u2007':'numsp','\xA0':'nbsp','\u205F\u200A':'ThickSpace','\u203E':'oline','_':'lowbar','\u2010':'dash','\u2013':'ndash','\u2014':'mdash','\u2015':'horbar',',':'comma',';':'semi','\u204F':'bsemi',':':'colon','\u2A74':'Colone','!':'excl','\xA1':'iexcl','?':'quest','\xBF':'iquest','.':'period','\u2025':'nldr','\u2026':'mldr','\xB7':'middot','\'':'apos','\u2018':'lsquo','\u2019':'rsquo','\u201A':'sbquo','\u2039':'lsaquo','\u203A':'rsaquo','"':'quot','\u201C':'ldquo','\u201D':'rdquo','\u201E':'bdquo','\xAB':'laquo','\xBB':'raquo','(':'lpar',')':'rpar','[':'lsqb',']':'rsqb','{':'lcub','}':'rcub','\u2308':'lceil','\u2309':'rceil','\u230A':'lfloor','\u230B':'rfloor','\u2985':'lopar','\u2986':'ropar','\u298B':'lbrke','\u298C':'rbrke','\u298D':'lbrkslu','\u298E':'rbrksld','\u298F':'lbrksld','\u2990':'rbrkslu','\u2991':'langd','\u2992':'rangd','\u2993':'lparlt','\u2994':'rpargt','\u2995':'gtlPar','\u2996':'ltrPar','\u27E6':'lobrk','\u27E7':'robrk','\u27E8':'lang','\u27E9':'rang','\u27EA':'Lang','\u27EB':'Rang','\u27EC':'loang','\u27ED':'roang','\u2772':'lbbrk','\u2773':'rbbrk','\u2016':'Vert','\xA7':'sect','\xB6':'para','@':'commat','*':'ast','/':'sol','undefined':null,'&':'amp','#':'num','%':'percnt','\u2030':'permil','\u2031':'pertenk','\u2020':'dagger','\u2021':'Dagger','\u2022':'bull','\u2043':'hybull','\u2032':'prime','\u2033':'Prime','\u2034':'tprime','\u2057':'qprime','\u2035':'bprime','\u2041':'caret','`':'grave','\xB4':'acute','\u02DC':'tilde','^':'Hat','\xAF':'macr','\u02D8':'breve','\u02D9':'dot','\xA8':'die','\u02DA':'ring','\u02DD':'dblac','\xB8':'cedil','\u02DB':'ogon','\u02C6':'circ','\u02C7':'caron','\xB0':'deg','\xA9':'copy','\xAE':'reg','\u2117':'copysr','\u2118':'wp','\u211E':'rx','\u2127':'mho','\u2129':'iiota','\u2190':'larr','\u219A':'nlarr','\u2192':'rarr','\u219B':'nrarr','\u2191':'uarr','\u2193':'darr','\u2194':'harr','\u21AE':'nharr','\u2195':'varr','\u2196':'nwarr','\u2197':'nearr','\u2198':'searr','\u2199':'swarr','\u219D':'rarrw','\u219D\u0338':'nrarrw','\u219E':'Larr','\u219F':'Uarr','\u21A0':'Rarr','\u21A1':'Darr','\u21A2':'larrtl','\u21A3':'rarrtl','\u21A4':'mapstoleft','\u21A5':'mapstoup','\u21A6':'map','\u21A7':'mapstodown','\u21A9':'larrhk','\u21AA':'rarrhk','\u21AB':'larrlp','\u21AC':'rarrlp','\u21AD':'harrw','\u21B0':'lsh','\u21B1':'rsh','\u21B2':'ldsh','\u21B3':'rdsh','\u21B5':'crarr','\u21B6':'cularr','\u21B7':'curarr','\u21BA':'olarr','\u21BB':'orarr','\u21BC':'lharu','\u21BD':'lhard','\u21BE':'uharr','\u21BF':'uharl','\u21C0':'rharu','\u21C1':'rhard','\u21C2':'dharr','\u21C3':'dharl','\u21C4':'rlarr','\u21C5':'udarr','\u21C6':'lrarr','\u21C7':'llarr','\u21C8':'uuarr','\u21C9':'rrarr','\u21CA':'ddarr','\u21CB':'lrhar','\u21CC':'rlhar','\u21D0':'lArr','\u21CD':'nlArr','\u21D1':'uArr','\u21D2':'rArr','\u21CF':'nrArr','\u21D3':'dArr','\u21D4':'iff','\u21CE':'nhArr','\u21D5':'vArr','\u21D6':'nwArr','\u21D7':'neArr','\u21D8':'seArr','\u21D9':'swArr','\u21DA':'lAarr','\u21DB':'rAarr','\u21DD':'zigrarr','\u21E4':'larrb','\u21E5':'rarrb','\u21F5':'duarr','\u21FD':'loarr','\u21FE':'roarr','\u21FF':'hoarr','\u2200':'forall','\u2201':'comp','\u2202':'part','\u2202\u0338':'npart','\u2203':'exist','\u2204':'nexist','\u2205':'empty','\u2207':'Del','\u2208':'in','\u2209':'notin','\u220B':'ni','\u220C':'notni','\u03F6':'bepsi','\u220F':'prod','\u2210':'coprod','\u2211':'sum','+':'plus','\xB1':'pm','\xF7':'div','\xD7':'times','<':'lt','\u226E':'nlt','<\u20D2':'nvlt','=':'equals','\u2260':'ne','=\u20E5':'bne','\u2A75':'Equal','>':'gt','\u226F':'ngt','>\u20D2':'nvgt','\xAC':'not','|':'vert','\xA6':'brvbar','\u2212':'minus','\u2213':'mp','\u2214':'plusdo','\u2044':'frasl','\u2216':'setmn','\u2217':'lowast','\u2218':'compfn','\u221A':'Sqrt','\u221D':'prop','\u221E':'infin','\u221F':'angrt','\u2220':'ang','\u2220\u20D2':'nang','\u2221':'angmsd','\u2222':'angsph','\u2223':'mid','\u2224':'nmid','\u2225':'par','\u2226':'npar','\u2227':'and','\u2228':'or','\u2229':'cap','\u2229\uFE00':'caps','\u222A':'cup','\u222A\uFE00':'cups','\u222B':'int','\u222C':'Int','\u222D':'tint','\u2A0C':'qint','\u222E':'oint','\u222F':'Conint','\u2230':'Cconint','\u2231':'cwint','\u2232':'cwconint','\u2233':'awconint','\u2234':'there4','\u2235':'becaus','\u2236':'ratio','\u2237':'Colon','\u2238':'minusd','\u223A':'mDDot','\u223B':'homtht','\u223C':'sim','\u2241':'nsim','\u223C\u20D2':'nvsim','\u223D':'bsim','\u223D\u0331':'race','\u223E':'ac','\u223E\u0333':'acE','\u223F':'acd','\u2240':'wr','\u2242':'esim','\u2242\u0338':'nesim','\u2243':'sime','\u2244':'nsime','\u2245':'cong','\u2247':'ncong','\u2246':'simne','\u2248':'ap','\u2249':'nap','\u224A':'ape','\u224B':'apid','\u224B\u0338':'napid','\u224C':'bcong','\u224D':'CupCap','\u226D':'NotCupCap','\u224D\u20D2':'nvap','\u224E':'bump','\u224E\u0338':'nbump','\u224F':'bumpe','\u224F\u0338':'nbumpe','\u2250':'doteq','\u2250\u0338':'nedot','\u2251':'eDot','\u2252':'efDot','\u2253':'erDot','\u2254':'colone','\u2255':'ecolon','\u2256':'ecir','\u2257':'cire','\u2259':'wedgeq','\u225A':'veeeq','\u225C':'trie','\u225F':'equest','\u2261':'equiv','\u2262':'nequiv','\u2261\u20E5':'bnequiv','\u2264':'le','\u2270':'nle','\u2264\u20D2':'nvle','\u2265':'ge','\u2271':'nge','\u2265\u20D2':'nvge','\u2266':'lE','\u2266\u0338':'nlE','\u2267':'gE','\u2267\u0338':'ngE','\u2268\uFE00':'lvnE','\u2268':'lnE','\u2269':'gnE','\u2269\uFE00':'gvnE','\u226A':'ll','\u226A\u0338':'nLtv','\u226A\u20D2':'nLt','\u226B':'gg','\u226B\u0338':'nGtv','\u226B\u20D2':'nGt','\u226C':'twixt','\u2272':'lsim','\u2274':'nlsim','\u2273':'gsim','\u2275':'ngsim','\u2276':'lg','\u2278':'ntlg','\u2277':'gl','\u2279':'ntgl','\u227A':'pr','\u2280':'npr','\u227B':'sc','\u2281':'nsc','\u227C':'prcue','\u22E0':'nprcue','\u227D':'sccue','\u22E1':'nsccue','\u227E':'prsim','\u227F':'scsim','\u227F\u0338':'NotSucceedsTilde','\u2282':'sub','\u2284':'nsub','\u2282\u20D2':'vnsub','\u2283':'sup','\u2285':'nsup','\u2283\u20D2':'vnsup','\u2286':'sube','\u2288':'nsube','\u2287':'supe','\u2289':'nsupe','\u228A\uFE00':'vsubne','\u228A':'subne','\u228B\uFE00':'vsupne','\u228B':'supne','\u228D':'cupdot','\u228E':'uplus','\u228F':'sqsub','\u228F\u0338':'NotSquareSubset','\u2290':'sqsup','\u2290\u0338':'NotSquareSuperset','\u2291':'sqsube','\u22E2':'nsqsube','\u2292':'sqsupe','\u22E3':'nsqsupe','\u2293':'sqcap','\u2293\uFE00':'sqcaps','\u2294':'sqcup','\u2294\uFE00':'sqcups','\u2295':'oplus','\u2296':'ominus','\u2297':'otimes','\u2298':'osol','\u2299':'odot','\u229A':'ocir','\u229B':'oast','\u229D':'odash','\u229E':'plusb','\u229F':'minusb','\u22A0':'timesb','\u22A1':'sdotb','\u22A2':'vdash','\u22AC':'nvdash','\u22A3':'dashv','\u22A4':'top','\u22A5':'bot','\u22A7':'models','\u22A8':'vDash','\u22AD':'nvDash','\u22A9':'Vdash','\u22AE':'nVdash','\u22AA':'Vvdash','\u22AB':'VDash','\u22AF':'nVDash','\u22B0':'prurel','\u22B2':'vltri','\u22EA':'nltri','\u22B3':'vrtri','\u22EB':'nrtri','\u22B4':'ltrie','\u22EC':'nltrie','\u22B4\u20D2':'nvltrie','\u22B5':'rtrie','\u22ED':'nrtrie','\u22B5\u20D2':'nvrtrie','\u22B6':'origof','\u22B7':'imof','\u22B8':'mumap','\u22B9':'hercon','\u22BA':'intcal','\u22BB':'veebar','\u22BD':'barvee','\u22BE':'angrtvb','\u22BF':'lrtri','\u22C0':'Wedge','\u22C1':'Vee','\u22C2':'xcap','\u22C3':'xcup','\u22C4':'diam','\u22C5':'sdot','\u22C6':'Star','\u22C7':'divonx','\u22C8':'bowtie','\u22C9':'ltimes','\u22CA':'rtimes','\u22CB':'lthree','\u22CC':'rthree','\u22CD':'bsime','\u22CE':'cuvee','\u22CF':'cuwed','\u22D0':'Sub','\u22D1':'Sup','\u22D2':'Cap','\u22D3':'Cup','\u22D4':'fork','\u22D5':'epar','\u22D6':'ltdot','\u22D7':'gtdot','\u22D8':'Ll','\u22D8\u0338':'nLl','\u22D9':'Gg','\u22D9\u0338':'nGg','\u22DA\uFE00':'lesg','\u22DA':'leg','\u22DB':'gel','\u22DB\uFE00':'gesl','\u22DE':'cuepr','\u22DF':'cuesc','\u22E6':'lnsim','\u22E7':'gnsim','\u22E8':'prnsim','\u22E9':'scnsim','\u22EE':'vellip','\u22EF':'ctdot','\u22F0':'utdot','\u22F1':'dtdot','\u22F2':'disin','\u22F3':'isinsv','\u22F4':'isins','\u22F5':'isindot','\u22F5\u0338':'notindot','\u22F6':'notinvc','\u22F7':'notinvb','\u22F9':'isinE','\u22F9\u0338':'notinE','\u22FA':'nisd','\u22FB':'xnis','\u22FC':'nis','\u22FD':'notnivc','\u22FE':'notnivb','\u2305':'barwed','\u2306':'Barwed','\u230C':'drcrop','\u230D':'dlcrop','\u230E':'urcrop','\u230F':'ulcrop','\u2310':'bnot','\u2312':'profline','\u2313':'profsurf','\u2315':'telrec','\u2316':'target','\u231C':'ulcorn','\u231D':'urcorn','\u231E':'dlcorn','\u231F':'drcorn','\u2322':'frown','\u2323':'smile','\u232D':'cylcty','\u232E':'profalar','\u2336':'topbot','\u233D':'ovbar','\u233F':'solbar','\u237C':'angzarr','\u23B0':'lmoust','\u23B1':'rmoust','\u23B4':'tbrk','\u23B5':'bbrk','\u23B6':'bbrktbrk','\u23DC':'OverParenthesis','\u23DD':'UnderParenthesis','\u23DE':'OverBrace','\u23DF':'UnderBrace','\u23E2':'trpezium','\u23E7':'elinters','\u2423':'blank','\u2500':'boxh','\u2502':'boxv','\u250C':'boxdr','\u2510':'boxdl','\u2514':'boxur','\u2518':'boxul','\u251C':'boxvr','\u2524':'boxvl','\u252C':'boxhd','\u2534':'boxhu','\u253C':'boxvh','\u2550':'boxH','\u2551':'boxV','\u2552':'boxdR','\u2553':'boxDr','\u2554':'boxDR','\u2555':'boxdL','\u2556':'boxDl','\u2557':'boxDL','\u2558':'boxuR','\u2559':'boxUr','\u255A':'boxUR','\u255B':'boxuL','\u255C':'boxUl','\u255D':'boxUL','\u255E':'boxvR','\u255F':'boxVr','\u2560':'boxVR','\u2561':'boxvL','\u2562':'boxVl','\u2563':'boxVL','\u2564':'boxHd','\u2565':'boxhD','\u2566':'boxHD','\u2567':'boxHu','\u2568':'boxhU','\u2569':'boxHU','\u256A':'boxvH','\u256B':'boxVh','\u256C':'boxVH','\u2580':'uhblk','\u2584':'lhblk','\u2588':'block','\u2591':'blk14','\u2592':'blk12','\u2593':'blk34','\u25A1':'squ','\u25AA':'squf','\u25AB':'EmptyVerySmallSquare','\u25AD':'rect','\u25AE':'marker','\u25B1':'fltns','\u25B3':'xutri','\u25B4':'utrif','\u25B5':'utri','\u25B8':'rtrif','\u25B9':'rtri','\u25BD':'xdtri','\u25BE':'dtrif','\u25BF':'dtri','\u25C2':'ltrif','\u25C3':'ltri','\u25CA':'loz','\u25CB':'cir','\u25EC':'tridot','\u25EF':'xcirc','\u25F8':'ultri','\u25F9':'urtri','\u25FA':'lltri','\u25FB':'EmptySmallSquare','\u25FC':'FilledSmallSquare','\u2605':'starf','\u2606':'star','\u260E':'phone','\u2640':'female','\u2642':'male','\u2660':'spades','\u2663':'clubs','\u2665':'hearts','\u2666':'diams','\u266A':'sung','\u2713':'check','\u2717':'cross','\u2720':'malt','\u2736':'sext','\u2758':'VerticalSeparator','\u27C8':'bsolhsub','\u27C9':'suphsol','\u27F5':'xlarr','\u27F6':'xrarr','\u27F7':'xharr','\u27F8':'xlArr','\u27F9':'xrArr','\u27FA':'xhArr','\u27FC':'xmap','\u27FF':'dzigrarr','\u2902':'nvlArr','\u2903':'nvrArr','\u2904':'nvHarr','\u2905':'Map','\u290C':'lbarr','\u290D':'rbarr','\u290E':'lBarr','\u290F':'rBarr','\u2910':'RBarr','\u2911':'DDotrahd','\u2912':'UpArrowBar','\u2913':'DownArrowBar','\u2916':'Rarrtl','\u2919':'latail','\u291A':'ratail','\u291B':'lAtail','\u291C':'rAtail','\u291D':'larrfs','\u291E':'rarrfs','\u291F':'larrbfs','\u2920':'rarrbfs','\u2923':'nwarhk','\u2924':'nearhk','\u2925':'searhk','\u2926':'swarhk','\u2927':'nwnear','\u2928':'toea','\u2929':'tosa','\u292A':'swnwar','\u2933':'rarrc','\u2933\u0338':'nrarrc','\u2935':'cudarrr','\u2936':'ldca','\u2937':'rdca','\u2938':'cudarrl','\u2939':'larrpl','\u293C':'curarrm','\u293D':'cularrp','\u2945':'rarrpl','\u2948':'harrcir','\u2949':'Uarrocir','\u294A':'lurdshar','\u294B':'ldrushar','\u294E':'LeftRightVector','\u294F':'RightUpDownVector','\u2950':'DownLeftRightVector','\u2951':'LeftUpDownVector','\u2952':'LeftVectorBar','\u2953':'RightVectorBar','\u2954':'RightUpVectorBar','\u2955':'RightDownVectorBar','\u2956':'DownLeftVectorBar','\u2957':'DownRightVectorBar','\u2958':'LeftUpVectorBar','\u2959':'LeftDownVectorBar','\u295A':'LeftTeeVector','\u295B':'RightTeeVector','\u295C':'RightUpTeeVector','\u295D':'RightDownTeeVector','\u295E':'DownLeftTeeVector','\u295F':'DownRightTeeVector','\u2960':'LeftUpTeeVector','\u2961':'LeftDownTeeVector','\u2962':'lHar','\u2963':'uHar','\u2964':'rHar','\u2965':'dHar','\u2966':'luruhar','\u2967':'ldrdhar','\u2968':'ruluhar','\u2969':'rdldhar','\u296A':'lharul','\u296B':'llhard','\u296C':'rharul','\u296D':'lrhard','\u296E':'udhar','\u296F':'duhar','\u2970':'RoundImplies','\u2971':'erarr','\u2972':'simrarr','\u2973':'larrsim','\u2974':'rarrsim','\u2975':'rarrap','\u2976':'ltlarr','\u2978':'gtrarr','\u2979':'subrarr','\u297B':'suplarr','\u297C':'lfisht','\u297D':'rfisht','\u297E':'ufisht','\u297F':'dfisht','\u299A':'vzigzag','\u299C':'vangrt','\u299D':'angrtvbd','\u29A4':'ange','\u29A5':'range','\u29A6':'dwangle','\u29A7':'uwangle','\u29A8':'angmsdaa','\u29A9':'angmsdab','\u29AA':'angmsdac','\u29AB':'angmsdad','\u29AC':'angmsdae','\u29AD':'angmsdaf','\u29AE':'angmsdag','\u29AF':'angmsdah','\u29B0':'bemptyv','\u29B1':'demptyv','\u29B2':'cemptyv','\u29B3':'raemptyv','\u29B4':'laemptyv','\u29B5':'ohbar','\u29B6':'omid','\u29B7':'opar','\u29B9':'operp','\u29BB':'olcross','\u29BC':'odsold','\u29BE':'olcir','\u29BF':'ofcir','\u29C0':'olt','\u29C1':'ogt','\u29C2':'cirscir','\u29C3':'cirE','\u29C4':'solb','\u29C5':'bsolb','\u29C9':'boxbox','\u29CD':'trisb','\u29CE':'rtriltri','\u29CF':'LeftTriangleBar','\u29CF\u0338':'NotLeftTriangleBar','\u29D0':'RightTriangleBar','\u29D0\u0338':'NotRightTriangleBar','\u29DC':'iinfin','\u29DD':'infintie','\u29DE':'nvinfin','\u29E3':'eparsl','\u29E4':'smeparsl','\u29E5':'eqvparsl','\u29EB':'lozf','\u29F4':'RuleDelayed','\u29F6':'dsol','\u2A00':'xodot','\u2A01':'xoplus','\u2A02':'xotime','\u2A04':'xuplus','\u2A06':'xsqcup','\u2A0D':'fpartint','\u2A10':'cirfnint','\u2A11':'awint','\u2A12':'rppolint','\u2A13':'scpolint','\u2A14':'npolint','\u2A15':'pointint','\u2A16':'quatint','\u2A17':'intlarhk','\u2A22':'pluscir','\u2A23':'plusacir','\u2A24':'simplus','\u2A25':'plusdu','\u2A26':'plussim','\u2A27':'plustwo','\u2A29':'mcomma','\u2A2A':'minusdu','\u2A2D':'loplus','\u2A2E':'roplus','\u2A2F':'Cross','\u2A30':'timesd','\u2A31':'timesbar','\u2A33':'smashp','\u2A34':'lotimes','\u2A35':'rotimes','\u2A36':'otimesas','\u2A37':'Otimes','\u2A38':'odiv','\u2A39':'triplus','\u2A3A':'triminus','\u2A3B':'tritime','\u2A3C':'iprod','\u2A3F':'amalg','\u2A40':'capdot','\u2A42':'ncup','\u2A43':'ncap','\u2A44':'capand','\u2A45':'cupor','\u2A46':'cupcap','\u2A47':'capcup','\u2A48':'cupbrcap','\u2A49':'capbrcup','\u2A4A':'cupcup','\u2A4B':'capcap','\u2A4C':'ccups','\u2A4D':'ccaps','\u2A50':'ccupssm','\u2A53':'And','\u2A54':'Or','\u2A55':'andand','\u2A56':'oror','\u2A57':'orslope','\u2A58':'andslope','\u2A5A':'andv','\u2A5B':'orv','\u2A5C':'andd','\u2A5D':'ord','\u2A5F':'wedbar','\u2A66':'sdote','\u2A6A':'simdot','\u2A6D':'congdot','\u2A6D\u0338':'ncongdot','\u2A6E':'easter','\u2A6F':'apacir','\u2A70':'apE','\u2A70\u0338':'napE','\u2A71':'eplus','\u2A72':'pluse','\u2A73':'Esim','\u2A77':'eDDot','\u2A78':'equivDD','\u2A79':'ltcir','\u2A7A':'gtcir','\u2A7B':'ltquest','\u2A7C':'gtquest','\u2A7D':'les','\u2A7D\u0338':'nles','\u2A7E':'ges','\u2A7E\u0338':'nges','\u2A7F':'lesdot','\u2A80':'gesdot','\u2A81':'lesdoto','\u2A82':'gesdoto','\u2A83':'lesdotor','\u2A84':'gesdotol','\u2A85':'lap','\u2A86':'gap','\u2A87':'lne','\u2A88':'gne','\u2A89':'lnap','\u2A8A':'gnap','\u2A8B':'lEg','\u2A8C':'gEl','\u2A8D':'lsime','\u2A8E':'gsime','\u2A8F':'lsimg','\u2A90':'gsiml','\u2A91':'lgE','\u2A92':'glE','\u2A93':'lesges','\u2A94':'gesles','\u2A95':'els','\u2A96':'egs','\u2A97':'elsdot','\u2A98':'egsdot','\u2A99':'el','\u2A9A':'eg','\u2A9D':'siml','\u2A9E':'simg','\u2A9F':'simlE','\u2AA0':'simgE','\u2AA1':'LessLess','\u2AA1\u0338':'NotNestedLessLess','\u2AA2':'GreaterGreater','\u2AA2\u0338':'NotNestedGreaterGreater','\u2AA4':'glj','\u2AA5':'gla','\u2AA6':'ltcc','\u2AA7':'gtcc','\u2AA8':'lescc','\u2AA9':'gescc','\u2AAA':'smt','\u2AAB':'lat','\u2AAC':'smte','\u2AAC\uFE00':'smtes','\u2AAD':'late','\u2AAD\uFE00':'lates','\u2AAE':'bumpE','\u2AAF':'pre','\u2AAF\u0338':'npre','\u2AB0':'sce','\u2AB0\u0338':'nsce','\u2AB3':'prE','\u2AB4':'scE','\u2AB5':'prnE','\u2AB6':'scnE','\u2AB7':'prap','\u2AB8':'scap','\u2AB9':'prnap','\u2ABA':'scnap','\u2ABB':'Pr','\u2ABC':'Sc','\u2ABD':'subdot','\u2ABE':'supdot','\u2ABF':'subplus','\u2AC0':'supplus','\u2AC1':'submult','\u2AC2':'supmult','\u2AC3':'subedot','\u2AC4':'supedot','\u2AC5':'subE','\u2AC5\u0338':'nsubE','\u2AC6':'supE','\u2AC6\u0338':'nsupE','\u2AC7':'subsim','\u2AC8':'supsim','\u2ACB\uFE00':'vsubnE','\u2ACB':'subnE','\u2ACC\uFE00':'vsupnE','\u2ACC':'supnE','\u2ACF':'csub','\u2AD0':'csup','\u2AD1':'csube','\u2AD2':'csupe','\u2AD3':'subsup','\u2AD4':'supsub','\u2AD5':'subsub','\u2AD6':'supsup','\u2AD7':'suphsub','\u2AD8':'supdsub','\u2AD9':'forkv','\u2ADA':'topfork','\u2ADB':'mlcp','\u2AE4':'Dashv','\u2AE6':'Vdashl','\u2AE7':'Barv','\u2AE8':'vBar','\u2AE9':'vBarv','\u2AEB':'Vbar','\u2AEC':'Not','\u2AED':'bNot','\u2AEE':'rnmid','\u2AEF':'cirmid','\u2AF0':'midcir','\u2AF1':'topcir','\u2AF2':'nhpar','\u2AF3':'parsim','\u2AFD':'parsl','\u2AFD\u20E5':'nparsl','\u266D':'flat','\u266E':'natur','\u266F':'sharp','\xA4':'curren','\xA2':'cent','$':'dollar','\xA3':'pound','\xA5':'yen','\u20AC':'euro','\xB9':'sup1','\xBD':'half','\u2153':'frac13','\xBC':'frac14','\u2155':'frac15','\u2159':'frac16','\u215B':'frac18','\xB2':'sup2','\u2154':'frac23','\u2156':'frac25','\xB3':'sup3','\xBE':'frac34','\u2157':'frac35','\u215C':'frac38','\u2158':'frac45','\u215A':'frac56','\u215D':'frac58','\u215E':'frac78','\uD835\uDCB6':'ascr','\uD835\uDD52':'aopf','\uD835\uDD1E':'afr','\uD835\uDD38':'Aopf','\uD835\uDD04':'Afr','\uD835\uDC9C':'Ascr','\xAA':'ordf','\xE1':'aacute','\xC1':'Aacute','\xE0':'agrave','\xC0':'Agrave','\u0103':'abreve','\u0102':'Abreve','\xE2':'acirc','\xC2':'Acirc','\xE5':'aring','\xC5':'angst','\xE4':'auml','\xC4':'Auml','\xE3':'atilde','\xC3':'Atilde','\u0105':'aogon','\u0104':'Aogon','\u0101':'amacr','\u0100':'Amacr','\xE6':'aelig','\xC6':'AElig','\uD835\uDCB7':'bscr','\uD835\uDD53':'bopf','\uD835\uDD1F':'bfr','\uD835\uDD39':'Bopf','\u212C':'Bscr','\uD835\uDD05':'Bfr','\uD835\uDD20':'cfr','\uD835\uDCB8':'cscr','\uD835\uDD54':'copf','\u212D':'Cfr','\uD835\uDC9E':'Cscr','\u2102':'Copf','\u0107':'cacute','\u0106':'Cacute','\u0109':'ccirc','\u0108':'Ccirc','\u010D':'ccaron','\u010C':'Ccaron','\u010B':'cdot','\u010A':'Cdot','\xE7':'ccedil','\xC7':'Ccedil','\u2105':'incare','\uD835\uDD21':'dfr','\u2146':'dd','\uD835\uDD55':'dopf','\uD835\uDCB9':'dscr','\uD835\uDC9F':'Dscr','\uD835\uDD07':'Dfr','\u2145':'DD','\uD835\uDD3B':'Dopf','\u010F':'dcaron','\u010E':'Dcaron','\u0111':'dstrok','\u0110':'Dstrok','\xF0':'eth','\xD0':'ETH','\u2147':'ee','\u212F':'escr','\uD835\uDD22':'efr','\uD835\uDD56':'eopf','\u2130':'Escr','\uD835\uDD08':'Efr','\uD835\uDD3C':'Eopf','\xE9':'eacute','\xC9':'Eacute','\xE8':'egrave','\xC8':'Egrave','\xEA':'ecirc','\xCA':'Ecirc','\u011B':'ecaron','\u011A':'Ecaron','\xEB':'euml','\xCB':'Euml','\u0117':'edot','\u0116':'Edot','\u0119':'eogon','\u0118':'Eogon','\u0113':'emacr','\u0112':'Emacr','\uD835\uDD23':'ffr','\uD835\uDD57':'fopf','\uD835\uDCBB':'fscr','\uD835\uDD09':'Ffr','\uD835\uDD3D':'Fopf','\u2131':'Fscr','\uFB00':'fflig','\uFB03':'ffilig','\uFB04':'ffllig','\uFB01':'filig','fj':'fjlig','\uFB02':'fllig','\u0192':'fnof','\u210A':'gscr','\uD835\uDD58':'gopf','\uD835\uDD24':'gfr','\uD835\uDCA2':'Gscr','\uD835\uDD3E':'Gopf','\uD835\uDD0A':'Gfr','\u01F5':'gacute','\u011F':'gbreve','\u011E':'Gbreve','\u011D':'gcirc','\u011C':'Gcirc','\u0121':'gdot','\u0120':'Gdot','\u0122':'Gcedil','\uD835\uDD25':'hfr','\u210E':'planckh','\uD835\uDCBD':'hscr','\uD835\uDD59':'hopf','\u210B':'Hscr','\u210C':'Hfr','\u210D':'Hopf','\u0125':'hcirc','\u0124':'Hcirc','\u210F':'hbar','\u0127':'hstrok','\u0126':'Hstrok','\uD835\uDD5A':'iopf','\uD835\uDD26':'ifr','\uD835\uDCBE':'iscr','\u2148':'ii','\uD835\uDD40':'Iopf','\u2110':'Iscr','\u2111':'Im','\xED':'iacute','\xCD':'Iacute','\xEC':'igrave','\xCC':'Igrave','\xEE':'icirc','\xCE':'Icirc','\xEF':'iuml','\xCF':'Iuml','\u0129':'itilde','\u0128':'Itilde','\u0130':'Idot','\u012F':'iogon','\u012E':'Iogon','\u012B':'imacr','\u012A':'Imacr','\u0133':'ijlig','\u0132':'IJlig','\u0131':'imath','\uD835\uDCBF':'jscr','\uD835\uDD5B':'jopf','\uD835\uDD27':'jfr','\uD835\uDCA5':'Jscr','\uD835\uDD0D':'Jfr','\uD835\uDD41':'Jopf','\u0135':'jcirc','\u0134':'Jcirc','\u0237':'jmath','\uD835\uDD5C':'kopf','\uD835\uDCC0':'kscr','\uD835\uDD28':'kfr','\uD835\uDCA6':'Kscr','\uD835\uDD42':'Kopf','\uD835\uDD0E':'Kfr','\u0137':'kcedil','\u0136':'Kcedil','\uD835\uDD29':'lfr','\uD835\uDCC1':'lscr','\u2113':'ell','\uD835\uDD5D':'lopf','\u2112':'Lscr','\uD835\uDD0F':'Lfr','\uD835\uDD43':'Lopf','\u013A':'lacute','\u0139':'Lacute','\u013E':'lcaron','\u013D':'Lcaron','\u013C':'lcedil','\u013B':'Lcedil','\u0142':'lstrok','\u0141':'Lstrok','\u0140':'lmidot','\u013F':'Lmidot','\uD835\uDD2A':'mfr','\uD835\uDD5E':'mopf','\uD835\uDCC2':'mscr','\uD835\uDD10':'Mfr','\uD835\uDD44':'Mopf','\u2133':'Mscr','\uD835\uDD2B':'nfr','\uD835\uDD5F':'nopf','\uD835\uDCC3':'nscr','\u2115':'Nopf','\uD835\uDCA9':'Nscr','\uD835\uDD11':'Nfr','\u0144':'nacute','\u0143':'Nacute','\u0148':'ncaron','\u0147':'Ncaron','\xF1':'ntilde','\xD1':'Ntilde','\u0146':'ncedil','\u0145':'Ncedil','\u2116':'numero','\u014B':'eng','\u014A':'ENG','\uD835\uDD60':'oopf','\uD835\uDD2C':'ofr','\u2134':'oscr','\uD835\uDCAA':'Oscr','\uD835\uDD12':'Ofr','\uD835\uDD46':'Oopf','\xBA':'ordm','\xF3':'oacute','\xD3':'Oacute','\xF2':'ograve','\xD2':'Ograve','\xF4':'ocirc','\xD4':'Ocirc','\xF6':'ouml','\xD6':'Ouml','\u0151':'odblac','\u0150':'Odblac','\xF5':'otilde','\xD5':'Otilde','\xF8':'oslash','\xD8':'Oslash','\u014D':'omacr','\u014C':'Omacr','\u0153':'oelig','\u0152':'OElig','\uD835\uDD2D':'pfr','\uD835\uDCC5':'pscr','\uD835\uDD61':'popf','\u2119':'Popf','\uD835\uDD13':'Pfr','\uD835\uDCAB':'Pscr','\uD835\uDD62':'qopf','\uD835\uDD2E':'qfr','\uD835\uDCC6':'qscr','\uD835\uDCAC':'Qscr','\uD835\uDD14':'Qfr','\u211A':'Qopf','\u0138':'kgreen','\uD835\uDD2F':'rfr','\uD835\uDD63':'ropf','\uD835\uDCC7':'rscr','\u211B':'Rscr','\u211C':'Re','\u211D':'Ropf','\u0155':'racute','\u0154':'Racute','\u0159':'rcaron','\u0158':'Rcaron','\u0157':'rcedil','\u0156':'Rcedil','\uD835\uDD64':'sopf','\uD835\uDCC8':'sscr','\uD835\uDD30':'sfr','\uD835\uDD4A':'Sopf','\uD835\uDD16':'Sfr','\uD835\uDCAE':'Sscr','\u24C8':'oS','\u015B':'sacute','\u015A':'Sacute','\u015D':'scirc','\u015C':'Scirc','\u0161':'scaron','\u0160':'Scaron','\u015F':'scedil','\u015E':'Scedil','\xDF':'szlig','\uD835\uDD31':'tfr','\uD835\uDCC9':'tscr','\uD835\uDD65':'topf','\uD835\uDCAF':'Tscr','\uD835\uDD17':'Tfr','\uD835\uDD4B':'Topf','\u0165':'tcaron','\u0164':'Tcaron','\u0163':'tcedil','\u0162':'Tcedil','\u2122':'trade','\u0167':'tstrok','\u0166':'Tstrok','\uD835\uDCCA':'uscr','\uD835\uDD66':'uopf','\uD835\uDD32':'ufr','\uD835\uDD4C':'Uopf','\uD835\uDD18':'Ufr','\uD835\uDCB0':'Uscr','\xFA':'uacute','\xDA':'Uacute','\xF9':'ugrave','\xD9':'Ugrave','\u016D':'ubreve','\u016C':'Ubreve','\xFB':'ucirc','\xDB':'Ucirc','\u016F':'uring','\u016E':'Uring','\xFC':'uuml','\xDC':'Uuml','\u0171':'udblac','\u0170':'Udblac','\u0169':'utilde','\u0168':'Utilde','\u0173':'uogon','\u0172':'Uogon','\u016B':'umacr','\u016A':'Umacr','\uD835\uDD33':'vfr','\uD835\uDD67':'vopf','\uD835\uDCCB':'vscr','\uD835\uDD19':'Vfr','\uD835\uDD4D':'Vopf','\uD835\uDCB1':'Vscr','\uD835\uDD68':'wopf','\uD835\uDCCC':'wscr','\uD835\uDD34':'wfr','\uD835\uDCB2':'Wscr','\uD835\uDD4E':'Wopf','\uD835\uDD1A':'Wfr','\u0175':'wcirc','\u0174':'Wcirc','\uD835\uDD35':'xfr','\uD835\uDCCD':'xscr','\uD835\uDD69':'xopf','\uD835\uDD4F':'Xopf','\uD835\uDD1B':'Xfr','\uD835\uDCB3':'Xscr','\uD835\uDD36':'yfr','\uD835\uDCCE':'yscr','\uD835\uDD6A':'yopf','\uD835\uDCB4':'Yscr','\uD835\uDD1C':'Yfr','\uD835\uDD50':'Yopf','\xFD':'yacute','\xDD':'Yacute','\u0177':'ycirc','\u0176':'Ycirc','\xFF':'yuml','\u0178':'Yuml','\uD835\uDCCF':'zscr','\uD835\uDD37':'zfr','\uD835\uDD6B':'zopf','\u2128':'Zfr','\u2124':'Zopf','\uD835\uDCB5':'Zscr','\u017A':'zacute','\u0179':'Zacute','\u017E':'zcaron','\u017D':'Zcaron','\u017C':'zdot','\u017B':'Zdot','\u01B5':'imped','\xFE':'thorn','\xDE':'THORN','\u0149':'napos','\u03B1':'alpha','\u0391':'Alpha','\u03B2':'beta','\u0392':'Beta','\u03B3':'gamma','\u0393':'Gamma','\u03B4':'delta','\u0394':'Delta','\u03B5':'epsi','\u03F5':'epsiv','\u0395':'Epsilon','\u03DD':'gammad','\u03DC':'Gammad','\u03B6':'zeta','\u0396':'Zeta','\u03B7':'eta','\u0397':'Eta','\u03B8':'theta','\u03D1':'thetav','\u0398':'Theta','\u03B9':'iota','\u0399':'Iota','\u03BA':'kappa','\u03F0':'kappav','\u039A':'Kappa','\u03BB':'lambda','\u039B':'Lambda','\u03BC':'mu','\xB5':'micro','\u039C':'Mu','\u03BD':'nu','\u039D':'Nu','\u03BE':'xi','\u039E':'Xi','\u03BF':'omicron','\u039F':'Omicron','\u03C0':'pi','\u03D6':'piv','\u03A0':'Pi','\u03C1':'rho','\u03F1':'rhov','\u03A1':'Rho','\u03C3':'sigma','\u03A3':'Sigma','\u03C2':'sigmaf','\u03C4':'tau','\u03A4':'Tau','\u03C5':'upsi','\u03A5':'Upsilon','\u03D2':'Upsi','\u03C6':'phi','\u03D5':'phiv','\u03A6':'Phi','\u03C7':'chi','\u03A7':'Chi','\u03C8':'psi','\u03A8':'Psi','\u03C9':'omega','\u03A9':'ohm','\u0430':'acy','\u0410':'Acy','\u0431':'bcy','\u0411':'Bcy','\u0432':'vcy','\u0412':'Vcy','\u0433':'gcy','\u0413':'Gcy','\u0453':'gjcy','\u0403':'GJcy','\u0434':'dcy','\u0414':'Dcy','\u0452':'djcy','\u0402':'DJcy','\u0435':'iecy','\u0415':'IEcy','\u0451':'iocy','\u0401':'IOcy','\u0454':'jukcy','\u0404':'Jukcy','\u0436':'zhcy','\u0416':'ZHcy','\u0437':'zcy','\u0417':'Zcy','\u0455':'dscy','\u0405':'DScy','\u0438':'icy','\u0418':'Icy','\u0456':'iukcy','\u0406':'Iukcy','\u0457':'yicy','\u0407':'YIcy','\u0439':'jcy','\u0419':'Jcy','\u0458':'jsercy','\u0408':'Jsercy','\u043A':'kcy','\u041A':'Kcy','\u045C':'kjcy','\u040C':'KJcy','\u043B':'lcy','\u041B':'Lcy','\u0459':'ljcy','\u0409':'LJcy','\u043C':'mcy','\u041C':'Mcy','\u043D':'ncy','\u041D':'Ncy','\u045A':'njcy','\u040A':'NJcy','\u043E':'ocy','\u041E':'Ocy','\u043F':'pcy','\u041F':'Pcy','\u0440':'rcy','\u0420':'Rcy','\u0441':'scy','\u0421':'Scy','\u0442':'tcy','\u0422':'Tcy','\u045B':'tshcy','\u040B':'TSHcy','\u0443':'ucy','\u0423':'Ucy','\u045E':'ubrcy','\u040E':'Ubrcy','\u0444':'fcy','\u0424':'Fcy','\u0445':'khcy','\u0425':'KHcy','\u0446':'tscy','\u0426':'TScy','\u0447':'chcy','\u0427':'CHcy','\u045F':'dzcy','\u040F':'DZcy','\u0448':'shcy','\u0428':'SHcy','\u0449':'shchcy','\u0429':'SHCHcy','\u044A':'hardcy','\u042A':'HARDcy','\u044B':'ycy','\u042B':'Ycy','\u044C':'softcy','\u042C':'SOFTcy','\u044D':'ecy','\u042D':'Ecy','\u044E':'yucy','\u042E':'YUcy','\u044F':'yacy','\u042F':'YAcy','\u2135':'aleph','\u2136':'beth','\u2137':'gimel','\u2138':'daleth'}; - - var regexEscape = /["&'<>`]/g; - var escapeMap = { - '"': '"', - '&': '&', - '\'': ''', - '<': '<', - // See https://mathiasbynens.be/notes/ambiguous-ampersands: in HTML, the - // following is not strictly necessary unless it’s part of a tag or an - // unquoted attribute value. We’re only escaping it to support those - // situations, and for XML support. - '>': '>', - // In Internet Explorer ≤ 8, the backtick character can be used - // to break out of (un)quoted attribute values or HTML comments. - // See http://html5sec.org/#102, http://html5sec.org/#108, and - // http://html5sec.org/#133. - '`': '`' - }; - - var regexInvalidEntity = /&#(?:[xX][^a-fA-F0-9]|[^0-9xX])/; - var regexInvalidRawCodePoint = /[\0-\x08\x0B\x0E-\x1F\x7F-\x9F\uFDD0-\uFDEF\uFFFE\uFFFF]|[\uD83F\uD87F\uD8BF\uD8FF\uD93F\uD97F\uD9BF\uD9FF\uDA3F\uDA7F\uDABF\uDAFF\uDB3F\uDB7F\uDBBF\uDBFF][\uDFFE\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; - var regexDecode = /&#([0-9]+)(;?)|&#[xX]([a-fA-F0-9]+)(;?)|&([0-9a-zA-Z]+);|&(Aacute|Agrave|Atilde|Ccedil|Eacute|Egrave|Iacute|Igrave|Ntilde|Oacute|Ograve|Oslash|Otilde|Uacute|Ugrave|Yacute|aacute|agrave|atilde|brvbar|ccedil|curren|divide|eacute|egrave|frac12|frac14|frac34|iacute|igrave|iquest|middot|ntilde|oacute|ograve|oslash|otilde|plusmn|uacute|ugrave|yacute|AElig|Acirc|Aring|Ecirc|Icirc|Ocirc|THORN|Ucirc|acirc|acute|aelig|aring|cedil|ecirc|icirc|iexcl|laquo|micro|ocirc|pound|raquo|szlig|thorn|times|ucirc|Auml|COPY|Euml|Iuml|Ouml|QUOT|Uuml|auml|cent|copy|euml|iuml|macr|nbsp|ordf|ordm|ouml|para|quot|sect|sup1|sup2|sup3|uuml|yuml|AMP|ETH|REG|amp|deg|eth|not|reg|shy|uml|yen|GT|LT|gt|lt)([=a-zA-Z0-9])?/g; - var decodeMap = {'aacute':'\xE1','Aacute':'\xC1','abreve':'\u0103','Abreve':'\u0102','ac':'\u223E','acd':'\u223F','acE':'\u223E\u0333','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','acy':'\u0430','Acy':'\u0410','aelig':'\xE6','AElig':'\xC6','af':'\u2061','afr':'\uD835\uDD1E','Afr':'\uD835\uDD04','agrave':'\xE0','Agrave':'\xC0','alefsym':'\u2135','aleph':'\u2135','alpha':'\u03B1','Alpha':'\u0391','amacr':'\u0101','Amacr':'\u0100','amalg':'\u2A3F','amp':'&','AMP':'&','and':'\u2227','And':'\u2A53','andand':'\u2A55','andd':'\u2A5C','andslope':'\u2A58','andv':'\u2A5A','ang':'\u2220','ange':'\u29A4','angle':'\u2220','angmsd':'\u2221','angmsdaa':'\u29A8','angmsdab':'\u29A9','angmsdac':'\u29AA','angmsdad':'\u29AB','angmsdae':'\u29AC','angmsdaf':'\u29AD','angmsdag':'\u29AE','angmsdah':'\u29AF','angrt':'\u221F','angrtvb':'\u22BE','angrtvbd':'\u299D','angsph':'\u2222','angst':'\xC5','angzarr':'\u237C','aogon':'\u0105','Aogon':'\u0104','aopf':'\uD835\uDD52','Aopf':'\uD835\uDD38','ap':'\u2248','apacir':'\u2A6F','ape':'\u224A','apE':'\u2A70','apid':'\u224B','apos':'\'','ApplyFunction':'\u2061','approx':'\u2248','approxeq':'\u224A','aring':'\xE5','Aring':'\xC5','ascr':'\uD835\uDCB6','Ascr':'\uD835\uDC9C','Assign':'\u2254','ast':'*','asymp':'\u2248','asympeq':'\u224D','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','awconint':'\u2233','awint':'\u2A11','backcong':'\u224C','backepsilon':'\u03F6','backprime':'\u2035','backsim':'\u223D','backsimeq':'\u22CD','Backslash':'\u2216','Barv':'\u2AE7','barvee':'\u22BD','barwed':'\u2305','Barwed':'\u2306','barwedge':'\u2305','bbrk':'\u23B5','bbrktbrk':'\u23B6','bcong':'\u224C','bcy':'\u0431','Bcy':'\u0411','bdquo':'\u201E','becaus':'\u2235','because':'\u2235','Because':'\u2235','bemptyv':'\u29B0','bepsi':'\u03F6','bernou':'\u212C','Bernoullis':'\u212C','beta':'\u03B2','Beta':'\u0392','beth':'\u2136','between':'\u226C','bfr':'\uD835\uDD1F','Bfr':'\uD835\uDD05','bigcap':'\u22C2','bigcirc':'\u25EF','bigcup':'\u22C3','bigodot':'\u2A00','bigoplus':'\u2A01','bigotimes':'\u2A02','bigsqcup':'\u2A06','bigstar':'\u2605','bigtriangledown':'\u25BD','bigtriangleup':'\u25B3','biguplus':'\u2A04','bigvee':'\u22C1','bigwedge':'\u22C0','bkarow':'\u290D','blacklozenge':'\u29EB','blacksquare':'\u25AA','blacktriangle':'\u25B4','blacktriangledown':'\u25BE','blacktriangleleft':'\u25C2','blacktriangleright':'\u25B8','blank':'\u2423','blk12':'\u2592','blk14':'\u2591','blk34':'\u2593','block':'\u2588','bne':'=\u20E5','bnequiv':'\u2261\u20E5','bnot':'\u2310','bNot':'\u2AED','bopf':'\uD835\uDD53','Bopf':'\uD835\uDD39','bot':'\u22A5','bottom':'\u22A5','bowtie':'\u22C8','boxbox':'\u29C9','boxdl':'\u2510','boxdL':'\u2555','boxDl':'\u2556','boxDL':'\u2557','boxdr':'\u250C','boxdR':'\u2552','boxDr':'\u2553','boxDR':'\u2554','boxh':'\u2500','boxH':'\u2550','boxhd':'\u252C','boxhD':'\u2565','boxHd':'\u2564','boxHD':'\u2566','boxhu':'\u2534','boxhU':'\u2568','boxHu':'\u2567','boxHU':'\u2569','boxminus':'\u229F','boxplus':'\u229E','boxtimes':'\u22A0','boxul':'\u2518','boxuL':'\u255B','boxUl':'\u255C','boxUL':'\u255D','boxur':'\u2514','boxuR':'\u2558','boxUr':'\u2559','boxUR':'\u255A','boxv':'\u2502','boxV':'\u2551','boxvh':'\u253C','boxvH':'\u256A','boxVh':'\u256B','boxVH':'\u256C','boxvl':'\u2524','boxvL':'\u2561','boxVl':'\u2562','boxVL':'\u2563','boxvr':'\u251C','boxvR':'\u255E','boxVr':'\u255F','boxVR':'\u2560','bprime':'\u2035','breve':'\u02D8','Breve':'\u02D8','brvbar':'\xA6','bscr':'\uD835\uDCB7','Bscr':'\u212C','bsemi':'\u204F','bsim':'\u223D','bsime':'\u22CD','bsol':'\\','bsolb':'\u29C5','bsolhsub':'\u27C8','bull':'\u2022','bullet':'\u2022','bump':'\u224E','bumpe':'\u224F','bumpE':'\u2AAE','bumpeq':'\u224F','Bumpeq':'\u224E','cacute':'\u0107','Cacute':'\u0106','cap':'\u2229','Cap':'\u22D2','capand':'\u2A44','capbrcup':'\u2A49','capcap':'\u2A4B','capcup':'\u2A47','capdot':'\u2A40','CapitalDifferentialD':'\u2145','caps':'\u2229\uFE00','caret':'\u2041','caron':'\u02C7','Cayleys':'\u212D','ccaps':'\u2A4D','ccaron':'\u010D','Ccaron':'\u010C','ccedil':'\xE7','Ccedil':'\xC7','ccirc':'\u0109','Ccirc':'\u0108','Cconint':'\u2230','ccups':'\u2A4C','ccupssm':'\u2A50','cdot':'\u010B','Cdot':'\u010A','cedil':'\xB8','Cedilla':'\xB8','cemptyv':'\u29B2','cent':'\xA2','centerdot':'\xB7','CenterDot':'\xB7','cfr':'\uD835\uDD20','Cfr':'\u212D','chcy':'\u0447','CHcy':'\u0427','check':'\u2713','checkmark':'\u2713','chi':'\u03C7','Chi':'\u03A7','cir':'\u25CB','circ':'\u02C6','circeq':'\u2257','circlearrowleft':'\u21BA','circlearrowright':'\u21BB','circledast':'\u229B','circledcirc':'\u229A','circleddash':'\u229D','CircleDot':'\u2299','circledR':'\xAE','circledS':'\u24C8','CircleMinus':'\u2296','CirclePlus':'\u2295','CircleTimes':'\u2297','cire':'\u2257','cirE':'\u29C3','cirfnint':'\u2A10','cirmid':'\u2AEF','cirscir':'\u29C2','ClockwiseContourIntegral':'\u2232','CloseCurlyDoubleQuote':'\u201D','CloseCurlyQuote':'\u2019','clubs':'\u2663','clubsuit':'\u2663','colon':':','Colon':'\u2237','colone':'\u2254','Colone':'\u2A74','coloneq':'\u2254','comma':',','commat':'@','comp':'\u2201','compfn':'\u2218','complement':'\u2201','complexes':'\u2102','cong':'\u2245','congdot':'\u2A6D','Congruent':'\u2261','conint':'\u222E','Conint':'\u222F','ContourIntegral':'\u222E','copf':'\uD835\uDD54','Copf':'\u2102','coprod':'\u2210','Coproduct':'\u2210','copy':'\xA9','COPY':'\xA9','copysr':'\u2117','CounterClockwiseContourIntegral':'\u2233','crarr':'\u21B5','cross':'\u2717','Cross':'\u2A2F','cscr':'\uD835\uDCB8','Cscr':'\uD835\uDC9E','csub':'\u2ACF','csube':'\u2AD1','csup':'\u2AD0','csupe':'\u2AD2','ctdot':'\u22EF','cudarrl':'\u2938','cudarrr':'\u2935','cuepr':'\u22DE','cuesc':'\u22DF','cularr':'\u21B6','cularrp':'\u293D','cup':'\u222A','Cup':'\u22D3','cupbrcap':'\u2A48','cupcap':'\u2A46','CupCap':'\u224D','cupcup':'\u2A4A','cupdot':'\u228D','cupor':'\u2A45','cups':'\u222A\uFE00','curarr':'\u21B7','curarrm':'\u293C','curlyeqprec':'\u22DE','curlyeqsucc':'\u22DF','curlyvee':'\u22CE','curlywedge':'\u22CF','curren':'\xA4','curvearrowleft':'\u21B6','curvearrowright':'\u21B7','cuvee':'\u22CE','cuwed':'\u22CF','cwconint':'\u2232','cwint':'\u2231','cylcty':'\u232D','dagger':'\u2020','Dagger':'\u2021','daleth':'\u2138','darr':'\u2193','dArr':'\u21D3','Darr':'\u21A1','dash':'\u2010','dashv':'\u22A3','Dashv':'\u2AE4','dbkarow':'\u290F','dblac':'\u02DD','dcaron':'\u010F','Dcaron':'\u010E','dcy':'\u0434','Dcy':'\u0414','dd':'\u2146','DD':'\u2145','ddagger':'\u2021','ddarr':'\u21CA','DDotrahd':'\u2911','ddotseq':'\u2A77','deg':'\xB0','Del':'\u2207','delta':'\u03B4','Delta':'\u0394','demptyv':'\u29B1','dfisht':'\u297F','dfr':'\uD835\uDD21','Dfr':'\uD835\uDD07','dHar':'\u2965','dharl':'\u21C3','dharr':'\u21C2','DiacriticalAcute':'\xB4','DiacriticalDot':'\u02D9','DiacriticalDoubleAcute':'\u02DD','DiacriticalGrave':'`','DiacriticalTilde':'\u02DC','diam':'\u22C4','diamond':'\u22C4','Diamond':'\u22C4','diamondsuit':'\u2666','diams':'\u2666','die':'\xA8','DifferentialD':'\u2146','digamma':'\u03DD','disin':'\u22F2','div':'\xF7','divide':'\xF7','divideontimes':'\u22C7','divonx':'\u22C7','djcy':'\u0452','DJcy':'\u0402','dlcorn':'\u231E','dlcrop':'\u230D','dollar':'$','dopf':'\uD835\uDD55','Dopf':'\uD835\uDD3B','dot':'\u02D9','Dot':'\xA8','DotDot':'\u20DC','doteq':'\u2250','doteqdot':'\u2251','DotEqual':'\u2250','dotminus':'\u2238','dotplus':'\u2214','dotsquare':'\u22A1','doublebarwedge':'\u2306','DoubleContourIntegral':'\u222F','DoubleDot':'\xA8','DoubleDownArrow':'\u21D3','DoubleLeftArrow':'\u21D0','DoubleLeftRightArrow':'\u21D4','DoubleLeftTee':'\u2AE4','DoubleLongLeftArrow':'\u27F8','DoubleLongLeftRightArrow':'\u27FA','DoubleLongRightArrow':'\u27F9','DoubleRightArrow':'\u21D2','DoubleRightTee':'\u22A8','DoubleUpArrow':'\u21D1','DoubleUpDownArrow':'\u21D5','DoubleVerticalBar':'\u2225','downarrow':'\u2193','Downarrow':'\u21D3','DownArrow':'\u2193','DownArrowBar':'\u2913','DownArrowUpArrow':'\u21F5','DownBreve':'\u0311','downdownarrows':'\u21CA','downharpoonleft':'\u21C3','downharpoonright':'\u21C2','DownLeftRightVector':'\u2950','DownLeftTeeVector':'\u295E','DownLeftVector':'\u21BD','DownLeftVectorBar':'\u2956','DownRightTeeVector':'\u295F','DownRightVector':'\u21C1','DownRightVectorBar':'\u2957','DownTee':'\u22A4','DownTeeArrow':'\u21A7','drbkarow':'\u2910','drcorn':'\u231F','drcrop':'\u230C','dscr':'\uD835\uDCB9','Dscr':'\uD835\uDC9F','dscy':'\u0455','DScy':'\u0405','dsol':'\u29F6','dstrok':'\u0111','Dstrok':'\u0110','dtdot':'\u22F1','dtri':'\u25BF','dtrif':'\u25BE','duarr':'\u21F5','duhar':'\u296F','dwangle':'\u29A6','dzcy':'\u045F','DZcy':'\u040F','dzigrarr':'\u27FF','eacute':'\xE9','Eacute':'\xC9','easter':'\u2A6E','ecaron':'\u011B','Ecaron':'\u011A','ecir':'\u2256','ecirc':'\xEA','Ecirc':'\xCA','ecolon':'\u2255','ecy':'\u044D','Ecy':'\u042D','eDDot':'\u2A77','edot':'\u0117','eDot':'\u2251','Edot':'\u0116','ee':'\u2147','efDot':'\u2252','efr':'\uD835\uDD22','Efr':'\uD835\uDD08','eg':'\u2A9A','egrave':'\xE8','Egrave':'\xC8','egs':'\u2A96','egsdot':'\u2A98','el':'\u2A99','Element':'\u2208','elinters':'\u23E7','ell':'\u2113','els':'\u2A95','elsdot':'\u2A97','emacr':'\u0113','Emacr':'\u0112','empty':'\u2205','emptyset':'\u2205','EmptySmallSquare':'\u25FB','emptyv':'\u2205','EmptyVerySmallSquare':'\u25AB','emsp':'\u2003','emsp13':'\u2004','emsp14':'\u2005','eng':'\u014B','ENG':'\u014A','ensp':'\u2002','eogon':'\u0119','Eogon':'\u0118','eopf':'\uD835\uDD56','Eopf':'\uD835\uDD3C','epar':'\u22D5','eparsl':'\u29E3','eplus':'\u2A71','epsi':'\u03B5','epsilon':'\u03B5','Epsilon':'\u0395','epsiv':'\u03F5','eqcirc':'\u2256','eqcolon':'\u2255','eqsim':'\u2242','eqslantgtr':'\u2A96','eqslantless':'\u2A95','Equal':'\u2A75','equals':'=','EqualTilde':'\u2242','equest':'\u225F','Equilibrium':'\u21CC','equiv':'\u2261','equivDD':'\u2A78','eqvparsl':'\u29E5','erarr':'\u2971','erDot':'\u2253','escr':'\u212F','Escr':'\u2130','esdot':'\u2250','esim':'\u2242','Esim':'\u2A73','eta':'\u03B7','Eta':'\u0397','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','euro':'\u20AC','excl':'!','exist':'\u2203','Exists':'\u2203','expectation':'\u2130','exponentiale':'\u2147','ExponentialE':'\u2147','fallingdotseq':'\u2252','fcy':'\u0444','Fcy':'\u0424','female':'\u2640','ffilig':'\uFB03','fflig':'\uFB00','ffllig':'\uFB04','ffr':'\uD835\uDD23','Ffr':'\uD835\uDD09','filig':'\uFB01','FilledSmallSquare':'\u25FC','FilledVerySmallSquare':'\u25AA','fjlig':'fj','flat':'\u266D','fllig':'\uFB02','fltns':'\u25B1','fnof':'\u0192','fopf':'\uD835\uDD57','Fopf':'\uD835\uDD3D','forall':'\u2200','ForAll':'\u2200','fork':'\u22D4','forkv':'\u2AD9','Fouriertrf':'\u2131','fpartint':'\u2A0D','frac12':'\xBD','frac13':'\u2153','frac14':'\xBC','frac15':'\u2155','frac16':'\u2159','frac18':'\u215B','frac23':'\u2154','frac25':'\u2156','frac34':'\xBE','frac35':'\u2157','frac38':'\u215C','frac45':'\u2158','frac56':'\u215A','frac58':'\u215D','frac78':'\u215E','frasl':'\u2044','frown':'\u2322','fscr':'\uD835\uDCBB','Fscr':'\u2131','gacute':'\u01F5','gamma':'\u03B3','Gamma':'\u0393','gammad':'\u03DD','Gammad':'\u03DC','gap':'\u2A86','gbreve':'\u011F','Gbreve':'\u011E','Gcedil':'\u0122','gcirc':'\u011D','Gcirc':'\u011C','gcy':'\u0433','Gcy':'\u0413','gdot':'\u0121','Gdot':'\u0120','ge':'\u2265','gE':'\u2267','gel':'\u22DB','gEl':'\u2A8C','geq':'\u2265','geqq':'\u2267','geqslant':'\u2A7E','ges':'\u2A7E','gescc':'\u2AA9','gesdot':'\u2A80','gesdoto':'\u2A82','gesdotol':'\u2A84','gesl':'\u22DB\uFE00','gesles':'\u2A94','gfr':'\uD835\uDD24','Gfr':'\uD835\uDD0A','gg':'\u226B','Gg':'\u22D9','ggg':'\u22D9','gimel':'\u2137','gjcy':'\u0453','GJcy':'\u0403','gl':'\u2277','gla':'\u2AA5','glE':'\u2A92','glj':'\u2AA4','gnap':'\u2A8A','gnapprox':'\u2A8A','gne':'\u2A88','gnE':'\u2269','gneq':'\u2A88','gneqq':'\u2269','gnsim':'\u22E7','gopf':'\uD835\uDD58','Gopf':'\uD835\uDD3E','grave':'`','GreaterEqual':'\u2265','GreaterEqualLess':'\u22DB','GreaterFullEqual':'\u2267','GreaterGreater':'\u2AA2','GreaterLess':'\u2277','GreaterSlantEqual':'\u2A7E','GreaterTilde':'\u2273','gscr':'\u210A','Gscr':'\uD835\uDCA2','gsim':'\u2273','gsime':'\u2A8E','gsiml':'\u2A90','gt':'>','Gt':'\u226B','GT':'>','gtcc':'\u2AA7','gtcir':'\u2A7A','gtdot':'\u22D7','gtlPar':'\u2995','gtquest':'\u2A7C','gtrapprox':'\u2A86','gtrarr':'\u2978','gtrdot':'\u22D7','gtreqless':'\u22DB','gtreqqless':'\u2A8C','gtrless':'\u2277','gtrsim':'\u2273','gvertneqq':'\u2269\uFE00','gvnE':'\u2269\uFE00','Hacek':'\u02C7','hairsp':'\u200A','half':'\xBD','hamilt':'\u210B','hardcy':'\u044A','HARDcy':'\u042A','harr':'\u2194','hArr':'\u21D4','harrcir':'\u2948','harrw':'\u21AD','Hat':'^','hbar':'\u210F','hcirc':'\u0125','Hcirc':'\u0124','hearts':'\u2665','heartsuit':'\u2665','hellip':'\u2026','hercon':'\u22B9','hfr':'\uD835\uDD25','Hfr':'\u210C','HilbertSpace':'\u210B','hksearow':'\u2925','hkswarow':'\u2926','hoarr':'\u21FF','homtht':'\u223B','hookleftarrow':'\u21A9','hookrightarrow':'\u21AA','hopf':'\uD835\uDD59','Hopf':'\u210D','horbar':'\u2015','HorizontalLine':'\u2500','hscr':'\uD835\uDCBD','Hscr':'\u210B','hslash':'\u210F','hstrok':'\u0127','Hstrok':'\u0126','HumpDownHump':'\u224E','HumpEqual':'\u224F','hybull':'\u2043','hyphen':'\u2010','iacute':'\xED','Iacute':'\xCD','ic':'\u2063','icirc':'\xEE','Icirc':'\xCE','icy':'\u0438','Icy':'\u0418','Idot':'\u0130','iecy':'\u0435','IEcy':'\u0415','iexcl':'\xA1','iff':'\u21D4','ifr':'\uD835\uDD26','Ifr':'\u2111','igrave':'\xEC','Igrave':'\xCC','ii':'\u2148','iiiint':'\u2A0C','iiint':'\u222D','iinfin':'\u29DC','iiota':'\u2129','ijlig':'\u0133','IJlig':'\u0132','Im':'\u2111','imacr':'\u012B','Imacr':'\u012A','image':'\u2111','ImaginaryI':'\u2148','imagline':'\u2110','imagpart':'\u2111','imath':'\u0131','imof':'\u22B7','imped':'\u01B5','Implies':'\u21D2','in':'\u2208','incare':'\u2105','infin':'\u221E','infintie':'\u29DD','inodot':'\u0131','int':'\u222B','Int':'\u222C','intcal':'\u22BA','integers':'\u2124','Integral':'\u222B','intercal':'\u22BA','Intersection':'\u22C2','intlarhk':'\u2A17','intprod':'\u2A3C','InvisibleComma':'\u2063','InvisibleTimes':'\u2062','iocy':'\u0451','IOcy':'\u0401','iogon':'\u012F','Iogon':'\u012E','iopf':'\uD835\uDD5A','Iopf':'\uD835\uDD40','iota':'\u03B9','Iota':'\u0399','iprod':'\u2A3C','iquest':'\xBF','iscr':'\uD835\uDCBE','Iscr':'\u2110','isin':'\u2208','isindot':'\u22F5','isinE':'\u22F9','isins':'\u22F4','isinsv':'\u22F3','isinv':'\u2208','it':'\u2062','itilde':'\u0129','Itilde':'\u0128','iukcy':'\u0456','Iukcy':'\u0406','iuml':'\xEF','Iuml':'\xCF','jcirc':'\u0135','Jcirc':'\u0134','jcy':'\u0439','Jcy':'\u0419','jfr':'\uD835\uDD27','Jfr':'\uD835\uDD0D','jmath':'\u0237','jopf':'\uD835\uDD5B','Jopf':'\uD835\uDD41','jscr':'\uD835\uDCBF','Jscr':'\uD835\uDCA5','jsercy':'\u0458','Jsercy':'\u0408','jukcy':'\u0454','Jukcy':'\u0404','kappa':'\u03BA','Kappa':'\u039A','kappav':'\u03F0','kcedil':'\u0137','Kcedil':'\u0136','kcy':'\u043A','Kcy':'\u041A','kfr':'\uD835\uDD28','Kfr':'\uD835\uDD0E','kgreen':'\u0138','khcy':'\u0445','KHcy':'\u0425','kjcy':'\u045C','KJcy':'\u040C','kopf':'\uD835\uDD5C','Kopf':'\uD835\uDD42','kscr':'\uD835\uDCC0','Kscr':'\uD835\uDCA6','lAarr':'\u21DA','lacute':'\u013A','Lacute':'\u0139','laemptyv':'\u29B4','lagran':'\u2112','lambda':'\u03BB','Lambda':'\u039B','lang':'\u27E8','Lang':'\u27EA','langd':'\u2991','langle':'\u27E8','lap':'\u2A85','Laplacetrf':'\u2112','laquo':'\xAB','larr':'\u2190','lArr':'\u21D0','Larr':'\u219E','larrb':'\u21E4','larrbfs':'\u291F','larrfs':'\u291D','larrhk':'\u21A9','larrlp':'\u21AB','larrpl':'\u2939','larrsim':'\u2973','larrtl':'\u21A2','lat':'\u2AAB','latail':'\u2919','lAtail':'\u291B','late':'\u2AAD','lates':'\u2AAD\uFE00','lbarr':'\u290C','lBarr':'\u290E','lbbrk':'\u2772','lbrace':'{','lbrack':'[','lbrke':'\u298B','lbrksld':'\u298F','lbrkslu':'\u298D','lcaron':'\u013E','Lcaron':'\u013D','lcedil':'\u013C','Lcedil':'\u013B','lceil':'\u2308','lcub':'{','lcy':'\u043B','Lcy':'\u041B','ldca':'\u2936','ldquo':'\u201C','ldquor':'\u201E','ldrdhar':'\u2967','ldrushar':'\u294B','ldsh':'\u21B2','le':'\u2264','lE':'\u2266','LeftAngleBracket':'\u27E8','leftarrow':'\u2190','Leftarrow':'\u21D0','LeftArrow':'\u2190','LeftArrowBar':'\u21E4','LeftArrowRightArrow':'\u21C6','leftarrowtail':'\u21A2','LeftCeiling':'\u2308','LeftDoubleBracket':'\u27E6','LeftDownTeeVector':'\u2961','LeftDownVector':'\u21C3','LeftDownVectorBar':'\u2959','LeftFloor':'\u230A','leftharpoondown':'\u21BD','leftharpoonup':'\u21BC','leftleftarrows':'\u21C7','leftrightarrow':'\u2194','Leftrightarrow':'\u21D4','LeftRightArrow':'\u2194','leftrightarrows':'\u21C6','leftrightharpoons':'\u21CB','leftrightsquigarrow':'\u21AD','LeftRightVector':'\u294E','LeftTee':'\u22A3','LeftTeeArrow':'\u21A4','LeftTeeVector':'\u295A','leftthreetimes':'\u22CB','LeftTriangle':'\u22B2','LeftTriangleBar':'\u29CF','LeftTriangleEqual':'\u22B4','LeftUpDownVector':'\u2951','LeftUpTeeVector':'\u2960','LeftUpVector':'\u21BF','LeftUpVectorBar':'\u2958','LeftVector':'\u21BC','LeftVectorBar':'\u2952','leg':'\u22DA','lEg':'\u2A8B','leq':'\u2264','leqq':'\u2266','leqslant':'\u2A7D','les':'\u2A7D','lescc':'\u2AA8','lesdot':'\u2A7F','lesdoto':'\u2A81','lesdotor':'\u2A83','lesg':'\u22DA\uFE00','lesges':'\u2A93','lessapprox':'\u2A85','lessdot':'\u22D6','lesseqgtr':'\u22DA','lesseqqgtr':'\u2A8B','LessEqualGreater':'\u22DA','LessFullEqual':'\u2266','LessGreater':'\u2276','lessgtr':'\u2276','LessLess':'\u2AA1','lesssim':'\u2272','LessSlantEqual':'\u2A7D','LessTilde':'\u2272','lfisht':'\u297C','lfloor':'\u230A','lfr':'\uD835\uDD29','Lfr':'\uD835\uDD0F','lg':'\u2276','lgE':'\u2A91','lHar':'\u2962','lhard':'\u21BD','lharu':'\u21BC','lharul':'\u296A','lhblk':'\u2584','ljcy':'\u0459','LJcy':'\u0409','ll':'\u226A','Ll':'\u22D8','llarr':'\u21C7','llcorner':'\u231E','Lleftarrow':'\u21DA','llhard':'\u296B','lltri':'\u25FA','lmidot':'\u0140','Lmidot':'\u013F','lmoust':'\u23B0','lmoustache':'\u23B0','lnap':'\u2A89','lnapprox':'\u2A89','lne':'\u2A87','lnE':'\u2268','lneq':'\u2A87','lneqq':'\u2268','lnsim':'\u22E6','loang':'\u27EC','loarr':'\u21FD','lobrk':'\u27E6','longleftarrow':'\u27F5','Longleftarrow':'\u27F8','LongLeftArrow':'\u27F5','longleftrightarrow':'\u27F7','Longleftrightarrow':'\u27FA','LongLeftRightArrow':'\u27F7','longmapsto':'\u27FC','longrightarrow':'\u27F6','Longrightarrow':'\u27F9','LongRightArrow':'\u27F6','looparrowleft':'\u21AB','looparrowright':'\u21AC','lopar':'\u2985','lopf':'\uD835\uDD5D','Lopf':'\uD835\uDD43','loplus':'\u2A2D','lotimes':'\u2A34','lowast':'\u2217','lowbar':'_','LowerLeftArrow':'\u2199','LowerRightArrow':'\u2198','loz':'\u25CA','lozenge':'\u25CA','lozf':'\u29EB','lpar':'(','lparlt':'\u2993','lrarr':'\u21C6','lrcorner':'\u231F','lrhar':'\u21CB','lrhard':'\u296D','lrm':'\u200E','lrtri':'\u22BF','lsaquo':'\u2039','lscr':'\uD835\uDCC1','Lscr':'\u2112','lsh':'\u21B0','Lsh':'\u21B0','lsim':'\u2272','lsime':'\u2A8D','lsimg':'\u2A8F','lsqb':'[','lsquo':'\u2018','lsquor':'\u201A','lstrok':'\u0142','Lstrok':'\u0141','lt':'<','Lt':'\u226A','LT':'<','ltcc':'\u2AA6','ltcir':'\u2A79','ltdot':'\u22D6','lthree':'\u22CB','ltimes':'\u22C9','ltlarr':'\u2976','ltquest':'\u2A7B','ltri':'\u25C3','ltrie':'\u22B4','ltrif':'\u25C2','ltrPar':'\u2996','lurdshar':'\u294A','luruhar':'\u2966','lvertneqq':'\u2268\uFE00','lvnE':'\u2268\uFE00','macr':'\xAF','male':'\u2642','malt':'\u2720','maltese':'\u2720','map':'\u21A6','Map':'\u2905','mapsto':'\u21A6','mapstodown':'\u21A7','mapstoleft':'\u21A4','mapstoup':'\u21A5','marker':'\u25AE','mcomma':'\u2A29','mcy':'\u043C','Mcy':'\u041C','mdash':'\u2014','mDDot':'\u223A','measuredangle':'\u2221','MediumSpace':'\u205F','Mellintrf':'\u2133','mfr':'\uD835\uDD2A','Mfr':'\uD835\uDD10','mho':'\u2127','micro':'\xB5','mid':'\u2223','midast':'*','midcir':'\u2AF0','middot':'\xB7','minus':'\u2212','minusb':'\u229F','minusd':'\u2238','minusdu':'\u2A2A','MinusPlus':'\u2213','mlcp':'\u2ADB','mldr':'\u2026','mnplus':'\u2213','models':'\u22A7','mopf':'\uD835\uDD5E','Mopf':'\uD835\uDD44','mp':'\u2213','mscr':'\uD835\uDCC2','Mscr':'\u2133','mstpos':'\u223E','mu':'\u03BC','Mu':'\u039C','multimap':'\u22B8','mumap':'\u22B8','nabla':'\u2207','nacute':'\u0144','Nacute':'\u0143','nang':'\u2220\u20D2','nap':'\u2249','napE':'\u2A70\u0338','napid':'\u224B\u0338','napos':'\u0149','napprox':'\u2249','natur':'\u266E','natural':'\u266E','naturals':'\u2115','nbsp':'\xA0','nbump':'\u224E\u0338','nbumpe':'\u224F\u0338','ncap':'\u2A43','ncaron':'\u0148','Ncaron':'\u0147','ncedil':'\u0146','Ncedil':'\u0145','ncong':'\u2247','ncongdot':'\u2A6D\u0338','ncup':'\u2A42','ncy':'\u043D','Ncy':'\u041D','ndash':'\u2013','ne':'\u2260','nearhk':'\u2924','nearr':'\u2197','neArr':'\u21D7','nearrow':'\u2197','nedot':'\u2250\u0338','NegativeMediumSpace':'\u200B','NegativeThickSpace':'\u200B','NegativeThinSpace':'\u200B','NegativeVeryThinSpace':'\u200B','nequiv':'\u2262','nesear':'\u2928','nesim':'\u2242\u0338','NestedGreaterGreater':'\u226B','NestedLessLess':'\u226A','NewLine':'\n','nexist':'\u2204','nexists':'\u2204','nfr':'\uD835\uDD2B','Nfr':'\uD835\uDD11','nge':'\u2271','ngE':'\u2267\u0338','ngeq':'\u2271','ngeqq':'\u2267\u0338','ngeqslant':'\u2A7E\u0338','nges':'\u2A7E\u0338','nGg':'\u22D9\u0338','ngsim':'\u2275','ngt':'\u226F','nGt':'\u226B\u20D2','ngtr':'\u226F','nGtv':'\u226B\u0338','nharr':'\u21AE','nhArr':'\u21CE','nhpar':'\u2AF2','ni':'\u220B','nis':'\u22FC','nisd':'\u22FA','niv':'\u220B','njcy':'\u045A','NJcy':'\u040A','nlarr':'\u219A','nlArr':'\u21CD','nldr':'\u2025','nle':'\u2270','nlE':'\u2266\u0338','nleftarrow':'\u219A','nLeftarrow':'\u21CD','nleftrightarrow':'\u21AE','nLeftrightarrow':'\u21CE','nleq':'\u2270','nleqq':'\u2266\u0338','nleqslant':'\u2A7D\u0338','nles':'\u2A7D\u0338','nless':'\u226E','nLl':'\u22D8\u0338','nlsim':'\u2274','nlt':'\u226E','nLt':'\u226A\u20D2','nltri':'\u22EA','nltrie':'\u22EC','nLtv':'\u226A\u0338','nmid':'\u2224','NoBreak':'\u2060','NonBreakingSpace':'\xA0','nopf':'\uD835\uDD5F','Nopf':'\u2115','not':'\xAC','Not':'\u2AEC','NotCongruent':'\u2262','NotCupCap':'\u226D','NotDoubleVerticalBar':'\u2226','NotElement':'\u2209','NotEqual':'\u2260','NotEqualTilde':'\u2242\u0338','NotExists':'\u2204','NotGreater':'\u226F','NotGreaterEqual':'\u2271','NotGreaterFullEqual':'\u2267\u0338','NotGreaterGreater':'\u226B\u0338','NotGreaterLess':'\u2279','NotGreaterSlantEqual':'\u2A7E\u0338','NotGreaterTilde':'\u2275','NotHumpDownHump':'\u224E\u0338','NotHumpEqual':'\u224F\u0338','notin':'\u2209','notindot':'\u22F5\u0338','notinE':'\u22F9\u0338','notinva':'\u2209','notinvb':'\u22F7','notinvc':'\u22F6','NotLeftTriangle':'\u22EA','NotLeftTriangleBar':'\u29CF\u0338','NotLeftTriangleEqual':'\u22EC','NotLess':'\u226E','NotLessEqual':'\u2270','NotLessGreater':'\u2278','NotLessLess':'\u226A\u0338','NotLessSlantEqual':'\u2A7D\u0338','NotLessTilde':'\u2274','NotNestedGreaterGreater':'\u2AA2\u0338','NotNestedLessLess':'\u2AA1\u0338','notni':'\u220C','notniva':'\u220C','notnivb':'\u22FE','notnivc':'\u22FD','NotPrecedes':'\u2280','NotPrecedesEqual':'\u2AAF\u0338','NotPrecedesSlantEqual':'\u22E0','NotReverseElement':'\u220C','NotRightTriangle':'\u22EB','NotRightTriangleBar':'\u29D0\u0338','NotRightTriangleEqual':'\u22ED','NotSquareSubset':'\u228F\u0338','NotSquareSubsetEqual':'\u22E2','NotSquareSuperset':'\u2290\u0338','NotSquareSupersetEqual':'\u22E3','NotSubset':'\u2282\u20D2','NotSubsetEqual':'\u2288','NotSucceeds':'\u2281','NotSucceedsEqual':'\u2AB0\u0338','NotSucceedsSlantEqual':'\u22E1','NotSucceedsTilde':'\u227F\u0338','NotSuperset':'\u2283\u20D2','NotSupersetEqual':'\u2289','NotTilde':'\u2241','NotTildeEqual':'\u2244','NotTildeFullEqual':'\u2247','NotTildeTilde':'\u2249','NotVerticalBar':'\u2224','npar':'\u2226','nparallel':'\u2226','nparsl':'\u2AFD\u20E5','npart':'\u2202\u0338','npolint':'\u2A14','npr':'\u2280','nprcue':'\u22E0','npre':'\u2AAF\u0338','nprec':'\u2280','npreceq':'\u2AAF\u0338','nrarr':'\u219B','nrArr':'\u21CF','nrarrc':'\u2933\u0338','nrarrw':'\u219D\u0338','nrightarrow':'\u219B','nRightarrow':'\u21CF','nrtri':'\u22EB','nrtrie':'\u22ED','nsc':'\u2281','nsccue':'\u22E1','nsce':'\u2AB0\u0338','nscr':'\uD835\uDCC3','Nscr':'\uD835\uDCA9','nshortmid':'\u2224','nshortparallel':'\u2226','nsim':'\u2241','nsime':'\u2244','nsimeq':'\u2244','nsmid':'\u2224','nspar':'\u2226','nsqsube':'\u22E2','nsqsupe':'\u22E3','nsub':'\u2284','nsube':'\u2288','nsubE':'\u2AC5\u0338','nsubset':'\u2282\u20D2','nsubseteq':'\u2288','nsubseteqq':'\u2AC5\u0338','nsucc':'\u2281','nsucceq':'\u2AB0\u0338','nsup':'\u2285','nsupe':'\u2289','nsupE':'\u2AC6\u0338','nsupset':'\u2283\u20D2','nsupseteq':'\u2289','nsupseteqq':'\u2AC6\u0338','ntgl':'\u2279','ntilde':'\xF1','Ntilde':'\xD1','ntlg':'\u2278','ntriangleleft':'\u22EA','ntrianglelefteq':'\u22EC','ntriangleright':'\u22EB','ntrianglerighteq':'\u22ED','nu':'\u03BD','Nu':'\u039D','num':'#','numero':'\u2116','numsp':'\u2007','nvap':'\u224D\u20D2','nvdash':'\u22AC','nvDash':'\u22AD','nVdash':'\u22AE','nVDash':'\u22AF','nvge':'\u2265\u20D2','nvgt':'>\u20D2','nvHarr':'\u2904','nvinfin':'\u29DE','nvlArr':'\u2902','nvle':'\u2264\u20D2','nvlt':'<\u20D2','nvltrie':'\u22B4\u20D2','nvrArr':'\u2903','nvrtrie':'\u22B5\u20D2','nvsim':'\u223C\u20D2','nwarhk':'\u2923','nwarr':'\u2196','nwArr':'\u21D6','nwarrow':'\u2196','nwnear':'\u2927','oacute':'\xF3','Oacute':'\xD3','oast':'\u229B','ocir':'\u229A','ocirc':'\xF4','Ocirc':'\xD4','ocy':'\u043E','Ocy':'\u041E','odash':'\u229D','odblac':'\u0151','Odblac':'\u0150','odiv':'\u2A38','odot':'\u2299','odsold':'\u29BC','oelig':'\u0153','OElig':'\u0152','ofcir':'\u29BF','ofr':'\uD835\uDD2C','Ofr':'\uD835\uDD12','ogon':'\u02DB','ograve':'\xF2','Ograve':'\xD2','ogt':'\u29C1','ohbar':'\u29B5','ohm':'\u03A9','oint':'\u222E','olarr':'\u21BA','olcir':'\u29BE','olcross':'\u29BB','oline':'\u203E','olt':'\u29C0','omacr':'\u014D','Omacr':'\u014C','omega':'\u03C9','Omega':'\u03A9','omicron':'\u03BF','Omicron':'\u039F','omid':'\u29B6','ominus':'\u2296','oopf':'\uD835\uDD60','Oopf':'\uD835\uDD46','opar':'\u29B7','OpenCurlyDoubleQuote':'\u201C','OpenCurlyQuote':'\u2018','operp':'\u29B9','oplus':'\u2295','or':'\u2228','Or':'\u2A54','orarr':'\u21BB','ord':'\u2A5D','order':'\u2134','orderof':'\u2134','ordf':'\xAA','ordm':'\xBA','origof':'\u22B6','oror':'\u2A56','orslope':'\u2A57','orv':'\u2A5B','oS':'\u24C8','oscr':'\u2134','Oscr':'\uD835\uDCAA','oslash':'\xF8','Oslash':'\xD8','osol':'\u2298','otilde':'\xF5','Otilde':'\xD5','otimes':'\u2297','Otimes':'\u2A37','otimesas':'\u2A36','ouml':'\xF6','Ouml':'\xD6','ovbar':'\u233D','OverBar':'\u203E','OverBrace':'\u23DE','OverBracket':'\u23B4','OverParenthesis':'\u23DC','par':'\u2225','para':'\xB6','parallel':'\u2225','parsim':'\u2AF3','parsl':'\u2AFD','part':'\u2202','PartialD':'\u2202','pcy':'\u043F','Pcy':'\u041F','percnt':'%','period':'.','permil':'\u2030','perp':'\u22A5','pertenk':'\u2031','pfr':'\uD835\uDD2D','Pfr':'\uD835\uDD13','phi':'\u03C6','Phi':'\u03A6','phiv':'\u03D5','phmmat':'\u2133','phone':'\u260E','pi':'\u03C0','Pi':'\u03A0','pitchfork':'\u22D4','piv':'\u03D6','planck':'\u210F','planckh':'\u210E','plankv':'\u210F','plus':'+','plusacir':'\u2A23','plusb':'\u229E','pluscir':'\u2A22','plusdo':'\u2214','plusdu':'\u2A25','pluse':'\u2A72','PlusMinus':'\xB1','plusmn':'\xB1','plussim':'\u2A26','plustwo':'\u2A27','pm':'\xB1','Poincareplane':'\u210C','pointint':'\u2A15','popf':'\uD835\uDD61','Popf':'\u2119','pound':'\xA3','pr':'\u227A','Pr':'\u2ABB','prap':'\u2AB7','prcue':'\u227C','pre':'\u2AAF','prE':'\u2AB3','prec':'\u227A','precapprox':'\u2AB7','preccurlyeq':'\u227C','Precedes':'\u227A','PrecedesEqual':'\u2AAF','PrecedesSlantEqual':'\u227C','PrecedesTilde':'\u227E','preceq':'\u2AAF','precnapprox':'\u2AB9','precneqq':'\u2AB5','precnsim':'\u22E8','precsim':'\u227E','prime':'\u2032','Prime':'\u2033','primes':'\u2119','prnap':'\u2AB9','prnE':'\u2AB5','prnsim':'\u22E8','prod':'\u220F','Product':'\u220F','profalar':'\u232E','profline':'\u2312','profsurf':'\u2313','prop':'\u221D','Proportion':'\u2237','Proportional':'\u221D','propto':'\u221D','prsim':'\u227E','prurel':'\u22B0','pscr':'\uD835\uDCC5','Pscr':'\uD835\uDCAB','psi':'\u03C8','Psi':'\u03A8','puncsp':'\u2008','qfr':'\uD835\uDD2E','Qfr':'\uD835\uDD14','qint':'\u2A0C','qopf':'\uD835\uDD62','Qopf':'\u211A','qprime':'\u2057','qscr':'\uD835\uDCC6','Qscr':'\uD835\uDCAC','quaternions':'\u210D','quatint':'\u2A16','quest':'?','questeq':'\u225F','quot':'"','QUOT':'"','rAarr':'\u21DB','race':'\u223D\u0331','racute':'\u0155','Racute':'\u0154','radic':'\u221A','raemptyv':'\u29B3','rang':'\u27E9','Rang':'\u27EB','rangd':'\u2992','range':'\u29A5','rangle':'\u27E9','raquo':'\xBB','rarr':'\u2192','rArr':'\u21D2','Rarr':'\u21A0','rarrap':'\u2975','rarrb':'\u21E5','rarrbfs':'\u2920','rarrc':'\u2933','rarrfs':'\u291E','rarrhk':'\u21AA','rarrlp':'\u21AC','rarrpl':'\u2945','rarrsim':'\u2974','rarrtl':'\u21A3','Rarrtl':'\u2916','rarrw':'\u219D','ratail':'\u291A','rAtail':'\u291C','ratio':'\u2236','rationals':'\u211A','rbarr':'\u290D','rBarr':'\u290F','RBarr':'\u2910','rbbrk':'\u2773','rbrace':'}','rbrack':']','rbrke':'\u298C','rbrksld':'\u298E','rbrkslu':'\u2990','rcaron':'\u0159','Rcaron':'\u0158','rcedil':'\u0157','Rcedil':'\u0156','rceil':'\u2309','rcub':'}','rcy':'\u0440','Rcy':'\u0420','rdca':'\u2937','rdldhar':'\u2969','rdquo':'\u201D','rdquor':'\u201D','rdsh':'\u21B3','Re':'\u211C','real':'\u211C','realine':'\u211B','realpart':'\u211C','reals':'\u211D','rect':'\u25AD','reg':'\xAE','REG':'\xAE','ReverseElement':'\u220B','ReverseEquilibrium':'\u21CB','ReverseUpEquilibrium':'\u296F','rfisht':'\u297D','rfloor':'\u230B','rfr':'\uD835\uDD2F','Rfr':'\u211C','rHar':'\u2964','rhard':'\u21C1','rharu':'\u21C0','rharul':'\u296C','rho':'\u03C1','Rho':'\u03A1','rhov':'\u03F1','RightAngleBracket':'\u27E9','rightarrow':'\u2192','Rightarrow':'\u21D2','RightArrow':'\u2192','RightArrowBar':'\u21E5','RightArrowLeftArrow':'\u21C4','rightarrowtail':'\u21A3','RightCeiling':'\u2309','RightDoubleBracket':'\u27E7','RightDownTeeVector':'\u295D','RightDownVector':'\u21C2','RightDownVectorBar':'\u2955','RightFloor':'\u230B','rightharpoondown':'\u21C1','rightharpoonup':'\u21C0','rightleftarrows':'\u21C4','rightleftharpoons':'\u21CC','rightrightarrows':'\u21C9','rightsquigarrow':'\u219D','RightTee':'\u22A2','RightTeeArrow':'\u21A6','RightTeeVector':'\u295B','rightthreetimes':'\u22CC','RightTriangle':'\u22B3','RightTriangleBar':'\u29D0','RightTriangleEqual':'\u22B5','RightUpDownVector':'\u294F','RightUpTeeVector':'\u295C','RightUpVector':'\u21BE','RightUpVectorBar':'\u2954','RightVector':'\u21C0','RightVectorBar':'\u2953','ring':'\u02DA','risingdotseq':'\u2253','rlarr':'\u21C4','rlhar':'\u21CC','rlm':'\u200F','rmoust':'\u23B1','rmoustache':'\u23B1','rnmid':'\u2AEE','roang':'\u27ED','roarr':'\u21FE','robrk':'\u27E7','ropar':'\u2986','ropf':'\uD835\uDD63','Ropf':'\u211D','roplus':'\u2A2E','rotimes':'\u2A35','RoundImplies':'\u2970','rpar':')','rpargt':'\u2994','rppolint':'\u2A12','rrarr':'\u21C9','Rrightarrow':'\u21DB','rsaquo':'\u203A','rscr':'\uD835\uDCC7','Rscr':'\u211B','rsh':'\u21B1','Rsh':'\u21B1','rsqb':']','rsquo':'\u2019','rsquor':'\u2019','rthree':'\u22CC','rtimes':'\u22CA','rtri':'\u25B9','rtrie':'\u22B5','rtrif':'\u25B8','rtriltri':'\u29CE','RuleDelayed':'\u29F4','ruluhar':'\u2968','rx':'\u211E','sacute':'\u015B','Sacute':'\u015A','sbquo':'\u201A','sc':'\u227B','Sc':'\u2ABC','scap':'\u2AB8','scaron':'\u0161','Scaron':'\u0160','sccue':'\u227D','sce':'\u2AB0','scE':'\u2AB4','scedil':'\u015F','Scedil':'\u015E','scirc':'\u015D','Scirc':'\u015C','scnap':'\u2ABA','scnE':'\u2AB6','scnsim':'\u22E9','scpolint':'\u2A13','scsim':'\u227F','scy':'\u0441','Scy':'\u0421','sdot':'\u22C5','sdotb':'\u22A1','sdote':'\u2A66','searhk':'\u2925','searr':'\u2198','seArr':'\u21D8','searrow':'\u2198','sect':'\xA7','semi':';','seswar':'\u2929','setminus':'\u2216','setmn':'\u2216','sext':'\u2736','sfr':'\uD835\uDD30','Sfr':'\uD835\uDD16','sfrown':'\u2322','sharp':'\u266F','shchcy':'\u0449','SHCHcy':'\u0429','shcy':'\u0448','SHcy':'\u0428','ShortDownArrow':'\u2193','ShortLeftArrow':'\u2190','shortmid':'\u2223','shortparallel':'\u2225','ShortRightArrow':'\u2192','ShortUpArrow':'\u2191','shy':'\xAD','sigma':'\u03C3','Sigma':'\u03A3','sigmaf':'\u03C2','sigmav':'\u03C2','sim':'\u223C','simdot':'\u2A6A','sime':'\u2243','simeq':'\u2243','simg':'\u2A9E','simgE':'\u2AA0','siml':'\u2A9D','simlE':'\u2A9F','simne':'\u2246','simplus':'\u2A24','simrarr':'\u2972','slarr':'\u2190','SmallCircle':'\u2218','smallsetminus':'\u2216','smashp':'\u2A33','smeparsl':'\u29E4','smid':'\u2223','smile':'\u2323','smt':'\u2AAA','smte':'\u2AAC','smtes':'\u2AAC\uFE00','softcy':'\u044C','SOFTcy':'\u042C','sol':'/','solb':'\u29C4','solbar':'\u233F','sopf':'\uD835\uDD64','Sopf':'\uD835\uDD4A','spades':'\u2660','spadesuit':'\u2660','spar':'\u2225','sqcap':'\u2293','sqcaps':'\u2293\uFE00','sqcup':'\u2294','sqcups':'\u2294\uFE00','Sqrt':'\u221A','sqsub':'\u228F','sqsube':'\u2291','sqsubset':'\u228F','sqsubseteq':'\u2291','sqsup':'\u2290','sqsupe':'\u2292','sqsupset':'\u2290','sqsupseteq':'\u2292','squ':'\u25A1','square':'\u25A1','Square':'\u25A1','SquareIntersection':'\u2293','SquareSubset':'\u228F','SquareSubsetEqual':'\u2291','SquareSuperset':'\u2290','SquareSupersetEqual':'\u2292','SquareUnion':'\u2294','squarf':'\u25AA','squf':'\u25AA','srarr':'\u2192','sscr':'\uD835\uDCC8','Sscr':'\uD835\uDCAE','ssetmn':'\u2216','ssmile':'\u2323','sstarf':'\u22C6','star':'\u2606','Star':'\u22C6','starf':'\u2605','straightepsilon':'\u03F5','straightphi':'\u03D5','strns':'\xAF','sub':'\u2282','Sub':'\u22D0','subdot':'\u2ABD','sube':'\u2286','subE':'\u2AC5','subedot':'\u2AC3','submult':'\u2AC1','subne':'\u228A','subnE':'\u2ACB','subplus':'\u2ABF','subrarr':'\u2979','subset':'\u2282','Subset':'\u22D0','subseteq':'\u2286','subseteqq':'\u2AC5','SubsetEqual':'\u2286','subsetneq':'\u228A','subsetneqq':'\u2ACB','subsim':'\u2AC7','subsub':'\u2AD5','subsup':'\u2AD3','succ':'\u227B','succapprox':'\u2AB8','succcurlyeq':'\u227D','Succeeds':'\u227B','SucceedsEqual':'\u2AB0','SucceedsSlantEqual':'\u227D','SucceedsTilde':'\u227F','succeq':'\u2AB0','succnapprox':'\u2ABA','succneqq':'\u2AB6','succnsim':'\u22E9','succsim':'\u227F','SuchThat':'\u220B','sum':'\u2211','Sum':'\u2211','sung':'\u266A','sup':'\u2283','Sup':'\u22D1','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','supdot':'\u2ABE','supdsub':'\u2AD8','supe':'\u2287','supE':'\u2AC6','supedot':'\u2AC4','Superset':'\u2283','SupersetEqual':'\u2287','suphsol':'\u27C9','suphsub':'\u2AD7','suplarr':'\u297B','supmult':'\u2AC2','supne':'\u228B','supnE':'\u2ACC','supplus':'\u2AC0','supset':'\u2283','Supset':'\u22D1','supseteq':'\u2287','supseteqq':'\u2AC6','supsetneq':'\u228B','supsetneqq':'\u2ACC','supsim':'\u2AC8','supsub':'\u2AD4','supsup':'\u2AD6','swarhk':'\u2926','swarr':'\u2199','swArr':'\u21D9','swarrow':'\u2199','swnwar':'\u292A','szlig':'\xDF','Tab':'\t','target':'\u2316','tau':'\u03C4','Tau':'\u03A4','tbrk':'\u23B4','tcaron':'\u0165','Tcaron':'\u0164','tcedil':'\u0163','Tcedil':'\u0162','tcy':'\u0442','Tcy':'\u0422','tdot':'\u20DB','telrec':'\u2315','tfr':'\uD835\uDD31','Tfr':'\uD835\uDD17','there4':'\u2234','therefore':'\u2234','Therefore':'\u2234','theta':'\u03B8','Theta':'\u0398','thetasym':'\u03D1','thetav':'\u03D1','thickapprox':'\u2248','thicksim':'\u223C','ThickSpace':'\u205F\u200A','thinsp':'\u2009','ThinSpace':'\u2009','thkap':'\u2248','thksim':'\u223C','thorn':'\xFE','THORN':'\xDE','tilde':'\u02DC','Tilde':'\u223C','TildeEqual':'\u2243','TildeFullEqual':'\u2245','TildeTilde':'\u2248','times':'\xD7','timesb':'\u22A0','timesbar':'\u2A31','timesd':'\u2A30','tint':'\u222D','toea':'\u2928','top':'\u22A4','topbot':'\u2336','topcir':'\u2AF1','topf':'\uD835\uDD65','Topf':'\uD835\uDD4B','topfork':'\u2ADA','tosa':'\u2929','tprime':'\u2034','trade':'\u2122','TRADE':'\u2122','triangle':'\u25B5','triangledown':'\u25BF','triangleleft':'\u25C3','trianglelefteq':'\u22B4','triangleq':'\u225C','triangleright':'\u25B9','trianglerighteq':'\u22B5','tridot':'\u25EC','trie':'\u225C','triminus':'\u2A3A','TripleDot':'\u20DB','triplus':'\u2A39','trisb':'\u29CD','tritime':'\u2A3B','trpezium':'\u23E2','tscr':'\uD835\uDCC9','Tscr':'\uD835\uDCAF','tscy':'\u0446','TScy':'\u0426','tshcy':'\u045B','TSHcy':'\u040B','tstrok':'\u0167','Tstrok':'\u0166','twixt':'\u226C','twoheadleftarrow':'\u219E','twoheadrightarrow':'\u21A0','uacute':'\xFA','Uacute':'\xDA','uarr':'\u2191','uArr':'\u21D1','Uarr':'\u219F','Uarrocir':'\u2949','ubrcy':'\u045E','Ubrcy':'\u040E','ubreve':'\u016D','Ubreve':'\u016C','ucirc':'\xFB','Ucirc':'\xDB','ucy':'\u0443','Ucy':'\u0423','udarr':'\u21C5','udblac':'\u0171','Udblac':'\u0170','udhar':'\u296E','ufisht':'\u297E','ufr':'\uD835\uDD32','Ufr':'\uD835\uDD18','ugrave':'\xF9','Ugrave':'\xD9','uHar':'\u2963','uharl':'\u21BF','uharr':'\u21BE','uhblk':'\u2580','ulcorn':'\u231C','ulcorner':'\u231C','ulcrop':'\u230F','ultri':'\u25F8','umacr':'\u016B','Umacr':'\u016A','uml':'\xA8','UnderBar':'_','UnderBrace':'\u23DF','UnderBracket':'\u23B5','UnderParenthesis':'\u23DD','Union':'\u22C3','UnionPlus':'\u228E','uogon':'\u0173','Uogon':'\u0172','uopf':'\uD835\uDD66','Uopf':'\uD835\uDD4C','uparrow':'\u2191','Uparrow':'\u21D1','UpArrow':'\u2191','UpArrowBar':'\u2912','UpArrowDownArrow':'\u21C5','updownarrow':'\u2195','Updownarrow':'\u21D5','UpDownArrow':'\u2195','UpEquilibrium':'\u296E','upharpoonleft':'\u21BF','upharpoonright':'\u21BE','uplus':'\u228E','UpperLeftArrow':'\u2196','UpperRightArrow':'\u2197','upsi':'\u03C5','Upsi':'\u03D2','upsih':'\u03D2','upsilon':'\u03C5','Upsilon':'\u03A5','UpTee':'\u22A5','UpTeeArrow':'\u21A5','upuparrows':'\u21C8','urcorn':'\u231D','urcorner':'\u231D','urcrop':'\u230E','uring':'\u016F','Uring':'\u016E','urtri':'\u25F9','uscr':'\uD835\uDCCA','Uscr':'\uD835\uDCB0','utdot':'\u22F0','utilde':'\u0169','Utilde':'\u0168','utri':'\u25B5','utrif':'\u25B4','uuarr':'\u21C8','uuml':'\xFC','Uuml':'\xDC','uwangle':'\u29A7','vangrt':'\u299C','varepsilon':'\u03F5','varkappa':'\u03F0','varnothing':'\u2205','varphi':'\u03D5','varpi':'\u03D6','varpropto':'\u221D','varr':'\u2195','vArr':'\u21D5','varrho':'\u03F1','varsigma':'\u03C2','varsubsetneq':'\u228A\uFE00','varsubsetneqq':'\u2ACB\uFE00','varsupsetneq':'\u228B\uFE00','varsupsetneqq':'\u2ACC\uFE00','vartheta':'\u03D1','vartriangleleft':'\u22B2','vartriangleright':'\u22B3','vBar':'\u2AE8','Vbar':'\u2AEB','vBarv':'\u2AE9','vcy':'\u0432','Vcy':'\u0412','vdash':'\u22A2','vDash':'\u22A8','Vdash':'\u22A9','VDash':'\u22AB','Vdashl':'\u2AE6','vee':'\u2228','Vee':'\u22C1','veebar':'\u22BB','veeeq':'\u225A','vellip':'\u22EE','verbar':'|','Verbar':'\u2016','vert':'|','Vert':'\u2016','VerticalBar':'\u2223','VerticalLine':'|','VerticalSeparator':'\u2758','VerticalTilde':'\u2240','VeryThinSpace':'\u200A','vfr':'\uD835\uDD33','Vfr':'\uD835\uDD19','vltri':'\u22B2','vnsub':'\u2282\u20D2','vnsup':'\u2283\u20D2','vopf':'\uD835\uDD67','Vopf':'\uD835\uDD4D','vprop':'\u221D','vrtri':'\u22B3','vscr':'\uD835\uDCCB','Vscr':'\uD835\uDCB1','vsubne':'\u228A\uFE00','vsubnE':'\u2ACB\uFE00','vsupne':'\u228B\uFE00','vsupnE':'\u2ACC\uFE00','Vvdash':'\u22AA','vzigzag':'\u299A','wcirc':'\u0175','Wcirc':'\u0174','wedbar':'\u2A5F','wedge':'\u2227','Wedge':'\u22C0','wedgeq':'\u2259','weierp':'\u2118','wfr':'\uD835\uDD34','Wfr':'\uD835\uDD1A','wopf':'\uD835\uDD68','Wopf':'\uD835\uDD4E','wp':'\u2118','wr':'\u2240','wreath':'\u2240','wscr':'\uD835\uDCCC','Wscr':'\uD835\uDCB2','xcap':'\u22C2','xcirc':'\u25EF','xcup':'\u22C3','xdtri':'\u25BD','xfr':'\uD835\uDD35','Xfr':'\uD835\uDD1B','xharr':'\u27F7','xhArr':'\u27FA','xi':'\u03BE','Xi':'\u039E','xlarr':'\u27F5','xlArr':'\u27F8','xmap':'\u27FC','xnis':'\u22FB','xodot':'\u2A00','xopf':'\uD835\uDD69','Xopf':'\uD835\uDD4F','xoplus':'\u2A01','xotime':'\u2A02','xrarr':'\u27F6','xrArr':'\u27F9','xscr':'\uD835\uDCCD','Xscr':'\uD835\uDCB3','xsqcup':'\u2A06','xuplus':'\u2A04','xutri':'\u25B3','xvee':'\u22C1','xwedge':'\u22C0','yacute':'\xFD','Yacute':'\xDD','yacy':'\u044F','YAcy':'\u042F','ycirc':'\u0177','Ycirc':'\u0176','ycy':'\u044B','Ycy':'\u042B','yen':'\xA5','yfr':'\uD835\uDD36','Yfr':'\uD835\uDD1C','yicy':'\u0457','YIcy':'\u0407','yopf':'\uD835\uDD6A','Yopf':'\uD835\uDD50','yscr':'\uD835\uDCCE','Yscr':'\uD835\uDCB4','yucy':'\u044E','YUcy':'\u042E','yuml':'\xFF','Yuml':'\u0178','zacute':'\u017A','Zacute':'\u0179','zcaron':'\u017E','Zcaron':'\u017D','zcy':'\u0437','Zcy':'\u0417','zdot':'\u017C','Zdot':'\u017B','zeetrf':'\u2128','ZeroWidthSpace':'\u200B','zeta':'\u03B6','Zeta':'\u0396','zfr':'\uD835\uDD37','Zfr':'\u2128','zhcy':'\u0436','ZHcy':'\u0416','zigrarr':'\u21DD','zopf':'\uD835\uDD6B','Zopf':'\u2124','zscr':'\uD835\uDCCF','Zscr':'\uD835\uDCB5','zwj':'\u200D','zwnj':'\u200C'}; - var decodeMapLegacy = {'aacute':'\xE1','Aacute':'\xC1','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','aelig':'\xE6','AElig':'\xC6','agrave':'\xE0','Agrave':'\xC0','amp':'&','AMP':'&','aring':'\xE5','Aring':'\xC5','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','brvbar':'\xA6','ccedil':'\xE7','Ccedil':'\xC7','cedil':'\xB8','cent':'\xA2','copy':'\xA9','COPY':'\xA9','curren':'\xA4','deg':'\xB0','divide':'\xF7','eacute':'\xE9','Eacute':'\xC9','ecirc':'\xEA','Ecirc':'\xCA','egrave':'\xE8','Egrave':'\xC8','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','frac12':'\xBD','frac14':'\xBC','frac34':'\xBE','gt':'>','GT':'>','iacute':'\xED','Iacute':'\xCD','icirc':'\xEE','Icirc':'\xCE','iexcl':'\xA1','igrave':'\xEC','Igrave':'\xCC','iquest':'\xBF','iuml':'\xEF','Iuml':'\xCF','laquo':'\xAB','lt':'<','LT':'<','macr':'\xAF','micro':'\xB5','middot':'\xB7','nbsp':'\xA0','not':'\xAC','ntilde':'\xF1','Ntilde':'\xD1','oacute':'\xF3','Oacute':'\xD3','ocirc':'\xF4','Ocirc':'\xD4','ograve':'\xF2','Ograve':'\xD2','ordf':'\xAA','ordm':'\xBA','oslash':'\xF8','Oslash':'\xD8','otilde':'\xF5','Otilde':'\xD5','ouml':'\xF6','Ouml':'\xD6','para':'\xB6','plusmn':'\xB1','pound':'\xA3','quot':'"','QUOT':'"','raquo':'\xBB','reg':'\xAE','REG':'\xAE','sect':'\xA7','shy':'\xAD','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','szlig':'\xDF','thorn':'\xFE','THORN':'\xDE','times':'\xD7','uacute':'\xFA','Uacute':'\xDA','ucirc':'\xFB','Ucirc':'\xDB','ugrave':'\xF9','Ugrave':'\xD9','uml':'\xA8','uuml':'\xFC','Uuml':'\xDC','yacute':'\xFD','Yacute':'\xDD','yen':'\xA5','yuml':'\xFF'}; - var decodeMapNumeric = {'0':'\uFFFD','128':'\u20AC','130':'\u201A','131':'\u0192','132':'\u201E','133':'\u2026','134':'\u2020','135':'\u2021','136':'\u02C6','137':'\u2030','138':'\u0160','139':'\u2039','140':'\u0152','142':'\u017D','145':'\u2018','146':'\u2019','147':'\u201C','148':'\u201D','149':'\u2022','150':'\u2013','151':'\u2014','152':'\u02DC','153':'\u2122','154':'\u0161','155':'\u203A','156':'\u0153','158':'\u017E','159':'\u0178'}; - var invalidReferenceCodePoints = [1,2,3,4,5,6,7,8,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,64976,64977,64978,64979,64980,64981,64982,64983,64984,64985,64986,64987,64988,64989,64990,64991,64992,64993,64994,64995,64996,64997,64998,64999,65000,65001,65002,65003,65004,65005,65006,65007,65534,65535,131070,131071,196606,196607,262142,262143,327678,327679,393214,393215,458750,458751,524286,524287,589822,589823,655358,655359,720894,720895,786430,786431,851966,851967,917502,917503,983038,983039,1048574,1048575,1114110,1114111]; - - /*--------------------------------------------------------------------------*/ - - var stringFromCharCode = String.fromCharCode; - - var object = {}; - var hasOwnProperty = object.hasOwnProperty; - var has = function(object, propertyName) { - return hasOwnProperty.call(object, propertyName); - }; - - var contains = function(array, value) { - var index = -1; - var length = array.length; - while (++index < length) { - if (array[index] == value) { - return true; - } - } - return false; - }; - - var merge = function(options, defaults) { - if (!options) { - return defaults; - } - var result = {}; - var key; - for (key in defaults) { - // A `hasOwnProperty` check is not needed here, since only recognized - // option names are used anyway. Any others are ignored. - result[key] = has(options, key) ? options[key] : defaults[key]; - } - return result; - }; - - // Modified version of `ucs2encode`; see https://mths.be/punycode. - var codePointToSymbol = function(codePoint, strict) { - var output = ''; - if ((codePoint >= 0xD800 && codePoint <= 0xDFFF) || codePoint > 0x10FFFF) { - // See issue #4: - // “Otherwise, if the number is in the range 0xD800 to 0xDFFF or is - // greater than 0x10FFFF, then this is a parse error. Return a U+FFFD - // REPLACEMENT CHARACTER.” - if (strict) { - parseError('character reference outside the permissible Unicode range'); - } - return '\uFFFD'; - } - if (has(decodeMapNumeric, codePoint)) { - if (strict) { - parseError('disallowed character reference'); - } - return decodeMapNumeric[codePoint]; - } - if (strict && contains(invalidReferenceCodePoints, codePoint)) { - parseError('disallowed character reference'); - } - if (codePoint > 0xFFFF) { - codePoint -= 0x10000; - output += stringFromCharCode(codePoint >>> 10 & 0x3FF | 0xD800); - codePoint = 0xDC00 | codePoint & 0x3FF; - } - output += stringFromCharCode(codePoint); - return output; - }; - - var hexEscape = function(codePoint) { - return '&#x' + codePoint.toString(16).toUpperCase() + ';'; - }; - - var decEscape = function(codePoint) { - return '&#' + codePoint + ';'; - }; - - var parseError = function(message) { - throw Error('Parse error: ' + message); - }; - - /*--------------------------------------------------------------------------*/ - - var encode = function(string, options) { - options = merge(options, encode.options); - var strict = options.strict; - if (strict && regexInvalidRawCodePoint.test(string)) { - parseError('forbidden code point'); - } - var encodeEverything = options.encodeEverything; - var useNamedReferences = options.useNamedReferences; - var allowUnsafeSymbols = options.allowUnsafeSymbols; - var escapeCodePoint = options.decimal ? decEscape : hexEscape; - - var escapeBmpSymbol = function(symbol) { - return escapeCodePoint(symbol.charCodeAt(0)); - }; - - if (encodeEverything) { - // Encode ASCII symbols. - string = string.replace(regexAsciiWhitelist, function(symbol) { - // Use named references if requested & possible. - if (useNamedReferences && has(encodeMap, symbol)) { - return '&' + encodeMap[symbol] + ';'; - } - return escapeBmpSymbol(symbol); - }); - // Shorten a few escapes that represent two symbols, of which at least one - // is within the ASCII range. - if (useNamedReferences) { - string = string - .replace(/>\u20D2/g, '>⃒') - .replace(/<\u20D2/g, '<⃒') - .replace(/fj/g, 'fj'); - } - // Encode non-ASCII symbols. - if (useNamedReferences) { - // Encode non-ASCII symbols that can be replaced with a named reference. - string = string.replace(regexEncodeNonAscii, function(string) { - // Note: there is no need to check `has(encodeMap, string)` here. - return '&' + encodeMap[string] + ';'; - }); - } - // Note: any remaining non-ASCII symbols are handled outside of the `if`. - } else if (useNamedReferences) { - // Apply named character references. - // Encode `<>"'&` using named character references. - if (!allowUnsafeSymbols) { - string = string.replace(regexEscape, function(string) { - return '&' + encodeMap[string] + ';'; // no need to check `has()` here - }); - } - // Shorten escapes that represent two symbols, of which at least one is - // `<>"'&`. - string = string - .replace(/>\u20D2/g, '>⃒') - .replace(/<\u20D2/g, '<⃒'); - // Encode non-ASCII symbols that can be replaced with a named reference. - string = string.replace(regexEncodeNonAscii, function(string) { - // Note: there is no need to check `has(encodeMap, string)` here. - return '&' + encodeMap[string] + ';'; - }); - } else if (!allowUnsafeSymbols) { - // Encode `<>"'&` using hexadecimal escapes, now that they’re not handled - // using named character references. - string = string.replace(regexEscape, escapeBmpSymbol); - } - return string - // Encode astral symbols. - .replace(regexAstralSymbols, function($0) { - // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae - var high = $0.charCodeAt(0); - var low = $0.charCodeAt(1); - var codePoint = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000; - return escapeCodePoint(codePoint); - }) - // Encode any remaining BMP symbols that are not printable ASCII symbols - // using a hexadecimal escape. - .replace(regexBmpWhitelist, escapeBmpSymbol); - }; - // Expose default options (so they can be overridden globally). - encode.options = { - 'allowUnsafeSymbols': false, - 'encodeEverything': false, - 'strict': false, - 'useNamedReferences': false, - 'decimal' : false - }; - - var decode = function(html, options) { - options = merge(options, decode.options); - var strict = options.strict; - if (strict && regexInvalidEntity.test(html)) { - parseError('malformed character reference'); - } - return html.replace(regexDecode, function($0, $1, $2, $3, $4, $5, $6, $7) { - var codePoint; - var semicolon; - var decDigits; - var hexDigits; - var reference; - var next; - if ($1) { - // Decode decimal escapes, e.g. `𝌆`. - decDigits = $1; - semicolon = $2; - if (strict && !semicolon) { - parseError('character reference was not terminated by a semicolon'); - } - codePoint = parseInt(decDigits, 10); - return codePointToSymbol(codePoint, strict); - } - if ($3) { - // Decode hexadecimal escapes, e.g. `𝌆`. - hexDigits = $3; - semicolon = $4; - if (strict && !semicolon) { - parseError('character reference was not terminated by a semicolon'); - } - codePoint = parseInt(hexDigits, 16); - return codePointToSymbol(codePoint, strict); - } - if ($5) { - // Decode named character references with trailing `;`, e.g. `©`. - reference = $5; - if (has(decodeMap, reference)) { - return decodeMap[reference]; - } else { - // Ambiguous ampersand. https://mths.be/notes/ambiguous-ampersands - if (strict) { - parseError( - 'named character reference was not terminated by a semicolon' - ); - } - return $0; - } - } - // If we’re still here, it’s a legacy reference for sure. No need for an - // extra `if` check. - // Decode named character references without trailing `;`, e.g. `&` - // This is only a parse error if it gets converted to `&`, or if it is - // followed by `=` in an attribute context. - reference = $6; - next = $7; - if (next && options.isAttributeValue) { - if (strict && next == '=') { - parseError('`&` did not start a character reference'); - } - return $0; - } else { - if (strict) { - parseError( - 'named character reference was not terminated by a semicolon' - ); - } - // Note: there is no need to check `has(decodeMapLegacy, reference)`. - return decodeMapLegacy[reference] + (next || ''); - } - }); - }; - // Expose default options (so they can be overridden globally). - decode.options = { - 'isAttributeValue': false, - 'strict': false - }; - - var escape = function(string) { - return string.replace(regexEscape, function($0) { - // Note: there is no need to check `has(escapeMap, $0)` here. - return escapeMap[$0]; - }); - }; - - /*--------------------------------------------------------------------------*/ - - var he = { - 'version': '1.1.1', - 'encode': encode, - 'decode': decode, - 'escape': escape, - 'unescape': decode - }; - - // Some AMD build optimizers, like r.js, check for specific condition patterns - // like the following: - if ( - typeof undefined == 'function' && - typeof undefined.amd == 'object' && - undefined.amd - ) { - undefined(function() { - return he; - }); - } else if (freeExports && !freeExports.nodeType) { - if (freeModule) { // in Node.js, io.js, or RingoJS v0.8.0+ - freeModule.exports = he; - } else { // in Narwhal or RingoJS v0.7.0- - for (var key in he) { - has(he, key) && (freeExports[key] = he[key]); - } - } - } else { // in Rhino or a web browser - root.he = he; - } - -}(commonjsGlobal)); -}); - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - 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 () { - return str.charCodeAt(++index) -} - -function eof () { - return index >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var 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) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn$1; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn$1 = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn$1(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn$1, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - "development" !== 'production' && warn$1( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - 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.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if ("development" !== 'production' && el.tag === 'template') { - warn$1("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - "development" !== 'production' && warn$1( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else { - warn$1( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if ("development" !== 'production' && children[i].text !== ' ') { - warn$1( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if ("development" !== 'production' && el.key) { - warn$1( - "`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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if ("development" !== 'production' && slotScope) { - warn$1( - "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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // 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); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn$1); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if ("development" !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - { - var expression = parseText(value, delimiters); - if (expression) { - warn$1( - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - // #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'); - } - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - "development" !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn$1('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _el = el; - while (_el) { - if (_el.for && _el.alias === value) { - warn$1( - "<" + (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 = _el.parent; - } -} - -/* */ - -/** - * Expand input[v-model] with dyanmic 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]"> - */ - -function preTransformNode (el, options) { - if (el.tag === 'input') { - var map = el.attrsMap; - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - var typeBinding = getBindingAttr(el, 'type'); - var ifCondition = getAndRemoveAttr(el, 'v-if', true); - var ifConditionExtra = ifCondition ? ("&&(" + ifCondition + ")") : ""; - var hasElse = getAndRemoveAttr(el, 'v-else', true) != null; - var elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true); - // 1. checkbox - var 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 - var 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 - var 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value; - el.attrsList.push({ name: name, value: value }); -} - -var model = { - preTransformNode: preTransformNode -}; - -var modules = [ - klass, - style, - model -]; - -/* */ - -var warn$2; - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; - - -function model$1 ( - el, - dir, - _warn -) { - warn$2 = _warn; - var value = dir.value; - var modifiers = dir.modifiers; - var tag = el.tag; - var type = el.attrsMap.type; - - { - // inputs with type="file" are read only and setting the input's - // value will throw an error. - if (tag === 'input' && type === 'file') { - warn$2( - "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" + - "File inputs are read only. Use a v-on:change listener instead." - ); - } - } - - 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 { - warn$2( - "<" + (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.' - ); - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var valueBinding = getBindingAttr(el, 'value') || 'null'; - var trueValueBinding = getBindingAttr(el, 'true-value') || 'true'; - var 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&&(" + value + "=$$a.concat([$$v]))}" + - "else{$$i>-1&&(" + value + "=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}" + - "}else{" + (genAssignmentCode(value, '$$c')) + "}", - null, true - ); -} - -function genRadioModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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') + "})"; - - var assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'; - var code = "var $$selectedVal = " + selectedVal + ";"; - code = code + " " + (genAssignmentCode(value, assignment)); - addHandler(el, 'change', code, null, true); -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var type = el.attrsMap.type; - var ref = modifiers || {}; - var lazy = ref.lazy; - var number = ref.number; - var trim = ref.trim; - var needCompositionGuard = !lazy && type !== 'range'; - var event = lazy - ? 'change' - : type === 'range' - ? RANGE_TOKEN - : 'input'; - - var valueExpression = '$event.target.value'; - if (trim) { - valueExpression = "$event.target.value.trim()"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var 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()'); - } -} - -/* */ - -function text (el, dir) { - if (dir.value) { - addProp(el, 'textContent', ("_s(" + (dir.value) + ")")); - } -} - -/* */ - -function html (el, dir) { - if (dir.value) { - addProp(el, 'innerHTML', ("_s(" + (dir.value) + ")")); - } -} - -var directives = { - model: model$1, - text: text, - html: html -}; - -/* */ - -var baseOptions = { - expectHTML: true, - modules: modules, - directives: directives, - isPreTag: isPreTag, - isUnaryTag: isUnaryTag, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - staticKeys: genStaticKeys(modules) -}; - -/* */ - -var isStaticKey; -var isPlatformReservedTag; - -var genStaticKeysCached = cached(genStaticKeys$1); - -/** - * 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. - */ -function optimize (root, options) { - 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$1 (keys) { - return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + - (keys ? ',' + keys : '') - ) -} - -function markStatic (node) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - markStatic(child); - if (!child.static) { - node.static = false; - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].block; - markStatic(block); - if (!block.static) { - node.static = false; - } - } - } - } -} - -function markStaticRoots (node, isInFor) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - markStaticRoots(node.children[i], isInFor || !!node.for); - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - markStaticRoots(node.ifConditions[i$1].block, isInFor); - } - } - } -} - -function isStatic (node) { - 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) { - while (node.parent) { - node = node.parent; - if (node.tag !== 'template') { - return false - } - if (node.for) { - return true - } - } - return false -} - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if ("development" !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -function on (el, dir) { - if ("development" !== 'production' && dir.modifiers) { - warn("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData$2(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - "development" !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if ("development" !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData$2 (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if ("development" !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn; - delete options.warn; - - /* istanbul ignore if */ - { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -// `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. -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref = createCompiler(baseOptions); -var compile = ref.compile; -var compileToFunctions = ref.compileToFunctions; - -/* */ - -var 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,http-equiv,' + - 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + - 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,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,type,usemap,value,width,wrap' -); - -/* istanbul ignore next */ -var isRenderableAttr = function (name) { - return ( - isAttr(name) || - name.indexOf('data-') === 0 || - name.indexOf('aria-') === 0 - ) -}; -var propsToAttrMap = { - acceptCharset: 'accept-charset', - className: 'class', - htmlFor: 'for', - httpEquiv: 'http-equiv' -}; - -var ESC = { - '<': '<', - '>': '>', - '"': '"', - '&': '&' -}; - -function escape (s) { - return s.replace(/[<>"&]/g, escapeChar) -} - -function escapeChar (a) { - return ESC[a] || a -} - -/* */ - -var plainStringRE = /^"(?:[^"\\]|\\.)*"$|^'(?:[^'\\]|\\.)*'$/; - -// let the model AST transform translate v-model into appropriate -// props bindings -function applyModelTransform (el, state) { - if (el.directives) { - for (var i = 0; i < el.directives.length; i++) { - var 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(function (p) { return p.name !== 'value'; }); - } - break - } - } - } -} - -function genAttrSegments ( - attrs -) { - return attrs.map(function (ref) { - var name = ref.name; - var value = ref.value; - - return genAttrSegment(name, value); - }) -} - -function genDOMPropSegments ( - props, - attrs -) { - var segments = []; - props.forEach(function (ref) { - var name = ref.name; - var value = ref.value; - - name = propsToAttrMap[name] || name.toLowerCase(); - if (isRenderableAttr(name) && - !(attrs && attrs.some(function (a) { return a.name === name; })) - ) { - segments.push(genAttrSegment(name, value)); - } - }); - return segments -} - -function genAttrSegment (name, value) { - 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 + "=" + value) - } - } else { - return { - type: EXPRESSION, - value: ("_ssrAttr(" + (JSON.stringify(name)) + "," + value + ")") - } - } -} - -function genClassSegments ( - staticClass, - classBinding -) { - if (staticClass && !classBinding) { - return [{ type: RAW, value: (" class=" + staticClass) }] - } else { - return [{ - type: EXPRESSION, - value: ("_ssrClass(" + (staticClass || 'null') + "," + (classBinding || 'null') + ")") - }] - } -} - -function genStyleSegments ( - staticStyle, - parsedStaticStyle, - styleBinding, - vShowExpression -) { - 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') + ")") - }] - } -} - -/* */ - -/** - * 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. - */ - -// optimizability constants -var 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 -}; - -var isPlatformReservedTag$1; - -function optimize$1 (root, options) { - if (!root) { return } - isPlatformReservedTag$1 = options.isReservedTag || no; - walk(root, true); -} - -function walk (node, isRoot) { - if (isUnOptimizableTree(node)) { - node.ssrOptimizability = optimizability.FALSE; - return - } - // root node or nodes with custom directives should always be a VNode - var selfUnoptimizable = isRoot || hasCustomDirective(node); - var check = function (child) { - if (child.ssrOptimizability !== optimizability.FULL) { - node.ssrOptimizability = selfUnoptimizable - ? optimizability.PARTIAL - : optimizability.SELF; - } - }; - if (selfUnoptimizable) { - node.ssrOptimizability = optimizability.CHILDREN; - } - if (node.type === 1) { - for (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - walk(child); - check(child); - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].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) { - var children = el.children; - var optimizedChildren = []; - - var currentOptimizableGroup = []; - var pushGroup = function () { - if (currentOptimizableGroup.length) { - optimizedChildren.push({ - type: 1, - parent: el, - tag: 'template', - attrsList: [], - attrsMap: {}, - children: currentOptimizableGroup, - ssrOptimizability: optimizability.FULL - }); - } - currentOptimizableGroup = []; - }; - - for (var i = 0; i < children.length; i++) { - var 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) { - if (node.type === 2 || node.type === 3) { // text or expression - return false - } - return ( - isBuiltInTag(node.tag) || // built-in (slot, component) - !isPlatformReservedTag$1(node.tag) || // custom component - !!node.component || // "is" component - isSelectWithModel(node) // <select v-model> requires runtime inspection - ) -} - -var isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once'); - -function hasCustomDirective (node) { - return ( - node.type === 1 && - node.directives && - node.directives.some(function (d) { return !isBuiltInDir(d.name); }) - ) -} - -// <select v-model> cannot be optimized because it requires a runtime check -// to determine proper selected option -function isSelectWithModel (node) { - return ( - node.type === 1 && - node.tag === 'select' && - node.directives != null && - node.directives.some(function (d) { return d.name === 'model'; }) - ) -} - -/* */ - -// 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. - -// segment types -var RAW = 0; -var INTERPOLATION = 1; -var EXPRESSION = 2; - -function generate$1 ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genSSRElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genSSRElement (el, state) { - 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) { - var data = el.plain ? undefined : genData$2(el, state); - var children = stringifyChildren - ? ("[" + (genChildrenAsStringNode(el, state)) + "]") - : genSSRChildren(el, state, true); - return ("_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")") -} - -function genSSRChildren (el, state, checkSkip) { - 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) { - var 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) { - // 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) - } - - var openSegments = elementToOpenTagSegments(el, state); - var childrenSegments = childrenToSegments(el, state); - var ref = state.options; - var isUnaryTag = ref.isUnaryTag; - var close = (isUnaryTag && isUnaryTag(el.tag)) - ? [] - : [{ type: RAW, value: ("</" + (el.tag) + ">") }]; - return openSegments.concat(childrenSegments, close) -} - -function elementToOpenTagSegments (el, state) { - applyModelTransform(el, state); - var binding; - var 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) { - var 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, - state -) { - var segments = []; - for (var i = 0; i < children.length; i++) { - var 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) { - segments.push({ type: RAW, value: escape(c.text) }); - } - } - return segments -} - -function flattenSegments (segments) { - var mergedSegments = []; - var textBuffer = ''; - - var pushBuffer = function () { - if (textBuffer) { - mergedSegments.push(JSON.stringify(textBuffer)); - textBuffer = ''; - } - }; - - for (var i = 0; i < segments.length; i++) { - var 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('+') -} - -/* */ - -var createCompiler$1 = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize$1(ast, options); - var code = generate$1(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref$1 = createCompiler$1(baseOptions); -var compile$1 = ref$1.compile; -var compileToFunctions$1 = ref$1.compileToFunctions; - -/* */ - -exports.parseComponent = parseComponent; -exports.compile = compile; -exports.compileToFunctions = compileToFunctions; -exports.ssrCompile = compile$1; -exports.ssrCompileToFunctions = compileToFunctions$1; - -Object.defineProperty(exports, '__esModule', { value: true }); - -}))); diff --git a/packages/vue-template-compiler/build.js b/packages/vue-template-compiler/build.js deleted file mode 100644 index 93eb6db5600..00000000000 --- a/packages/vue-template-compiler/build.js +++ /dev/null @@ -1,4697 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var deindent = _interopDefault(require('de-indent')); -var he = _interopDefault(require('he')); - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining - - - - - - - - -/** - * Check if value is primitive - */ - - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - - - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ - - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ - - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ - - -/** - * Convert an Array-like object to a real Array. - */ - - -/** - * Mix properties into target object. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ - - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ - - - - -/** - * Ensure a function is called only once. - */ - -/* */ - -var 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) -var 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 -var 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' -); - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if (process.env.NODE_ENV !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href' - ? options.shouldDecodeNewlinesForHref - : options.shouldDecodeNewlines; - attrs[i] = { - name: args[1], - value: decodeAttr(value, shouldDecodeNewlines) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if (process.env.NODE_ENV !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -var splitRE = /\r?\n/g; -var replaceRE = /./g; -var isSpecialTag = makeMap('script,style,template', true); - - - -/** - * Parse a single-file component (*.vue) file into an SFC Descriptor Object. - */ -function parseComponent ( - content, - options - ) { - if ( options === void 0 ) options = {}; - - var sfc = { - template: null, - script: null, - styles: [], - customBlocks: [] - }; - var depth = 0; - var currentBlock = null; - - function start ( - tag, - attrs, - unary, - start, - end - ) { - if (depth === 0) { - currentBlock = { - type: tag, - content: '', - start: end, - attrs: attrs.reduce(function (cumulated, ref) { - var name = ref.name; - var value = ref.value; - - cumulated[name] = value || true; - return cumulated - }, Object.create(null)) - }; - if (isSpecialTag(tag)) { - checkAttrs(currentBlock, attrs); - if (tag === 'style') { - sfc.styles.push(currentBlock); - } else { - sfc[tag] = currentBlock; - } - } else { // custom blocks - sfc.customBlocks.push(currentBlock); - } - } - if (!unary) { - depth++; - } - } - - function checkAttrs (block, attrs) { - for (var i = 0; i < attrs.length; i++) { - var 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; - } - if (attr.name === 'src') { - block.src = attr.value; - } - } - } - - function end (tag, start, end) { - if (depth === 1 && currentBlock) { - currentBlock.end = start; - var text = deindent(content.slice(currentBlock.start, currentBlock.end)); - // 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, pad) { - if (pad === 'space') { - return content.slice(0, block.start).replace(replaceRE, ' ') - } else { - var offset = content.slice(0, block.start).split(splitRE).length; - var padChar = block.type === 'script' && !block.lang - ? '//\n' - : '\n'; - return Array(offset).join(padChar) - } - } - - parseHTML(content, { - start: start, - end: end - }); - - return sfc -} - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ - - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - - -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools - - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - - - - - -// 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. - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ - - -/** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ -function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -function assertObjectType (name, value, vm) { - 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. - */ - - -/** - * Resolve an asset. - * This function is used because child instances need access - * to assets defined in its ancestor chain. - */ - -/* */ - -/* */ - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -function flushCallbacks () { - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - channel.port1.onmessage = flushCallbacks; - -} else { - /* istanbul ignore next */ - -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - -} else { - // fallback to macro - -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ - -/* */ - -/* */ - -// these are reserved for web because they are directly compiled away -// during template compilation -var isReservedAttr = makeMap('style,class'); - -// attributes that should be using props for binding -var acceptValue = makeMap('input,textarea,option,select,progress'); -var mustUseProp = function (tag, type, attr) { - return ( - (attr === 'value' && acceptValue(tag)) && type !== 'button' || - (attr === 'selected' && tag === 'option') || - (attr === 'checked' && tag === 'input') || - (attr === 'muted' && tag === 'video') - ) -}; - -var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck'); - -var 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,translate,' + - 'truespeed,typemustmatch,visible' -); - -/* */ - -/* */ - - - -var 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. -var 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 -); - -var isPreTag = function (tag) { return tag === 'pre'; }; - -var isReservedTag = function (tag) { - return isHTMLTag(tag) || isSVG(tag) -}; - -function getTagNamespace (tag) { - 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' - } -} - - - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/* */ - -/** - * Query an element selector if it's not an element already. - */ - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - process.env.NODE_ENV !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - if (process.env.NODE_ENV !== 'production' && staticClass) { - var expression = parseText(staticClass, options.delimiters); - if (expression) { - 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">.' - ); - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass); - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } -} - -function genData (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -var klass = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData -}; - -/* */ - -var parseStyleText = cached(function (cssText) { - var res = {}; - var listDelimiter = /;(?![^(]*\))/g; - var propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var tmp = item.split(propertyDelimiter); - tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim()); - } - }); - return res -}); - -// normalize possible array / string values into Object - - -/** - * parent component style should be after child's - * so that parent component's style could override it - */ - -/* */ - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - if (staticStyle) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(staticStyle, options.delimiters); - if (expression) { - 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.staticStyle = JSON.stringify(parseStyleText(staticStyle)); - } - - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:(" + (el.styleBinding) + "),"; - } - return data -} - -var style = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$1 -}; - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - 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 () { - return str.charCodeAt(++index) -} - -function eof () { - return index >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var 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) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn$1; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn$1 = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn$1(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn$1, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - process.env.NODE_ENV !== 'production' && warn$1( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - function checkRootConstraints (el) { - if (process.env.NODE_ENV !== 'production') { - if (el.tag === 'slot' || el.tag === 'template') { - warnOnce( - "Cannot use <" + (el.tag) + "> as component root element because it may " + - 'contain multiple nodes.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else if (process.env.NODE_ENV !== 'production') { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - if (process.env.NODE_ENV !== 'production') { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if (process.env.NODE_ENV !== 'production' && el.tag === 'template') { - warn$1("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - process.env.NODE_ENV !== 'production' && warn$1( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else if (process.env.NODE_ENV !== 'production') { - warn$1( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if (process.env.NODE_ENV !== 'production' && children[i].text !== ' ') { - warn$1( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if (process.env.NODE_ENV !== 'production' && el.key) { - warn$1( - "`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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && slotScope) { - warn$1( - "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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // 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); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn$1); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if (process.env.NODE_ENV !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(value, delimiters); - if (expression) { - warn$1( - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - // #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'); - } - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - process.env.NODE_ENV !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn$1('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _el = el; - while (_el) { - if (_el.for && _el.alias === value) { - warn$1( - "<" + (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 = _el.parent; - } -} - -/* */ - -/** - * Expand input[v-model] with dyanmic 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]"> - */ - -function preTransformNode (el, options) { - if (el.tag === 'input') { - var map = el.attrsMap; - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - var typeBinding = getBindingAttr(el, 'type'); - var ifCondition = getAndRemoveAttr(el, 'v-if', true); - var ifConditionExtra = ifCondition ? ("&&(" + ifCondition + ")") : ""; - var hasElse = getAndRemoveAttr(el, 'v-else', true) != null; - var elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true); - // 1. checkbox - var 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 - var 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 - var 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value; - el.attrsList.push({ name: name, value: value }); -} - -var model = { - preTransformNode: preTransformNode -}; - -var modules = [ - klass, - style, - model -]; - -/* */ - -var warn$2; - -// in some cases, the event used has to be determined at runtime -// so we used some reserved tokens during compile. -var RANGE_TOKEN = '__r'; - - -function model$1 ( - el, - dir, - _warn -) { - warn$2 = _warn; - var value = dir.value; - var modifiers = dir.modifiers; - var tag = el.tag; - var type = el.attrsMap.type; - - if (process.env.NODE_ENV !== 'production') { - // inputs with type="file" are read only and setting the input's - // value will throw an error. - if (tag === 'input' && type === 'file') { - warn$2( - "<" + (el.tag) + " v-model=\"" + value + "\" type=\"file\">:\n" + - "File inputs are read only. Use a v-on:change listener instead." - ); - } - } - - 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 (process.env.NODE_ENV !== 'production') { - warn$2( - "<" + (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.' - ); - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var valueBinding = getBindingAttr(el, 'value') || 'null'; - var trueValueBinding = getBindingAttr(el, 'true-value') || 'true'; - var 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&&(" + value + "=$$a.concat([$$v]))}" + - "else{$$i>-1&&(" + value + "=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}" + - "}else{" + (genAssignmentCode(value, '$$c')) + "}", - null, true - ); -} - -function genRadioModel ( - el, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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, - value, - modifiers -) { - var number = modifiers && modifiers.number; - var 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') + "})"; - - var assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'; - var code = "var $$selectedVal = " + selectedVal + ";"; - code = code + " " + (genAssignmentCode(value, assignment)); - addHandler(el, 'change', code, null, true); -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var type = el.attrsMap.type; - var ref = modifiers || {}; - var lazy = ref.lazy; - var number = ref.number; - var trim = ref.trim; - var needCompositionGuard = !lazy && type !== 'range'; - var event = lazy - ? 'change' - : type === 'range' - ? RANGE_TOKEN - : 'input'; - - var valueExpression = '$event.target.value'; - if (trim) { - valueExpression = "$event.target.value.trim()"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var 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()'); - } -} - -/* */ - -function text (el, dir) { - if (dir.value) { - addProp(el, 'textContent', ("_s(" + (dir.value) + ")")); - } -} - -/* */ - -function html (el, dir) { - if (dir.value) { - addProp(el, 'innerHTML', ("_s(" + (dir.value) + ")")); - } -} - -var directives = { - model: model$1, - text: text, - html: html -}; - -/* */ - -var baseOptions = { - expectHTML: true, - modules: modules, - directives: directives, - isPreTag: isPreTag, - isUnaryTag: isUnaryTag, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - staticKeys: genStaticKeys(modules) -}; - -/* */ - -var isStaticKey; -var isPlatformReservedTag; - -var genStaticKeysCached = cached(genStaticKeys$1); - -/** - * 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. - */ -function optimize (root, options) { - 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$1 (keys) { - return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + - (keys ? ',' + keys : '') - ) -} - -function markStatic (node) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - markStatic(child); - if (!child.static) { - node.static = false; - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].block; - markStatic(block); - if (!block.static) { - node.static = false; - } - } - } - } -} - -function markStaticRoots (node, isInFor) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - markStaticRoots(node.children[i], isInFor || !!node.for); - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - markStaticRoots(node.ifConditions[i$1].block, isInFor); - } - } - } -} - -function isStatic (node) { - 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) { - while (node.parent) { - node = node.parent; - if (node.tag !== 'template') { - return false - } - if (node.for) { - return true - } - } - return false -} - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if (process.env.NODE_ENV !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -function on (el, dir) { - if (process.env.NODE_ENV !== 'production' && dir.modifiers) { - warn("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData$2(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - process.env.NODE_ENV !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if (process.env.NODE_ENV !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData$2 (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if (process.env.NODE_ENV !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn; - delete options.warn; - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - if (process.env.NODE_ENV !== 'production') { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 (process.env.NODE_ENV !== 'production') { - if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -// `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. -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref = createCompiler(baseOptions); -var compile = ref.compile; -var compileToFunctions = ref.compileToFunctions; - -/* */ - -var 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,http-equiv,' + - 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + - 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,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,type,usemap,value,width,wrap' -); - -/* istanbul ignore next */ -var isRenderableAttr = function (name) { - return ( - isAttr(name) || - name.indexOf('data-') === 0 || - name.indexOf('aria-') === 0 - ) -}; -var propsToAttrMap = { - acceptCharset: 'accept-charset', - className: 'class', - htmlFor: 'for', - httpEquiv: 'http-equiv' -}; - -var ESC = { - '<': '<', - '>': '>', - '"': '"', - '&': '&' -}; - -function escape (s) { - return s.replace(/[<>"&]/g, escapeChar) -} - -function escapeChar (a) { - return ESC[a] || a -} - -/* */ - -var plainStringRE = /^"(?:[^"\\]|\\.)*"$|^'(?:[^'\\]|\\.)*'$/; - -// let the model AST transform translate v-model into appropriate -// props bindings -function applyModelTransform (el, state) { - if (el.directives) { - for (var i = 0; i < el.directives.length; i++) { - var 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(function (p) { return p.name !== 'value'; }); - } - break - } - } - } -} - -function genAttrSegments ( - attrs -) { - return attrs.map(function (ref) { - var name = ref.name; - var value = ref.value; - - return genAttrSegment(name, value); - }) -} - -function genDOMPropSegments ( - props, - attrs -) { - var segments = []; - props.forEach(function (ref) { - var name = ref.name; - var value = ref.value; - - name = propsToAttrMap[name] || name.toLowerCase(); - if (isRenderableAttr(name) && - !(attrs && attrs.some(function (a) { return a.name === name; })) - ) { - segments.push(genAttrSegment(name, value)); - } - }); - return segments -} - -function genAttrSegment (name, value) { - 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 + "=" + value) - } - } else { - return { - type: EXPRESSION, - value: ("_ssrAttr(" + (JSON.stringify(name)) + "," + value + ")") - } - } -} - -function genClassSegments ( - staticClass, - classBinding -) { - if (staticClass && !classBinding) { - return [{ type: RAW, value: (" class=" + staticClass) }] - } else { - return [{ - type: EXPRESSION, - value: ("_ssrClass(" + (staticClass || 'null') + "," + (classBinding || 'null') + ")") - }] - } -} - -function genStyleSegments ( - staticStyle, - parsedStaticStyle, - styleBinding, - vShowExpression -) { - 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') + ")") - }] - } -} - -/* */ - -/** - * 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. - */ - -// optimizability constants -var 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 -}; - -var isPlatformReservedTag$1; - -function optimize$1 (root, options) { - if (!root) { return } - isPlatformReservedTag$1 = options.isReservedTag || no; - walk(root, true); -} - -function walk (node, isRoot) { - if (isUnOptimizableTree(node)) { - node.ssrOptimizability = optimizability.FALSE; - return - } - // root node or nodes with custom directives should always be a VNode - var selfUnoptimizable = isRoot || hasCustomDirective(node); - var check = function (child) { - if (child.ssrOptimizability !== optimizability.FULL) { - node.ssrOptimizability = selfUnoptimizable - ? optimizability.PARTIAL - : optimizability.SELF; - } - }; - if (selfUnoptimizable) { - node.ssrOptimizability = optimizability.CHILDREN; - } - if (node.type === 1) { - for (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - walk(child); - check(child); - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].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) { - var children = el.children; - var optimizedChildren = []; - - var currentOptimizableGroup = []; - var pushGroup = function () { - if (currentOptimizableGroup.length) { - optimizedChildren.push({ - type: 1, - parent: el, - tag: 'template', - attrsList: [], - attrsMap: {}, - children: currentOptimizableGroup, - ssrOptimizability: optimizability.FULL - }); - } - currentOptimizableGroup = []; - }; - - for (var i = 0; i < children.length; i++) { - var 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) { - if (node.type === 2 || node.type === 3) { // text or expression - return false - } - return ( - isBuiltInTag(node.tag) || // built-in (slot, component) - !isPlatformReservedTag$1(node.tag) || // custom component - !!node.component || // "is" component - isSelectWithModel(node) // <select v-model> requires runtime inspection - ) -} - -var isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once'); - -function hasCustomDirective (node) { - return ( - node.type === 1 && - node.directives && - node.directives.some(function (d) { return !isBuiltInDir(d.name); }) - ) -} - -// <select v-model> cannot be optimized because it requires a runtime check -// to determine proper selected option -function isSelectWithModel (node) { - return ( - node.type === 1 && - node.tag === 'select' && - node.directives != null && - node.directives.some(function (d) { return d.name === 'model'; }) - ) -} - -/* */ - -// 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. - -// segment types -var RAW = 0; -var INTERPOLATION = 1; -var EXPRESSION = 2; - -function generate$1 ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genSSRElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genSSRElement (el, state) { - 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) { - var data = el.plain ? undefined : genData$2(el, state); - var children = stringifyChildren - ? ("[" + (genChildrenAsStringNode(el, state)) + "]") - : genSSRChildren(el, state, true); - return ("_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")") -} - -function genSSRChildren (el, state, checkSkip) { - 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) { - var 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) { - // 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) - } - - var openSegments = elementToOpenTagSegments(el, state); - var childrenSegments = childrenToSegments(el, state); - var ref = state.options; - var isUnaryTag = ref.isUnaryTag; - var close = (isUnaryTag && isUnaryTag(el.tag)) - ? [] - : [{ type: RAW, value: ("</" + (el.tag) + ">") }]; - return openSegments.concat(childrenSegments, close) -} - -function elementToOpenTagSegments (el, state) { - applyModelTransform(el, state); - var binding; - var 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) { - var 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, - state -) { - var segments = []; - for (var i = 0; i < children.length; i++) { - var 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) { - segments.push({ type: RAW, value: escape(c.text) }); - } - } - return segments -} - -function flattenSegments (segments) { - var mergedSegments = []; - var textBuffer = ''; - - var pushBuffer = function () { - if (textBuffer) { - mergedSegments.push(JSON.stringify(textBuffer)); - textBuffer = ''; - } - }; - - for (var i = 0; i < segments.length; i++) { - var 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('+') -} - -/* */ - -var createCompiler$1 = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize$1(ast, options); - var code = generate$1(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -var ref$1 = createCompiler$1(baseOptions); -var compile$1 = ref$1.compile; -var compileToFunctions$1 = ref$1.compileToFunctions; - -/* */ - -exports.parseComponent = parseComponent; -exports.compile = compile; -exports.compileToFunctions = compileToFunctions; -exports.ssrCompile = compile$1; -exports.ssrCompileToFunctions = compileToFunctions$1; diff --git a/packages/vue-template-compiler/index.js b/packages/vue-template-compiler/index.js deleted file mode 100644 index 287d281738e..00000000000 --- a/packages/vue-template-compiler/index.js +++ /dev/null @@ -1,18 +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 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/vue-template-compiler/package.json b/packages/vue-template-compiler/package.json deleted file mode 100644 index 17cf9d135f6..00000000000 --- a/packages/vue-template-compiler/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "vue-template-compiler", - "version": "2.5.3", - "description": "template compiler for Vue 2.0", - "main": "index.js", - "unpkg": "browser.js", - "jsdelivr": "browser.js", - "browser": "browser.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": { - "he": "^1.1.0", - "de-indent": "^1.0.2" - } -} diff --git a/packages/weex-template-compiler/README.md b/packages/weex-template-compiler/README.md deleted file mode 100644 index 6ebca962fcc..00000000000 --- a/packages/weex-template-compiler/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# weex-template-compiler - -> This package is auto-generated. For pull requests please see [src/platforms/weex/entry-compiler.js](https://github.com/vuejs/vue/tree/dev/src/platforms/weex/entry-compiler.js). diff --git a/packages/weex-template-compiler/build.js b/packages/weex-template-compiler/build.js deleted file mode 100644 index 81f77215c19..00000000000 --- a/packages/weex-template-compiler/build.js +++ /dev/null @@ -1,3911 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var he = _interopDefault(require('he')); - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining - - - - - - - - -/** - * Check if value is primitive - */ - - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - - - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ - - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ - - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ - - -/** - * Convert an Array-like object to a real Array. - */ - - -/** - * Mix properties into target object. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ - - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return 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? - */ - - - - -/** - * Ensure a function is called only once. - */ - -/* */ - -var 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) -var 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 -var 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' -); - -/** - * Not type-checking this file because it's mostly vendor code. - */ - -/*! - * HTML Parser By John Resig (ejohn.org) - * Modified by Juriy "kangax" Zaytsev - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -// Regular Expressions for parsing tags and attributes -var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -var ncname = '[a-zA-Z_][\\w\\-\\.]*'; -var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; -var startTagOpen = new RegExp(("^<" + qnameCapture)); -var startTagClose = /^\s*(\/?)>/; -var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>")); -var doctype = /^<!DOCTYPE [^>]+>/i; -var comment = /^<!--/; -var conditionalComment = /^<!\[/; - -var IS_REGEX_CAPTURING_BROKEN = false; -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === ''; -}); - -// Special Elements (can contain anything) -var isPlainTextElement = makeMap('script,style,textarea', true); -var reCache = {}; - -var decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n' -}; -var encodedAttr = /&(?:lt|gt|quot|amp);/g; -var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10);/g; - -// #5992 -var isIgnoreNewlineTag = makeMap('pre,textarea', true); -var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; - -function decodeAttr (value, shouldDecodeNewlines) { - var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; - return value.replace(re, function (match) { return decodingMap[match]; }) -} - -function parseHTML (html, options) { - var stack = []; - var expectHTML = options.expectHTML; - var isUnaryTag$$1 = options.isUnaryTag || no; - var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no; - var index = 0; - var last, lastTag; - while (html) { - last = html; - // Make sure we're not in a plaintext content element like script/style - if (!lastTag || !isPlainTextElement(lastTag)) { - var textEnd = html.indexOf('<'); - if (textEnd === 0) { - // Comment: - if (comment.test(html)) { - var commentEnd = html.indexOf('-->'); - - if (commentEnd >= 0) { - if (options.shouldKeepComment) { - options.comment(html.substring(4, commentEnd)); - } - advance(commentEnd + 3); - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - var conditionalEnd = html.indexOf(']>'); - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2); - continue - } - } - - // Doctype: - var doctypeMatch = html.match(doctype); - if (doctypeMatch) { - advance(doctypeMatch[0].length); - continue - } - - // End tag: - var endTagMatch = html.match(endTag); - if (endTagMatch) { - var curIndex = index; - advance(endTagMatch[0].length); - parseEndTag(endTagMatch[1], curIndex, index); - continue - } - - // Start tag: - var startTagMatch = parseStartTag(); - if (startTagMatch) { - handleStartTag(startTagMatch); - if (shouldIgnoreFirstNewline(lastTag, html)) { - advance(1); - } - continue - } - } - - var text = (void 0), rest = (void 0), next = (void 0); - if (textEnd >= 0) { - rest = html.slice(textEnd); - while ( - !endTag.test(rest) && - !startTagOpen.test(rest) && - !comment.test(rest) && - !conditionalComment.test(rest) - ) { - // < in plain text, be forgiving and treat it as text - next = rest.indexOf('<', 1); - if (next < 0) { break } - textEnd += next; - rest = html.slice(textEnd); - } - text = html.substring(0, textEnd); - advance(textEnd); - } - - if (textEnd < 0) { - text = html; - html = ''; - } - - if (options.chars && text) { - options.chars(text); - } - } else { - var endTagLength = 0; - var stackedTag = lastTag.toLowerCase(); - var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); - var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { - endTagLength = endTag.length; - if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { - text = text - .replace(/<!--([\s\S]*?)-->/g, '$1') - .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$1.length; - html = rest$1; - parseEndTag(stackedTag, index - endTagLength, index); - } - - if (html === last) { - options.chars && options.chars(html); - if (process.env.NODE_ENV !== 'production' && !stack.length && options.warn) { - options.warn(("Mal-formatted tag at end of template: \"" + html + "\"")); - } - break - } - } - - // Clean up any remaining tags - parseEndTag(); - - function advance (n) { - index += n; - html = html.substring(n); - } - - function parseStartTag () { - var start = html.match(startTagOpen); - if (start) { - var match = { - tagName: start[1], - attrs: [], - start: index - }; - advance(start[0].length); - var end, attr; - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length); - match.attrs.push(attr); - } - if (end) { - match.unarySlash = end[1]; - advance(end[0].length); - match.end = index; - return match - } - } - } - - function handleStartTag (match) { - var tagName = match.tagName; - var unarySlash = match.unarySlash; - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag); - } - if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) { - parseEndTag(tagName); - } - } - - var unary = isUnaryTag$$1(tagName) || !!unarySlash; - - var l = match.attrs.length; - var attrs = new Array(l); - for (var i = 0; i < l; i++) { - var args = match.attrs[i]; - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3]; } - if (args[4] === '') { delete args[4]; } - if (args[5] === '') { delete args[5]; } - } - var value = args[3] || args[4] || args[5] || ''; - attrs[i] = { - name: args[1], - value: decodeAttr( - value, - options.shouldDecodeNewlines - ) - }; - } - - if (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }); - lastTag = tagName; - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end); - } - } - - function parseEndTag (tagName, start, end) { - var pos, lowerCasedTagName; - if (start == null) { start = index; } - if (end == null) { end = index; } - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase(); - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0; - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (var i = stack.length - 1; i >= pos; i--) { - if (process.env.NODE_ENV !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - ("tag <" + (stack[i].tag) + "> has no matching end tag.") - ); - } - if (options.end) { - options.end(stack[i].tag, start, end); - } - } - - // Remove the open elements from the stack - stack.length = pos; - lastTag = pos && stack[pos - 1].tag; - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end); - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end); - } - if (options.end) { - options.end(tagName, start, end); - } - } - } -} - -/* */ - -var validDivisionCharRE = /[\w).+\-_$\]]/; - -function parseFilters (exp) { - var inSingle = false; - var inDouble = false; - var inTemplateString = false; - var inRegex = false; - var curly = 0; - var square = 0; - var paren = 0; - var lastFilterIndex = 0; - var 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) { // / - var j = i - 1; - var p = (void 0); - // 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, filter) { - var i = filter.indexOf('('); - if (i < 0) { - // _f: resolveFilter - return ("_f(\"" + filter + "\")(" + exp + ")") - } else { - var name = filter.slice(0, i); - var args = filter.slice(i + 1); - return ("_f(\"" + name + "\")(" + exp + "," + args) - } -} - -/* */ - -var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g; -var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - -var buildRegex = cached(function (delimiters) { - var open = delimiters[0].replace(regexEscapeRE, '\\$&'); - var close = delimiters[1].replace(regexEscapeRE, '\\$&'); - return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') -}); - -function parseText ( - text, - delimiters -) { - var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; - if (!tagRE.test(text)) { - return - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index; - while ((match = tagRE.exec(text))) { - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))); - } - // tag token - var exp = parseFilters(match[1].trim()); - tokens.push(("_s(" + exp + ")")); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))); - } - return tokens.join('+') -} - -/* */ - -/** - * Cross-platform code generation for component v-model - */ -function genComponentModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var number = ref.number; - var trim = ref.trim; - - var baseValueExpression = '$$v'; - var valueExpression = baseValueExpression; - if (trim) { - valueExpression = - "(typeof " + baseValueExpression + " === 'string'" + - "? " + baseValueExpression + ".trim()" + - ": " + baseValueExpression + ")"; - } - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - var assignment = genAssignmentCode(value, valueExpression); - - el.model = { - value: ("(" + value + ")"), - expression: ("\"" + value + "\""), - callback: ("function (" + baseValueExpression + ") {" + assignment + "}") - }; -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -function genAssignmentCode ( - value, - assignment -) { - var 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]] - * - */ - -var len; -var str; -var chr; -var index; -var expressionPos; -var expressionEndPos; - - - -function parseModel (val) { - 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 () { - return str.charCodeAt(++index) -} - -function eof () { - return index >= len -} - -function isStringStart (chr) { - return chr === 0x22 || chr === 0x27 -} - -function parseBracket (chr) { - var 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) { - var stringQuote = chr; - while (!eof()) { - chr = next(); - if (chr === stringQuote) { - break - } - } -} - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var warn$1 = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn$1 = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn$1(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - - -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools - - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -/** - * Defer a task to execute it asynchronously. - */ -var nextTick = (function () { - var callbacks = []; - var pending = false; - var timerFunc; - - function nextTickHandler () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } - } - - // An asynchronous deferring mechanism. - // In pre 2.4, we used to use microtasks (Promise/MutationObserver) - // but microtasks actually has too high a priority and fires in between - // supposedly sequential events (e.g. #4521, #6690) or even between - // bubbling of the same event (#6566). Technically setImmediate should be - // the ideal choice, but it's not available everywhere; and the only polyfill - // that consistently queues the callback after all DOM events triggered in the - // same loop is by using MessageChannel. - /* istanbul ignore if */ - if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - timerFunc = function () { - setImmediate(nextTickHandler); - }; - } else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' - )) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = nextTickHandler; - timerFunc = function () { - port.postMessage(1); - }; - } else - /* istanbul ignore next */ - if (typeof Promise !== 'undefined' && isNative(Promise)) { - // use microtask in non-DOM environments, e.g. Weex - var p = Promise.resolve(); - timerFunc = function () { - p.then(nextTickHandler); - }; - } else { - // fallback to setTimeout - timerFunc = function () { - setTimeout(nextTickHandler, 0); - }; - } - - return function queueNextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - 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(function (resolve, reject) { - _resolve = resolve; - }) - } - } -})(); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -function baseWarn (msg) { - console.error(("[Vue compiler]: " + msg)); -} - -function pluckModuleFunction ( - modules, - key -) { - return modules - ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) - : [] -} - -function addProp (el, name, value) { - (el.props || (el.props = [])).push({ name: name, value: value }); -} - -function addAttr (el, name, value) { - (el.attrs || (el.attrs = [])).push({ name: name, value: value }); -} - -function addDirective ( - el, - name, - rawName, - value, - arg, - modifiers -) { - (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers }); -} - -function addHandler ( - el, - name, - value, - modifiers, - important, - warn -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - process.env.NODE_ENV !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ); - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture; - name = '!' + name; // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once; - name = '~' + name; // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive; - name = '&' + name; // mark the event as passive - } - var events; - if (modifiers && modifiers.native) { - delete modifiers.native; - events = el.nativeEvents || (el.nativeEvents = {}); - } else { - events = el.events || (el.events = {}); - } - var newHandler = { value: value, modifiers: modifiers }; - var 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; - } -} - -function getBindingAttr ( - el, - name, - getStatic -) { - var dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name); - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - var 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. -function getAndRemoveAttr ( - el, - name, - removeFromMap -) { - var val; - if ((val = el.attrsMap[name]) != null) { - var list = el.attrsList; - for (var 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 -} - -/* */ - -var onRE = /^@|^v-on:/; -var dirRE = /^v-|^@|^:/; -var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/; -var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/; - -var argRE = /:(.*)$/; -var bindRE = /^:|^v-bind:/; -var modifierRE = /\.[^.]+/g; - -var decodeHTMLCached = cached(he.decode); - -// configurable state -var warn; -var delimiters; -var transforms; -var preTransforms; -var postTransforms; -var platformIsPreTag; -var platformMustUseProp; -var platformGetTagNamespace; - - - -function createASTElement ( - tag, - attrs, - parent -) { - return { - type: 1, - tag: tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent: parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -function parse ( - template, - options -) { - warn = options.warn || baseWarn; - - platformIsPreTag = options.isPreTag || no; - platformMustUseProp = options.mustUseProp || no; - platformGetTagNamespace = options.getTagNamespace || no; - - transforms = pluckModuleFunction(options.modules, 'transformNode'); - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); - - delimiters = options.delimiters; - - var stack = []; - var preserveWhitespace = options.preserveWhitespace !== false; - var root; - var currentParent; - var inVPre = false; - var inPre = false; - var warned = false; - - function warnOnce (msg) { - if (!warned) { - warned = true; - warn(msg); - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false; - } - if (platformIsPreTag(element.tag)) { - inPre = false; - } - } - - parseHTML(template, { - warn: warn, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldKeepComment: options.comments, - start: function start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs); - } - - var element = createASTElement(tag, attrs, currentParent); - if (ns) { - element.ns = ns; - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true; - process.env.NODE_ENV !== 'production' && warn( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - "<" + tag + ">" + ', as they will not be parsed.' - ); - } - - // apply pre-transforms - for (var 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); - // element-scope stuff - processElement(element, options); - } - - function checkRootConstraints (el) { - if (process.env.NODE_ENV !== 'production') { - if (el.tag === 'slot' || el.tag === 'template') { - warnOnce( - "Cannot use <" + (el.tag) + "> as component root element because it may " + - 'contain multiple nodes.' - ); - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ); - } - } - } - - // tree management - if (!root) { - root = element; - checkRootConstraints(root); - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element); - addIfCondition(root, { - exp: element.elseif, - block: element - }); - } else if (process.env.NODE_ENV !== 'production') { - warnOnce( - "Component template should contain exactly one root element. " + - "If you are using v-if on multiple elements, " + - "use v-else-if to chain them instead." - ); - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent); - } else if (element.slotScope) { // scoped slot - currentParent.plain = false; - var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; - } else { - currentParent.children.push(element); - element.parent = currentParent; - } - } - if (!unary) { - currentParent = element; - stack.push(element); - } else { - endPre(element); - } - // apply post-transforms - for (var i$1 = 0; i$1 < postTransforms.length; i$1++) { - postTransforms[i$1](element, options); - } - }, - - end: function end () { - // remove trailing whitespace - var element = stack[stack.length - 1]; - var lastNode = element.children[element.children.length - 1]; - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop(); - } - // pop stack - stack.length -= 1; - currentParent = stack[stack.length - 1]; - endPre(element); - }, - - chars: function chars (text) { - if (!currentParent) { - if (process.env.NODE_ENV !== 'production') { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ); - } else if ((text = text.trim())) { - warnOnce( - ("text \"" + text + "\" outside root element will be ignored.") - ); - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - var children = currentParent.children; - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : ''; - if (text) { - var expression; - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression: expression, - text: text - }); - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text: text - }); - } - } - }, - comment: function comment (text) { - currentParent.children.push({ - type: 3, - text: text, - isComment: true - }); - } - }); - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true; - } -} - -function processRawAttrs (el) { - var l = el.attrsList.length; - if (l) { - var attrs = el.attrs = new Array(l); - for (var i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - }; - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true; - } -} - -function processElement (element, options) { - processKey(element); - - // determine whether this is a plain element after - // removing structural attributes - element.plain = !element.key && !element.attrsList.length; - - processRef(element); - processSlot(element); - processComponent(element); - for (var i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element; - } - processAttrs(element); -} - -function processKey (el) { - var exp = getBindingAttr(el, 'key'); - if (exp) { - if (process.env.NODE_ENV !== 'production' && el.tag === 'template') { - warn("<template> cannot be keyed. Place the key on real elements instead."); - } - el.key = exp; - } -} - -function processRef (el) { - var ref = getBindingAttr(el, 'ref'); - if (ref) { - el.ref = ref; - el.refInFor = checkInFor(el); - } -} - -function processFor (el) { - var exp; - if ((exp = getAndRemoveAttr(el, 'v-for'))) { - var inMatch = exp.match(forAliasRE); - if (!inMatch) { - process.env.NODE_ENV !== 'production' && warn( - ("Invalid v-for expression: " + exp) - ); - return - } - el.for = inMatch[2].trim(); - var alias = inMatch[1].trim(); - var iteratorMatch = alias.match(forIteratorRE); - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim(); - el.iterator1 = iteratorMatch[2].trim(); - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim(); - } - } else { - el.alias = alias; - } - } -} - -function processIf (el) { - var 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; - } - var elseif = getAndRemoveAttr(el, 'v-else-if'); - if (elseif) { - el.elseif = elseif; - } - } -} - -function processIfConditions (el, parent) { - var prev = findPrevElement(parent.children); - if (prev && prev.if) { - addIfCondition(prev, { - exp: el.elseif, - block: el - }); - } else if (process.env.NODE_ENV !== 'production') { - warn( - "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " + - "used on element <" + (el.tag) + "> without corresponding v-if." - ); - } -} - -function findPrevElement (children) { - var i = children.length; - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if (process.env.NODE_ENV !== 'production' && children[i].text !== ' ') { - warn( - "text \"" + (children[i].text.trim()) + "\" between v-if and v-else(-if) " + - "will be ignored." - ); - } - children.pop(); - } - } -} - -function addIfCondition (el, condition) { - if (!el.ifConditions) { - el.ifConditions = []; - } - el.ifConditions.push(condition); -} - -function processOnce (el) { - var once$$1 = getAndRemoveAttr(el, 'v-once'); - if (once$$1 != null) { - el.once = true; - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name'); - if (process.env.NODE_ENV !== 'production' && 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." - ); - } - } else { - var slotScope; - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope'); - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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.", - true - ); - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope'); - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope; - } - var slotTarget = getBindingAttr(el, 'slot'); - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget; - // preserve slot as an attribute for native shadow DOM compat - // only for non-scoped slots. - if (!el.slotScope) { - addAttr(el, 'slot', slotTarget); - } - } - } -} - -function processComponent (el) { - var binding; - if ((binding = getBindingAttr(el, 'is'))) { - el.component = binding; - } - if (getAndRemoveAttr(el, 'inline-template') != null) { - el.inlineTemplate = true; - } -} - -function processAttrs (el) { - var list = el.attrsList; - var i, l, name, rawName, value, modifiers, isProp; - 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); - if (modifiers) { - name = name.replace(modifierRE, ''); - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, ''); - value = parseFilters(value); - isProp = false; - if (modifiers) { - if (modifiers.prop) { - isProp = true; - name = camelize(name); - if (name === 'innerHtml') { name = 'innerHTML'; } - } - if (modifiers.camel) { - name = camelize(name); - } - if (modifiers.sync) { - addHandler( - el, - ("update:" + (camelize(name))), - genAssignmentCode(value, "$event") - ); - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value); - } else { - addAttr(el, name, value); - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, ''); - addHandler(el, name, value, modifiers, false, warn); - } else { // normal directives - name = name.replace(dirRE, ''); - // parse arg - var argMatch = name.match(argRE); - var arg = argMatch && argMatch[1]; - if (arg) { - name = name.slice(0, -(arg.length + 1)); - } - addDirective(el, name, rawName, value, arg, modifiers); - if (process.env.NODE_ENV !== 'production' && name === 'model') { - checkForAliasModel(el, value); - } - } - } else { - // literal attribute - if (process.env.NODE_ENV !== 'production') { - var expression = parseText(value, delimiters); - if (expression) { - 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">.' - ); - } - } - addAttr(el, name, JSON.stringify(value)); - } - } -} - -function checkInFor (el) { - var parent = el; - while (parent) { - if (parent.for !== undefined) { - return true - } - parent = parent.parent; - } - return false -} - -function parseModifiers (name) { - var match = name.match(modifierRE); - if (match) { - var ret = {}; - match.forEach(function (m) { ret[m.slice(1)] = true; }); - return ret - } -} - -function makeAttrsMap (attrs) { - var map = {}; - for (var i = 0, l = attrs.length; i < l; i++) { - if ( - process.env.NODE_ENV !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn('duplicate attribute: ' + attrs[i].name); - } - 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) { - return el.tag === 'script' || el.tag === 'style' -} - -function isForbiddenTag (el) { - return ( - el.tag === 'style' || - (el.tag === 'script' && ( - !el.attrsMap.type || - el.attrsMap.type === 'text/javascript' - )) - ) -} - -var ieNSBug = /^xmlns:NS\d+/; -var ieNSPrefix = /^NS\d+:/; - -/* istanbul ignore next */ -function guardIESVGBug (attrs) { - var res = []; - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - if (!ieNSBug.test(attr.name)) { - attr.name = attr.name.replace(ieNSPrefix, ''); - res.push(attr); - } - } - return res -} - -function checkForAliasModel (el, value) { - var _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 = _el.parent; - } -} - -/* */ - -var isStaticKey; -var isPlatformReservedTag; - -var genStaticKeysCached = cached(genStaticKeys$1); - -/** - * 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. - */ -function optimize (root, options) { - 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$1 (keys) { - return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + - (keys ? ',' + keys : '') - ) -} - -function markStatic (node) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - var child = node.children[i]; - markStatic(child); - if (!child.static) { - node.static = false; - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - var block = node.ifConditions[i$1].block; - markStatic(block); - if (!block.static) { - node.static = false; - } - } - } - } -} - -function markStaticRoots (node, isInFor) { - 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 (var i = 0, l = node.children.length; i < l; i++) { - markStaticRoots(node.children[i], isInFor || !!node.for); - } - } - if (node.ifConditions) { - for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { - markStaticRoots(node.ifConditions[i$1].block, isInFor); - } - } - } -} - -function isStatic (node) { - 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) { - while (node.parent) { - node = node.parent; - if (node.tag !== 'template') { - return false - } - if (node.for) { - return true - } - } - return false -} - -/* */ - -var fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/; -var simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/; - -// keyCode aliases -var keyCodes = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -}; - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -var genGuard = function (condition) { return ("if(" + condition + ")return null;"); }; - -var modifierCode = { - 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") -}; - -function genHandlers ( - events, - isNative, - warn -) { - var res = isNative ? 'nativeOn:{' : 'on:{'; - for (var name in events) { - var handler = events[name]; - // #5330: warn click.right, since right clicks do not actually fire click events. - if (process.env.NODE_ENV !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - "Use \"contextmenu\" instead of \"click.right\" since right clicks " + - "do not actually fire \"click\" events." - ); - } - res += "\"" + name + "\":" + (genHandler(name, handler)) + ","; - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name, - handler -) { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return ("[" + (handler.map(function (handler) { return genHandler(name, handler); }).join(',')) + "]") - } - - var isMethodPath = simplePathRE.test(handler.value); - var isFunctionExpression = fnExpRE.test(handler.value); - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : ("function($event){" + (handler.value) + "}") // inline statement - } else { - var code = ''; - var genModifierCode = ''; - var keys = []; - for (var key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key]; - // left/right - if (keyCodes[key]) { - keys.push(key); - } - } else if (key === 'exact') { - var modifiers = (handler.modifiers); - genModifierCode += genGuard( - ['ctrl', 'shift', 'alt', 'meta'] - .filter(function (keyModifier) { return !modifiers[keyModifier]; }) - .map(function (keyModifier) { return ("$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; - } - var handlerCode = isMethodPath - ? handler.value + '($event)' - : isFunctionExpression - ? ("(" + (handler.value) + ")($event)") - : handler.value; - return ("function($event){" + code + handlerCode + "}") - } -} - -function genKeyFilter (keys) { - return ("if(!('button' in $event)&&" + (keys.map(genFilterCode).join('&&')) + ")return null;") -} - -function genFilterCode (key) { - var keyVal = parseInt(key, 10); - if (keyVal) { - return ("$event.keyCode!==" + keyVal) - } - var code = keyCodes[key]; - return ( - "_k($event.keyCode," + - (JSON.stringify(key)) + "," + - (JSON.stringify(code)) + "," + - "$event.key)" - ) -} - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ - - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - - - - - -// 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. - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (hasOwn(target, key)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn$1( - '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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ - - -/** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ -function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - if (!vm) { - warn$1( - "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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } 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) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && typeof childVal !== 'function') { - process.env.NODE_ENV !== 'production' && warn$1( - 'The "data" option should be a function ' + - 'that returns a per-instance value in component ' + - 'definitions.', - vm - ); - - return parentVal - } - return mergeDataOrFn.call(this, parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -function assertObjectType (name, value, vm) { - if (!isPlainObject(value)) { - warn$1( - "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. - */ - - -/** - * Resolve an asset. - * This function is used because child instances need access - * to assets defined in its ancestor chain. - */ - -/* */ - -/* */ - -/* */ - -function on (el, dir) { - if (process.env.NODE_ENV !== 'production' && dir.modifiers) { - warn$1("v-on without argument does not support modifiers."); - } - el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; -} - -/* */ - -function bind$1 (el, dir) { - el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") - }; -} - -/* */ - -var baseDirectives = { - on: on, - bind: bind$1, - cloak: noop -}; - -/* */ - -var CodegenState = function CodegenState (options) { - 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); - var isReservedTag = options.isReservedTag || no; - this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; - this.onceId = 0; - this.staticRenderFns = []; -}; - - - -function generate ( - ast, - options -) { - var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; - return { - render: ("with(this){return " + code + "}"), - staticRenderFns: state.staticRenderFns - } -} - -function genElement (el, state) { - 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) { - return genChildren(el, state) || 'void 0' - } else if (el.tag === 'slot') { - return genSlot(el, state) - } else { - // component or element - var code; - if (el.component) { - code = genComponent(el.component, el, state); - } else { - var data = el.plain ? undefined : genData(el, state); - - var children = el.inlineTemplate ? null : genChildren(el, state, true); - code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; - } - // module transforms - for (var i = 0; i < state.transforms.length; i++) { - code = state.transforms[i](el, code); - } - return code - } -} - -// hoist static sub-trees out -function genStatic (el, state) { - el.staticProcessed = true; - state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); - return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") -} - -// v-once -function genOnce (el, state) { - el.onceProcessed = true; - if (el.if && !el.ifProcessed) { - return genIf(el, state) - } else if (el.staticInFor) { - var key = ''; - var parent = el.parent; - while (parent) { - if (parent.for) { - key = parent.key; - break - } - parent = parent.parent; - } - if (!key) { - process.env.NODE_ENV !== 'production' && state.warn( - "v-once can only be used inside v-for that is keyed. " - ); - return genElement(el, state) - } - return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")") - } else { - return genStatic(el, state) - } -} - -function genIf ( - el, - state, - altGen, - altEmpty -) { - el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) -} - -function genIfConditions ( - conditions, - state, - altGen, - altEmpty -) { - if (!conditions.length) { - return altEmpty || '_e()' - } - - var 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) - } -} - -function genFor ( - el, - state, - altGen, - altHelper -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - - if (process.env.NODE_ENV !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.", - true /* tip */ - ); - } - - el.forProcessed = true; // avoid recursion - return (altHelper || '_l') + "((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + ((altGen || genElement)(el, state)) + - '})' -} - -function genData (el, state) { - var data = '{'; - - // directives first. - // directives may mutate the el's other properties before they are generated. - var 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 (var 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, state.warn)) + ","; - } - if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += "slot:" + (el.slotTarget) + ","; - } - // scoped slots - if (el.scopedSlots) { - data += (genScopedSlots(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) { - var inlineTemplate = genInlineTemplate(el, state); - if (inlineTemplate) { - data += inlineTemplate + ","; - } - } - data = data.replace(/,$/, '') + '}'; - // 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, state) { - var dirs = el.directives; - if (!dirs) { return } - var res = 'directives:['; - var hasRuntime = false; - var i, l, dir, needRuntime; - for (i = 0, l = dirs.length; i < l; i++) { - dir = dirs[i]; - needRuntime = true; - var gen = 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.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},"; - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el, state) { - var ast = el.children[0]; - if (process.env.NODE_ENV !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.'); - } - if (ast.type === 1) { - var inlineRenderFns = generate(ast, state.options); - return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") - } -} - -function genScopedSlots ( - slots, - state -) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { - return genScopedSlot(key, slots[key], state) - }).join(',')) + "])") -} - -function genScopedSlot ( - key, - el, - state -) { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - var fn = "function(" + (String(el.slotScope)) + "){" + - "return " + (el.tag === 'template' - ? el.if - ? ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") - : genChildren(el, state) || 'undefined' - : genElement(el, state)) + "}"; - return ("{key:" + key + ",fn:" + fn + "}") -} - -function genForScopedSlot ( - key, - el, - state -) { - var exp = el.for; - var alias = el.alias; - var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; - var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + - "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genScopedSlot(key, el, state)) + - '})' -} - -function genChildren ( - el, - state, - checkSkip, - altGenElement, - altGenNode -) { - var children = el.children; - if (children.length) { - var el$1 = children[0]; - // optimize single v-for - if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot' - ) { - return (altGenElement || genElement)(el$1, state) - } - var normalizationType = checkSkip - ? getNormalizationType(children, state.maybeComponent) - : 0; - var gen = altGenNode || genNode; - return ("[" + (children.map(function (c) { return 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, - maybeComponent -) { - var res = 0; - for (var i = 0; i < children.length; i++) { - var el = children[i]; - if (el.type !== 1) { - continue - } - if (needsNormalization(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return needsNormalization(c.block); }))) { - res = 2; - break - } - if (maybeComponent(el) || - (el.ifConditions && el.ifConditions.some(function (c) { return maybeComponent(c.block); }))) { - res = 1; - } - } - return res -} - -function needsNormalization (el) { - return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' -} - -function genNode (node, state) { - if (node.type === 1) { - return genElement(node, state) - } if (node.type === 3 && node.isComment) { - return genComment(node) - } else { - return genText(node) - } -} - -function genText (text) { - return ("_v(" + (text.type === 2 - ? text.expression // no need for () because already wrapped in _s() - : transformSpecialNewlines(JSON.stringify(text.text))) + ")") -} - -function genComment (comment) { - return ("_e(" + (JSON.stringify(comment.text)) + ")") -} - -function genSlot (el, state) { - var slotName = el.slotName || '"default"'; - var children = genChildren(el, state); - var res = "_t(" + slotName + (children ? ("," + children) : ''); - var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); - var bind$$1 = el.attrsMap['v-bind']; - if ((attrs || bind$$1) && !children) { - res += ",null"; - } - if (attrs) { - res += "," + attrs; - } - if (bind$$1) { - res += (attrs ? '' : ',null') + "," + bind$$1; - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName, - el, - state -) { - var children = el.inlineTemplate ? null : genChildren(el, state, true); - return ("_c(" + componentName + "," + (genData(el, state)) + (children ? ("," + children) : '') + ")") -} - -function genProps (props) { - var res = ''; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text) { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} - -/* */ - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -var 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 -var unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)'); - -// check valid identifier for v-for -var identRE = /[A-Za-z_$][\w$]*/; - -// strip strings in expressions -var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g; - -// detect problematic expressions in a template -function detectErrors (ast) { - var errors = []; - if (ast) { - checkNode(ast, errors); - } - return errors -} - -function checkNode (node, errors) { - if (node.type === 1) { - for (var name in node.attrsMap) { - if (dirRE.test(name)) { - var value = node.attrsMap[name]; - if (value) { - if (name === 'v-for') { - checkFor(node, ("v-for=\"" + value + "\""), errors); - } else if (onRE.test(name)) { - checkEvent(value, (name + "=\"" + value + "\""), errors); - } else { - checkExpression(value, (name + "=\"" + value + "\""), errors); - } - } - } - } - if (node.children) { - for (var i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors); - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors); - } -} - -function checkEvent (exp, text, errors) { - var stipped = exp.replace(stripStringRE, ''); - var keywordMatch = stipped.match(unaryOperatorsRE); - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - "avoid using JavaScript unary operator as property name: " + - "\"" + (keywordMatch[0]) + "\" in expression " + (text.trim()) - ); - } - checkExpression(exp, text, errors); -} - -function checkFor (node, text, errors) { - checkExpression(node.for || '', text, errors); - checkIdentifier(node.alias, 'v-for alias', text, errors); - checkIdentifier(node.iterator1, 'v-for iterator', text, errors); - checkIdentifier(node.iterator2, 'v-for iterator', text, errors); -} - -function checkIdentifier (ident, type, text, errors) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(("invalid " + type + " \"" + ident + "\" in expression: " + (text.trim()))); - } -} - -function checkExpression (exp, text, errors) { - try { - new Function(("return " + exp)); - } catch (e) { - var keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE); - if (keywordMatch) { - errors.push( - "avoid using JavaScript keyword as property name: " + - "\"" + (keywordMatch[0]) + "\"\n Raw expression: " + (text.trim()) - ); - } else { - errors.push( - "invalid expression: " + (e.message) + " in\n\n" + - " " + exp + "\n\n" + - " Raw expression: " + (text.trim()) + "\n" - ); - } - } -} - -/* */ - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err: err, code: code }); - return noop - } -} - -function createCompileToFunctionFn (compile) { - var cache = Object.create(null); - - return function compileToFunctions ( - template, - options, - vm - ) { - options = extend({}, options); - var warn$$1 = options.warn || warn$1; - delete options.warn; - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - // detect possible CSP restriction - try { - new Function('return 1'); - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn$$1( - '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 - var key = options.delimiters - ? String(options.delimiters) + template - : template; - if (cache[key]) { - return cache[key] - } - - // compile - var compiled = compile(template, options); - - // check compilation errors/tips - if (process.env.NODE_ENV !== 'production') { - if (compiled.errors && compiled.errors.length) { - warn$$1( - "Error compiling template:\n\n" + template + "\n\n" + - compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n', - vm - ); - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(function (msg) { return tip(msg, vm); }); - } - } - - // turn code into functions - var res = {}; - var fnGenErrors = []; - res.render = createFunction(compiled.render, fnGenErrors); - res.staticRenderFns = compiled.staticRenderFns.map(function (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 (process.env.NODE_ENV !== 'production') { - if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn$$1( - "Failed to generate render function:\n\n" + - fnGenErrors.map(function (ref) { - var err = ref.err; - var code = ref.code; - - return ((err.toString()) + " in\n\n" + code + "\n"); - }).join('\n'), - vm - ); - } - } - - return (cache[key] = res) - } -} - -/* */ - -function createCompilerCreator (baseCompile) { - return function createCompiler (baseOptions) { - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip) { - (tip ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } - - return { - compile: compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} - -/* */ - -// `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. -var createCompiler = createCompilerCreator(function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}); - -/* */ - -function transformNode (el, options) { - var warn = options.warn || baseWarn; - var staticClass = getAndRemoveAttr(el, 'class'); - var ref = parseStaticClass(staticClass, options); - var dynamic = ref.dynamic; - var classResult = ref.classResult; - if (process.env.NODE_ENV !== 'production' && dynamic && staticClass) { - warn( - "class=\"" + staticClass + "\": " + - 'Interpolation inside attributes has been deprecated. ' + - 'Use v-bind or the colon shorthand instead.' - ); - } - if (!dynamic && classResult) { - el.staticClass = classResult; - } - var classBinding = getBindingAttr(el, 'class', false /* getStatic */); - if (classBinding) { - el.classBinding = classBinding; - } else if (dynamic) { - el.classBinding = classResult; - } -} - -function genData$1 (el) { - var data = ''; - if (el.staticClass) { - data += "staticClass:" + (el.staticClass) + ","; - } - if (el.classBinding) { - data += "class:" + (el.classBinding) + ","; - } - return data -} - -function parseStaticClass (staticClass, options) { - // "a b c" -> ["a", "b", "c"] => staticClass: ["a", "b", "c"] - // "a {{x}} c" -> ["a", x, "c"] => classBinding: '["a", x, "c"]' - var dynamic = false; - var classResult = ''; - if (staticClass) { - var classList = staticClass.trim().split(' ').map(function (name) { - var result = parseText(name, options.delimiters); - if (result) { - dynamic = true; - return result - } - return JSON.stringify(name) - }); - if (classList.length) { - classResult = '[' + classList.join(',') + ']'; - } - } - return { dynamic: dynamic, classResult: classResult } -} - -var klass = { - staticKeys: ['staticClass'], - transformNode: transformNode, - genData: genData$1 -}; - -/* */ - -var normalize = cached(camelize); - -function transformNode$1 (el, options) { - var warn = options.warn || baseWarn; - var staticStyle = getAndRemoveAttr(el, 'style'); - var ref = parseStaticStyle(staticStyle, options); - var dynamic = ref.dynamic; - var styleResult = ref.styleResult; - if (process.env.NODE_ENV !== 'production' && dynamic) { - warn( - "style=\"" + (String(staticStyle)) + "\": " + - 'Interpolation inside attributes has been deprecated. ' + - 'Use v-bind or the colon shorthand instead.' - ); - } - if (!dynamic && styleResult) { - el.staticStyle = styleResult; - } - var styleBinding = getBindingAttr(el, 'style', false /* getStatic */); - if (styleBinding) { - el.styleBinding = styleBinding; - } else if (dynamic) { - el.styleBinding = styleResult; - } -} - -function genData$2 (el) { - var data = ''; - if (el.staticStyle) { - data += "staticStyle:" + (el.staticStyle) + ","; - } - if (el.styleBinding) { - data += "style:" + (el.styleBinding) + ","; - } - return data -} - -function parseStaticStyle (staticStyle, options) { - // "width: 200px; height: 200px;" -> {width: 200, height: 200} - // "width: 200px; height: {{y}}" -> {width: 200, height: y} - var dynamic = false; - var styleResult = ''; - if (staticStyle) { - var styleList = staticStyle.trim().split(';').map(function (style) { - var result = style.trim().split(':'); - if (result.length !== 2) { - return - } - var key = normalize(result[0].trim()); - var value = result[1].trim(); - var dynamicValue = parseText(value, options.delimiters); - if (dynamicValue) { - dynamic = true; - return key + ':' + dynamicValue - } - return key + ':' + JSON.stringify(value) - }).filter(function (result) { return result; }); - if (styleList.length) { - styleResult = '{' + styleList.join(',') + '}'; - } - } - return { dynamic: dynamic, styleResult: styleResult } -} - -var style = { - staticKeys: ['staticStyle'], - transformNode: transformNode$1, - genData: genData$2 -}; - -/* */ - -var normalize$1 = cached(camelize); - -function normalizeKeyName (str) { - if (str.match(/^v\-/)) { - return str.replace(/(v-[a-z\-]+\:)([a-z\-]+)$/i, function ($, directive, prop) { - return directive + normalize$1(prop) - }) - } - return normalize$1(str) -} - -function transformNode$2 (el, options) { - if (Array.isArray(el.attrsList)) { - el.attrsList.forEach(function (attr) { - if (attr.name && attr.name.match(/\-/)) { - var realName = normalizeKeyName(attr.name); - if (el.attrsMap) { - el.attrsMap[realName] = el.attrsMap[attr.name]; - delete el.attrsMap[attr.name]; - } - attr.name = realName; - } - }); - } -} -var props = { - transformNode: transformNode$2 -}; - -/* */ - -function preTransformNode (el, options) { - if (el.tag === 'cell' && !el.attrsList.some(function (item) { return item.name === 'append'; })) { - el.attrsMap.append = 'tree'; - el.attrsList.push({ name: 'append', value: 'tree' }); - } - if (el.attrsMap.append === 'tree') { - el.appendAsTree = true; - } -} - -function genData$3 (el) { - return el.appendAsTree ? "appendAsTree:true," : '' -} - -var append = { - staticKeys: ['appendAsTree'], - preTransformNode: preTransformNode, - genData: genData$3 -}; - -var modules = [ - klass, - style, - props, - append -]; - -/* */ - -function model ( - el, - dir, - _warn -) { - if (el.tag === 'input' || el.tag === 'textarea') { - genDefaultModel(el, dir.value, dir.modifiers); - } else { - genComponentModel(el, dir.value, dir.modifiers); - } -} - -function genDefaultModel ( - el, - value, - modifiers -) { - var ref = modifiers || {}; - var lazy = ref.lazy; - var trim = ref.trim; - var number = ref.number; - var event = lazy ? 'change' : 'input'; - - var valueExpression = "$event.target.attr.value" + (trim ? '.trim()' : ''); - if (number) { - valueExpression = "_n(" + valueExpression + ")"; - } - - var code = genAssignmentCode(value, valueExpression); - addAttr(el, 'value', ("(" + value + ")")); - addHandler(el, event, code, null, true); -} - -var directives = { - model: model -}; - -/* globals document */ - -var isReservedTag = makeMap( - 'template,script,style,element,content,slot,link,meta,svg,view,' + - 'a,div,img,image,text,span,input,switch,textarea,spinner,select,' + - 'slider,slider-neighbor,indicator,canvas,' + - 'list,cell,header,loading,loading-indicator,refresh,scrollable,scroller,' + - 'video,web,embed,tabbar,tabheader,datepicker,timepicker,marquee,countdown', - true -); - -// Elements that you can, intentionally, leave open (and which close themselves) -// more flexible than web -var canBeLeftOpenTag$1 = makeMap( - 'web,spinner,switch,video,textarea,canvas,' + - 'indicator,marquee,countdown', - true -); - -var isRuntimeComponent = makeMap( - 'richtext,trisition,trisition-group', - true -); - -var isUnaryTag$1 = makeMap( - 'embed,img,image,input,link,meta', - true -); - -function mustUseProp () { /* console.log('mustUseProp') */ } -function getTagNamespace () { /* console.log('getTagNamespace') */ } - -/* */ - -var baseOptions = { - modules: modules, - directives: directives, - isUnaryTag: isUnaryTag$1, - mustUseProp: mustUseProp, - canBeLeftOpenTag: canBeLeftOpenTag$1, - isReservedTag: isReservedTag, - getTagNamespace: getTagNamespace, - preserveWhitespace: false, - staticKeys: genStaticKeys(modules) -}; - -var ref = createCompiler(baseOptions); -var compile = ref.compile; - -exports.compile = compile; diff --git a/packages/weex-template-compiler/index.js b/packages/weex-template-compiler/index.js deleted file mode 100644 index f50a0e659e9..00000000000 --- a/packages/weex-template-compiler/index.js +++ /dev/null @@ -1,17 +0,0 @@ -try { - var vueVersion = require('weex-vue-framework').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 you are using weex-vue-loader, re-installing them should bump ' + packageName + ' to the latest.\n' - ) -} - -module.exports = require('./build') diff --git a/packages/weex-template-compiler/package.json b/packages/weex-template-compiler/package.json deleted file mode 100644 index 19c493a216e..00000000000 --- a/packages/weex-template-compiler/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "weex-template-compiler", - "version": "2.4.2-weex.1", - "description": "Weex template compiler for Vue 2.0", - "main": "index.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/weex-template-compiler#readme", - "dependencies": { - "he": "^1.1.0" - } -} diff --git a/packages/weex-vue-framework/README.md b/packages/weex-vue-framework/README.md deleted file mode 100644 index 9f3183185a6..00000000000 --- a/packages/weex-vue-framework/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# weex-vue-framework - -> This package is auto-generated. For pull requests please see [src/platforms/weex/entry-framework.js](https://github.com/vuejs/vue/blob/dev/src/platforms/weex/entry-framework.js). diff --git a/packages/weex-vue-framework/factory.js b/packages/weex-vue-framework/factory.js deleted file mode 100644 index 266cba3f412..00000000000 --- a/packages/weex-vue-framework/factory.js +++ /dev/null @@ -1,6984 +0,0 @@ -'use strict'; - -module.exports = function weexFactory (exports, document) { - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real 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. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * 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/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ - - -/** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var 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 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '<Root>' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var 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(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - - -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - 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'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -/** - * Defer a task to execute it asynchronously. - */ -var nextTick = (function () { - var callbacks = []; - var pending = false; - var timerFunc; - - function nextTickHandler () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } - } - - // An asynchronous deferring mechanism. - // In pre 2.4, we used to use microtasks (Promise/MutationObserver) - // but microtasks actually has too high a priority and fires in between - // supposedly sequential events (e.g. #4521, #6690) or even between - // bubbling of the same event (#6566). Technically setImmediate should be - // the ideal choice, but it's not available everywhere; and the only polyfill - // that consistently queues the callback after all DOM events triggered in the - // same loop is by using MessageChannel. - /* istanbul ignore if */ - if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - timerFunc = function () { - setImmediate(nextTickHandler); - }; - } else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' - )) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = nextTickHandler; - timerFunc = function () { - port.postMessage(1); - }; - } else - /* istanbul ignore next */ - if (typeof Promise !== 'undefined' && isNative(Promise)) { - // use microtask in non-DOM environments, e.g. Weex - var p = Promise.resolve(); - timerFunc = function () { - p.then(nextTickHandler); - }; - } else { - // fallback to setTimeout - timerFunc = function () { - setTimeout(nextTickHandler, 0); - }; - } - - return function queueNextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - 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(function (resolve, reject) { - _resolve = resolve; - }) - } - } -})(); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -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 () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - - -var uid$1 = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid$1++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// 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; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = 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; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - 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. -function cloneVNode (vnode, deep) { - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - 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.isCloned = true; - if (deep && vnode.children) { - cloned.children = cloneVNodes(vnode.children); - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - 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. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -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. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - 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 = !shallow && 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 (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (hasOwn(target, key)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - 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) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } 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) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn.call(this, parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * 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, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && 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, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else if (process.env.NODE_ENV !== 'production') { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else if (process.env.NODE_ENV !== 'production') { - 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, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if (process.env.NODE_ENV !== 'production' && inject) { - 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) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - 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. - */ -function mergeOptions ( - parent, - child, - vm -) { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - 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. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (process.env.NODE_ENV !== 'production' && 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 typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.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) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var 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 = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * 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) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -if (process.env.NODE_ENV !== 'production') { - var 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,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -var mark; -var measure; - -if (process.env.NODE_ENV !== 'production') { - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - var invoker; - var 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; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - if (process.env.NODE_ENV !== 'production') { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(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, - hash, - key, - altKey, - preserve -) { - 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 -} - -/* */ - -// 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. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.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. -function normalizeChildren (children) { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.isArray(children) - ? normalizeArrayChildren(children) - : undefined -} - -function isTextNode (node) { - return isDef(node) && isDef(node.text) && isFalse(node.isComment) -} - -function normalizeArrayChildren (children, nestedIndex) { - var res = []; - var 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 (Array.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 -} - -/* */ - -function ensureCtor (comp, base) { - if ( - comp.__esModule || - (hasSymbol && comp[Symbol.toStringTag] === 'Module') - ) { - comp = comp.default; - } - return isObject(comp) - ? base.extend(comp) - : comp -} - -function createAsyncPlaceholder ( - factory, - data, - context, - children, - tag -) { - var node = createEmptyVNode(); - node.asyncFactory = factory; - node.asyncMeta = { data: data, context: context, children: children, tag: tag }; - return node -} - -function resolveAsyncComponent ( - factory, - baseCtor, - context -) { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context); - } else { - var contexts = factory.contexts = [context]; - var sync = true; - - var forceRender = function () { - for (var i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate(); - } - }; - - var resolve = once(function (res) { - // 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(); - } - }); - - var reject = once(function (reason) { - process.env.NODE_ENV !== 'production' && warn( - "Failed to resolve async component: " + (String(factory)) + - (reason ? ("\nReason: " + reason) : '') - ); - if (isDef(factory.errorComp)) { - factory.error = true; - forceRender(); - } - }); - - var res = factory(resolve, reject); - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject); - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(function () { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true; - forceRender(); - } - }, res.delay || 200); - } - } - - if (isDef(res.timeout)) { - setTimeout(function () { - if (isUndef(factory.resolved)) { - reject( - process.env.NODE_ENV !== 'production' - ? ("timeout (" + (res.timeout) + "ms)") - : null - ); - } - }, res.timeout); - } - } - } - - sync = false; - // return in case resolved synchronously - return factory.loading - ? factory.loadingComp - : factory.resolved - } -} - -/* */ - -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - -function getFirstComponentChild (children) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; i++) { - var c = children[i]; - if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { - return c - } - } - } -} - -/* */ - -/* */ - -function initEvents (vm) { - vm._events = Object.create(null); - vm._hasHookEvent = false; - // init parent attached events - var listeners = vm.$options._parentListeners; - if (listeners) { - updateComponentListeners(vm, listeners); - } -} - -var target; - -function add (event, fn, once) { - if (once) { - target.$once(event, fn); - } else { - target.$on(event, fn); - } -} - -function remove$1 (event, fn) { - target.$off(event, fn); -} - -function updateComponentListeners ( - vm, - listeners, - oldListeners -) { - target = vm; - updateListeners(listeners, oldListeners || {}, add, remove$1, vm); -} - -function eventsMixin (Vue) { - var hookRE = /^hook:/; - Vue.prototype.$on = function (event, fn) { - var this$1 = this; - - var vm = this; - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$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, fn) { - var vm = 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, fn) { - var this$1 = this; - - var vm = this; - // all - if (!arguments.length) { - vm._events = Object.create(null); - return vm - } - // array of events - if (Array.isArray(event)) { - for (var i = 0, l = event.length; i < l; i++) { - this$1.$off(event[i], fn); - } - return vm - } - // specific event - var cbs = vm._events[event]; - if (!cbs) { - return vm - } - if (arguments.length === 1) { - vm._events[event] = null; - return vm - } - if (fn) { - // specific handler - var cb; - var i$1 = cbs.length; - while (i$1--) { - cb = cbs[i$1]; - if (cb === fn || cb.fn === fn) { - cbs.splice(i$1, 1); - break - } - } - } - return vm - }; - - Vue.prototype.$emit = function (event) { - var vm = this; - if (process.env.NODE_ENV !== 'production') { - var 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 + "\"." - ); - } - } - var cbs = vm._events[event]; - if (cbs) { - cbs = cbs.length > 1 ? toArray(cbs) : cbs; - var args = toArray(arguments, 1); - for (var i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args); - } catch (e) { - handleError(e, vm, ("event handler for \"" + event + "\"")); - } - } - } - return vm - }; -} - -/* */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -function resolveSlots ( - children, - context -) { - var slots = {}; - if (!children) { - return slots - } - var defaultSlot = []; - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - var 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.functionalContext === context) && - data && data.slot != null - ) { - var name = child.data.slot; - var slot = (slots[name] || (slots[name] = [])); - if (child.tag === 'template') { - slot.push.apply(slot, child.children); - } else { - slot.push(child); - } - } else { - defaultSlot.push(child); - } - } - // ignore whitespace - if (!defaultSlot.every(isWhitespace)) { - slots.default = defaultSlot; - } - return slots -} - -function isWhitespace (node) { - return node.isComment || node.text === ' ' -} - -function resolveScopedSlots ( - fns, // see flow/vnode - res -) { - res = res || {}; - for (var i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res); - } else { - res[fns[i].key] = fns[i].fn; - } - } - return res -} - -/* */ - -var activeInstance = null; -var isUpdatingChildComponent = false; - -function initLifecycle (vm) { - var options = vm.$options; - - // locate first non-abstract parent - var 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._watcher = null; - vm._inactive = null; - vm._directInactive = false; - vm._isMounted = false; - vm._isDestroyed = false; - vm._isBeingDestroyed = false; -} - -function lifecycleMixin (Vue) { - Vue.prototype._update = function (vnode, hydrating) { - var vm = this; - if (vm._isMounted) { - callHook(vm, 'beforeUpdate'); - } - var prevEl = vm.$el; - var prevVnode = vm._vnode; - var prevActiveInstance = activeInstance; - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ); - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null; - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode); - } - activeInstance = prevActiveInstance; - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el; - } - // updated hook is called by the scheduler to ensure that children are - // updated in a parent's updated hook. - }; - - Vue.prototype.$forceUpdate = function () { - var vm = this; - if (vm._watcher) { - vm._watcher.update(); - } - }; - - Vue.prototype.$destroy = function () { - var vm = this; - if (vm._isBeingDestroyed) { - return - } - callHook(vm, 'beforeDestroy'); - vm._isBeingDestroyed = true; - // remove self from parent - var parent = vm.$parent; - if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { - remove(parent.$children, vm); - } - // teardown watchers - if (vm._watcher) { - vm._watcher.teardown(); - } - var i = vm._watchers.length; - while (i--) { - vm._watchers[i].teardown(); - } - // 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; - } - }; -} - -function mountComponent ( - vm, - el, - hydrating -) { - vm.$el = el; - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode; - if (process.env.NODE_ENV !== 'production') { - /* 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'); - - var updateComponent; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - updateComponent = function () { - var name = vm._name; - var id = vm._uid; - var startTag = "vue-perf-start:" + id; - var endTag = "vue-perf-end:" + id; - - mark(startTag); - var 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 = function () { - vm._update(vm._render(), hydrating); - }; - } - - vm._watcher = new Watcher(vm, updateComponent, noop); - hydrating = false; - - // 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 -} - -function updateChildComponent ( - vm, - propsData, - listeners, - parentVnode, - renderChildren -) { - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = true; - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - var hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ); - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject; - vm.$listeners = listeners || emptyObject; - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false; - var props = vm._props; - var propKeys = vm.$options._propKeys || []; - for (var i = 0; i < propKeys.length; i++) { - var key = propKeys[i]; - props[key] = validateProp(key, vm.$options.props, propsData, vm); - } - observerState.shouldConvert = true; - // keep a copy of raw propsData - vm.$options.propsData = propsData; - } - - // update listeners - if (listeners) { - var oldListeners = vm.$options._parentListeners; - vm.$options._parentListeners = listeners; - updateComponentListeners(vm, listeners, oldListeners); - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context); - vm.$forceUpdate(); - } - - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = false; - } -} - -function isInInactiveTree (vm) { - while (vm && (vm = vm.$parent)) { - if (vm._inactive) { return true } - } - return false -} - -function activateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = false; - if (isInInactiveTree(vm)) { - return - } - } else if (vm._directInactive) { - return - } - if (vm._inactive || vm._inactive === null) { - vm._inactive = false; - for (var i = 0; i < vm.$children.length; i++) { - activateChildComponent(vm.$children[i]); - } - callHook(vm, 'activated'); - } -} - -function deactivateChildComponent (vm, direct) { - if (direct) { - vm._directInactive = true; - if (isInInactiveTree(vm)) { - return - } - } - if (!vm._inactive) { - vm._inactive = true; - for (var i = 0; i < vm.$children.length; i++) { - deactivateChildComponent(vm.$children[i]); - } - callHook(vm, 'deactivated'); - } -} - -function callHook (vm, hook) { - var handlers = vm.$options[hook]; - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm); - } catch (e) { - handleError(e, vm, (hook + " hook")); - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook); - } -} - -/* */ - - -var MAX_UPDATE_COUNT = 100; - -var queue = []; -var activatedChildren = []; -var has = {}; -var circular = {}; -var waiting = false; -var flushing = false; -var index = 0; - -/** - * Reset the scheduler's state. - */ -function resetSchedulerState () { - index = queue.length = activatedChildren.length = 0; - has = {}; - if (process.env.NODE_ENV !== 'production') { - circular = {}; - } - waiting = flushing = false; -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - flushing = true; - var 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(function (a, b) { return a.id - b.id; }); - - // 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]; - 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] > 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 - var activatedQueue = activatedChildren.slice(); - var updatedQueue = queue.slice(); - - resetSchedulerState(); - - // call component updated and activated hooks - callActivatedHooks(activatedQueue); - callUpdatedHooks(updatedQueue); - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } -} - -function callUpdatedHooks (queue) { - var i = queue.length; - while (i--) { - var watcher = queue[i]; - var vm = watcher.vm; - if (vm._watcher === watcher && vm._isMounted) { - 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. - */ -function queueActivatedComponent (vm) { - // 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 (var 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. - */ -function queueWatcher (watcher) { - var id = watcher.id; - if (has[id] == null) { - 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. - var 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; - nextTick(flushSchedulerQueue); - } - } -} - -/* */ - -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. - */ -var Watcher = function Watcher ( - vm, - expOrFn, - cb, - options -) { - this.vm = vm; - vm._watchers.push(this); - // options - if (options) { - this.deep = !!options.deep; - this.user = !!options.user; - this.lazy = !!options.lazy; - this.sync = !!options.sync; - } else { - this.deep = this.user = this.lazy = this.sync = false; - } - 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.expression = process.env.NODE_ENV !== 'production' - ? expOrFn.toString() - : ''; - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn; - } else { - this.getter = parsePath(expOrFn); - if (!this.getter) { - this.getter = function () {}; - process.env.NODE_ENV !== 'production' && 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. - */ -Watcher.prototype.get = function get () { - pushTarget(this); - var value; - var vm = this.vm; - try { - value = this.getter.call(vm, vm); - } catch (e) { - 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. - */ -Watcher.prototype.addDep = function addDep (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.cleanupDeps = function cleanupDeps () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - var dep = this$1.deps[i]; - if (!this$1.newDepIds.has(dep.id)) { - dep.removeSub(this$1); - } - } - 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. - */ -Watcher.prototype.update = function 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. - */ -Watcher.prototype.run = function run () { - 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. - isObject(value) || - this.deep - ) { - // set new value - var oldValue = this.value; - this.value = value; - if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - } -}; - -/** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ -Watcher.prototype.evaluate = function evaluate () { - this.value = this.get(); - this.dirty = false; -}; - -/** - * Depend on all deps collected by this watcher. - */ -Watcher.prototype.depend = function depend () { - var this$1 = this; - - var i = this.deps.length; - while (i--) { - this$1.deps[i].depend(); - } -}; - -/** - * Remove self from all dependencies' subscriber list. - */ -Watcher.prototype.teardown = function teardown () { - var this$1 = this; - - 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. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this); - } - var i = this.deps.length; - while (i--) { - this$1.deps[i].removeSub(this$1); - } - this.active = false; - } -}; - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -var seenObjects = new _Set(); -function traverse (val) { - seenObjects.clear(); - _traverse(val, seenObjects); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - return - } - if (val.__ob__) { - var 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 { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var sharedPropertyDefinition = { - enumerable: true, - configurable: true, - get: noop, - set: noop -}; - -function proxy (target, sourceKey, key) { - sharedPropertyDefinition.get = function proxyGetter () { - return this[sourceKey][key] - }; - sharedPropertyDefinition.set = function proxySetter (val) { - this[sourceKey][key] = val; - }; - Object.defineProperty(target, key, sharedPropertyDefinition); -} - -function initState (vm) { - vm._watchers = []; - var opts = vm.$options; - if (opts.props) { initProps(vm, opts.props); } - if (opts.methods) { initMethods(vm, opts.methods); } - if (opts.data) { - initData(vm); - } else { - observe(vm._data = {}, true /* asRootData */); - } - if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch && opts.watch !== nativeWatch) { - initWatch(vm, opts.watch); - } -} - -function initProps (vm, propsOptions) { - var propsData = vm.$options.propsData || {}; - var props = vm._props = {}; - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - var keys = vm.$options._propKeys = []; - var isRoot = !vm.$parent; - // root instance props should be converted - observerState.shouldConvert = isRoot; - var loop = function ( key ) { - keys.push(key); - var value = validateProp(key, propsOptions, propsData, vm); - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - var 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, function () { - if (vm.$parent && !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 - ); - } - }); - } else { - defineReactive(props, key, value); - } - // 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); - } - }; - - for (var key in propsOptions) loop( key ); - observerState.shouldConvert = true; -} - -function initData (vm) { - var data = vm.$options.data; - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {}; - if (!isPlainObject(data)) { - data = {}; - process.env.NODE_ENV !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', - vm - ); - } - // proxy data on instance - var keys = Object.keys(data); - var props = vm.$options.props; - var methods = vm.$options.methods; - var i = keys.length; - while (i--) { - var key = keys[i]; - if (process.env.NODE_ENV !== 'production') { - if (methods && hasOwn(methods, key)) { - warn( - ("Method \"" + key + "\" has already been defined as a data property."), - vm - ); - } - } - if (props && hasOwn(props, key)) { - process.env.NODE_ENV !== 'production' && 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 - observe(data, true /* asRootData */); -} - -function getData (data, vm) { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, "data()"); - return {} - } -} - -var computedWatcherOptions = { lazy: true }; - -function initComputed (vm, computed) { - var watchers = vm._computedWatchers = Object.create(null); - // computed properties are just getters during SSR - var isSSR = isServerRendering(); - - for (var key in computed) { - var userDef = computed[key]; - var getter = typeof userDef === 'function' ? userDef : userDef.get; - if (process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production') { - 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); - } - } - } -} - -function defineComputed ( - target, - key, - userDef -) { - var shouldCache = !isServerRendering(); - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef; - sharedPropertyDefinition.set = noop; - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop; - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop; - } - if (process.env.NODE_ENV !== 'production' && - 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 () { - var watcher = this._computedWatchers && this._computedWatchers[key]; - if (watcher) { - if (watcher.dirty) { - watcher.evaluate(); - } - if (Dep.target) { - watcher.depend(); - } - return watcher.value - } - } -} - -function initMethods (vm, methods) { - var props = vm.$options.props; - for (var key in methods) { - if (process.env.NODE_ENV !== 'production') { - if (methods[key] == null) { - warn( - "Method \"" + key + "\" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm); - } -} - -function initWatch (vm, watch) { - for (var key in watch) { - var handler = watch[key]; - if (Array.isArray(handler)) { - for (var i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]); - } - } else { - createWatcher(vm, key, handler); - } - } -} - -function createWatcher ( - vm, - keyOrFn, - handler, - options -) { - if (isPlainObject(handler)) { - options = handler; - handler = handler.handler; - } - if (typeof handler === 'string') { - handler = vm[handler]; - } - return vm.$watch(keyOrFn, handler, options) -} - -function stateMixin (Vue) { - // flow somehow has problems with directly declared definition object - // when using Object.defineProperty, so we have to procedurally build up - // the object here. - var dataDef = {}; - dataDef.get = function () { return this._data }; - var propsDef = {}; - propsDef.get = function () { return this._props }; - if (process.env.NODE_ENV !== 'production') { - dataDef.set = function (newData) { - 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, - cb, - options - ) { - var vm = this; - if (isPlainObject(cb)) { - return createWatcher(vm, expOrFn, cb, options) - } - options = options || {}; - options.user = true; - var watcher = new Watcher(vm, expOrFn, cb, options); - if (options.immediate) { - cb.call(vm, watcher.value); - } - return function unwatchFn () { - watcher.teardown(); - } - }; -} - -/* */ - -function initProvide (vm) { - var provide = vm.$options.provide; - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide; - } -} - -function initInjections (vm) { - var result = resolveInject(vm.$options.inject, vm); - if (result) { - observerState.shouldConvert = false; - Object.keys(result).forEach(function (key) { - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, key, result[key], function () { - 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]); - } - }); - observerState.shouldConvert = true; - } -} - -function resolveInject (inject, vm) { - if (inject) { - // inject is :any because flow is not smart enough to figure out cached - var result = Object.create(null); - var keys = hasSymbol - ? Reflect.ownKeys(inject).filter(function (key) { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var provideKey = inject[key].from; - var source = vm; - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey]; - break - } - source = source.$parent; - } - if (!source) { - if ('default' in inject[key]) { - var provideDefault = inject[key].default; - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault; - } else if (process.env.NODE_ENV !== 'production') { - warn(("Injection \"" + key + "\" not found"), vm); - } - } - } - return result - } -} - -/* */ - -/** - * Runtime helper for rendering v-for lists. - */ -function renderList ( - val, - render -) { - var ret, i, l, keys, key; - if (Array.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)) { - 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)._isVList = true; - } - return ret -} - -/* */ - -/** - * Runtime helper for rendering <slot> - */ -function renderSlot ( - name, - fallback, - props, - bindObject -) { - var scopedSlotFn = this.$scopedSlots[name]; - if (scopedSlotFn) { // scoped slot - props = props || {}; - if (bindObject) { - if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); - } - props = extend(extend({}, bindObject), props); - } - return scopedSlotFn(props) || fallback - } else { - var slotNodes = this.$slots[name]; - // warn duplicate slot usage - if (slotNodes && process.env.NODE_ENV !== 'production') { - slotNodes._rendered && warn( - "Duplicate presence of slot \"" + name + "\" found in the same render tree " + - "- this will likely cause render errors.", - this - ); - slotNodes._rendered = true; - } - return slotNodes || fallback - } -} - -/* */ - -/** - * Runtime helper for resolving filters - */ -function resolveFilter (id) { - return resolveAsset(this.$options, 'filters', id, true) || identity -} - -/* */ - -/** - * Runtime helper for checking keyCodes from config. - * exposed as Vue.prototype._k - * passing in eventKeyName as last argument separately for backwards compat - */ -function checkKeyCodes ( - eventKeyCode, - key, - builtInAlias, - eventKeyName -) { - var keyCodes = config.keyCodes[key] || builtInAlias; - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} - -/* */ - -/** - * Runtime helper for merging v-bind="object" into a VNode's data. - */ -function bindObjectProps ( - data, - tag, - value, - asProp, - isSync -) { - if (value) { - if (!isObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ); - } else { - if (Array.isArray(value)) { - value = toObject(value); - } - var hash; - var loop = function ( key ) { - if ( - key === 'class' || - key === 'style' || - isReservedAttribute(key) - ) { - hash = data; - } else { - var type = data.attrs && data.attrs.type; - hash = asProp || config.mustUseProp(tag, type, key) - ? data.domProps || (data.domProps = {}) - : data.attrs || (data.attrs = {}); - } - if (!(key in hash)) { - hash[key] = value[key]; - - if (isSync) { - var on = data.on || (data.on = {}); - on[("update:" + key)] = function ($event) { - value[key] = $event; - }; - } - } - }; - - for (var key in value) loop( key ); - } - } - return data -} - -/* */ - -/** - * Runtime helper for rendering static trees. - */ -function renderStatic ( - index, - isInFor -) { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - var renderFns = this.$options.staticRenderFns; - var cached = renderFns.cached || (renderFns.cached = []); - var tree = cached[index]; - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = renderFns[index].call(this._renderProxy, null, this); - markStatic(tree, ("__static__" + index), false); - return tree -} - -/** - * Runtime helper for v-once. - * Effectively it means marking the node as static with a unique key. - */ -function markOnce ( - tree, - index, - key -) { - markStatic(tree, ("__once__" + index + (key ? ("_" + key) : "")), true); - return tree -} - -function markStatic ( - tree, - key, - isOnce -) { - if (Array.isArray(tree)) { - for (var 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; -} - -/* */ - -function bindObjectListeners (data, value) { - if (value) { - if (!isPlainObject(value)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-on without argument expects an Object value', - this - ); - } else { - var on = data.on = data.on ? extend({}, data.on) : {}; - for (var key in value) { - var existing = on[key]; - var ours = value[key]; - on[key] = existing ? [].concat(existing, ours) : ours; - } - } - } - return data -} - -/* */ - -function installRenderHelpers (target) { - 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; -} - -/* */ - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - var options = Ctor.options; - 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 = function () { return resolveSlots(children, parent); }; - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - var contextVm = Object.create(parent); - var isCompiled = isTrue(options._compiled); - var needNormalization = !isCompiled; - - // 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 = data.scopedSlots || emptyObject; - } - - if (options._scopeId) { - this._c = function (a, b, c, d) { - var vnode = createElement(contextVm, a, b, c, d, needNormalization); - if (vnode) { - vnode.functionalScopeId = options._scopeId; - vnode.functionalContext = parent; - } - return vnode - }; - } else { - this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); }; - } -} - -installRenderHelpers(FunctionalRenderContext.prototype); - -function createFunctionalComponent ( - Ctor, - propsData, - data, - contextVm, - children -) { - var options = Ctor.options; - var props = {}; - var propOptions = options.props; - if (isDef(propOptions)) { - for (var 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); } - } - - var renderContext = new FunctionalRenderContext( - data, - props, - children, - contextVm, - Ctor - ); - - var vnode = options.render.call(null, renderContext._c, renderContext); - - if (vnode instanceof VNode) { - vnode.functionalContext = contextVm; - vnode.functionalOptions = options; - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot; - } - } - - return vnode -} - -function mergeProps (to, from) { - for (var key in from) { - to[camelize(key)] = from[key]; - } -} - -/* */ - -// hooks to be invoked on component VNodes during patch -var componentVNodeHooks = { - init: function init ( - vnode, - hydrating, - parentElm, - refElm - ) { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - var child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ); - child.$mount(hydrating ? vnode.elm : undefined, hydrating); - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - var mountedNode = vnode; // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode); - } - }, - - prepatch: function prepatch (oldVnode, vnode) { - var options = vnode.componentOptions; - var child = vnode.componentInstance = oldVnode.componentInstance; - updateChildComponent( - child, - options.propsData, // updated props - options.listeners, // updated listeners - vnode, // new parent vnode - options.children // new children - ); - }, - - insert: function insert (vnode) { - var context = vnode.context; - var componentInstance = vnode.componentInstance; - 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: function destroy (vnode) { - var componentInstance = vnode.componentInstance; - if (!componentInstance._isDestroyed) { - if (!vnode.data.keepAlive) { - componentInstance.$destroy(); - } else { - deactivateChildComponent(componentInstance, true /* direct */); - } - } - } -}; - -var hooksToMerge = Object.keys(componentVNodeHooks); - -function createComponent ( - Ctor, - data, - context, - children, - tag -) { - if (isUndef(Ctor)) { - return - } - - var baseCtor = context.$options._base; - - // plain options object: turn it into a constructor - if (isObject(Ctor)) { - Ctor = baseCtor.extend(Ctor); - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - if (process.env.NODE_ENV !== 'production') { - warn(("Invalid Component definition: " + (String(Ctor))), context); - } - return - } - - // async component - var asyncFactory; - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor; - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); - 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); - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data); - } - - // extract props - var propsData = extractPropsFromVNodeData(data, Ctor, tag); - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, propsData, data, context, children) - } - - // extract listeners, since these needs to be treated as - // child component listeners instead of DOM listeners - var listeners = data.on; - // replace with listeners with .native modifier - // so it gets processed during parent component patch. - data.on = data.nativeOn; - - if (isTrue(Ctor.options.abstract)) { - // abstract components do not keep anything - // other than props & listeners & slot - - // work around flow - var slot = data.slot; - data = {}; - if (slot) { - data.slot = slot; - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data); - - // return a placeholder vnode - var name = Ctor.options.name || tag; - var vnode = new VNode( - ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), - data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, - asyncFactory - ); - return vnode -} - -function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent, // activeInstance in lifecycle state - parentElm, - refElm -) { - var vnodeComponentOptions = vnode.componentOptions; - var options = { - _isComponent: true, - parent: parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - }; - // check inline-template render functions - var inlineTemplate = vnode.data.inlineTemplate; - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render; - options.staticRenderFns = inlineTemplate.staticRenderFns; - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data) { - if (!data.hook) { - data.hook = {}; - } - for (var i = 0; i < hooksToMerge.length; i++) { - var key = hooksToMerge[i]; - var fromParent = data.hook[key]; - var ours = componentVNodeHooks[key]; - data.hook[key] = fromParent ? mergeHook$1(ours, fromParent) : ours; - } -} - -function mergeHook$1 (one, two) { - return function (a, b, c, d) { - one(a, b, c, d); - two(a, b, c, d); - } -} - -// transform component v-model info (value and callback) into -// prop and event handler respectively. -function transformModel (options, data) { - var prop = (options.model && options.model.prop) || 'value'; - var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value; - var on = data.on || (data.on = {}); - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]); - } else { - on[event] = data.model.callback; - } -} - -/* */ - -var SIMPLE_NORMALIZE = 1; -var ALWAYS_NORMALIZE = 2; - -// wrapper function for providing a more flexible interface -// without getting yelled at by flow -function createElement ( - context, - tag, - data, - children, - normalizationType, - alwaysNormalize -) { - if (Array.isArray(data) || isPrimitive(data)) { - normalizationType = children; - children = data; - data = undefined; - } - if (isTrue(alwaysNormalize)) { - normalizationType = ALWAYS_NORMALIZE; - } - return _createElement(context, tag, data, children, normalizationType) -} - -function _createElement ( - context, - tag, - data, - children, - normalizationType -) { - if (isDef(data) && isDef((data).__ob__)) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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); - } - var vnode, ns; - if (typeof tag === 'string') { - var Ctor; - ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); - if (config.isReservedTag(tag)) { - // platform built-in elements - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ); - } else if (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, data, context, children); - } - if (isDef(vnode)) { - if (ns) { applyNS(vnode, ns); } - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - vnode.ns = ns; - if (vnode.tag === 'foreignObject') { - // use default namespace inside foreignObject - ns = undefined; - force = true; - } - if (isDef(vnode.children)) { - for (var i = 0, l = vnode.children.length; i < l; i++) { - var child = vnode.children[i]; - if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { - applyNS(child, ns, force); - } - } - } -} - -/* */ - -function initRender (vm) { - vm._vnode = null; // the root of the child tree - var options = vm.$options; - var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree - var renderContext = parentVnode && parentVnode.context; - vm.$slots = resolveSlots(options._renderChildren, renderContext); - vm.$scopedSlots = 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 - vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; - // normalization is always applied for the public version, used in - // user-written render functions. - vm.$createElement = function (a, b, c, d) { return 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 - var parentData = parentVnode && parentVnode.data; - - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () { - !isUpdatingChildComponent && warn("$attrs is readonly.", vm); - }, true); - defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () { - !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); - } -} - -function renderMixin (Vue) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype); - - Vue.prototype.$nextTick = function (fn) { - return nextTick(fn, this) - }; - - Vue.prototype._render = function () { - var vm = this; - var ref = vm.$options; - var render = ref.render; - var _parentVnode = ref._parentVnode; - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (var key in vm.$slots) { - var slot = vm.$slots[key]; - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */); - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode; - // render self - var vnode; - try { - vnode = render.call(vm._renderProxy, vm.$createElement); - } catch (e) { - handleError(e, vm, "render"); - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); - } catch (e) { - handleError(e, vm, "renderError"); - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } else { - vnode = vm._vnode; - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if (process.env.NODE_ENV !== 'production' && Array.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 - }; -} - -/* */ - -var uid = 0; - -function initMixin (Vue) { - Vue.prototype._init = function (options) { - var vm = this; - // a uid - vm._uid = uid++; - - var startTag, endTag; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - startTag = "vue-perf-start:" + (vm._uid); - endTag = "vue-perf-end:" + (vm._uid); - mark(startTag); - } - - // a flag to avoid this being observed - vm._isVue = 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); - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ); - } - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - initProxy(vm); - } else { - vm._renderProxy = vm; - } - // expose real self - vm._self = vm; - initLifecycle(vm); - initEvents(vm); - initRender(vm); - callHook(vm, 'beforeCreate'); - initInjections(vm); // resolve injections before data/props - initState(vm); - initProvide(vm); // resolve provide after data/props - callHook(vm, 'created'); - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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); - } - }; -} - -function initInternalComponent (vm, options) { - var opts = vm.$options = Object.create(vm.constructor.options); - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent; - opts.propsData = options.propsData; - opts._parentVnode = options._parentVnode; - opts._parentListeners = options._parentListeners; - opts._renderChildren = options._renderChildren; - opts._componentTag = options._componentTag; - opts._parentElm = options._parentElm; - opts._refElm = options._refElm; - if (options.render) { - opts.render = options.render; - opts.staticRenderFns = options.staticRenderFns; - } -} - -function resolveConstructorOptions (Ctor) { - var options = Ctor.options; - if (Ctor.super) { - var superOptions = resolveConstructorOptions(Ctor.super); - var 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) - var 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) { - var modified; - var latest = Ctor.options; - var extended = Ctor.extendOptions; - var sealed = Ctor.sealedOptions; - for (var key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) { modified = {}; } - modified[key] = dedupe(latest[key], extended[key], sealed[key]); - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - var res = []; - sealed = Array.isArray(sealed) ? sealed : [sealed]; - extended = Array.isArray(extended) ? extended : [extended]; - for (var i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]); - } - } - return res - } else { - return latest - } -} - -function Vue$2 (options) { - if (process.env.NODE_ENV !== 'production' && - !(this instanceof Vue$2) - ) { - warn('Vue is a constructor and should be called with the `new` keyword'); - } - this._init(options); -} - -initMixin(Vue$2); -stateMixin(Vue$2); -eventsMixin(Vue$2); -lifecycleMixin(Vue$2); -renderMixin(Vue$2); - -/* */ - -function initUse (Vue) { - Vue.use = function (plugin) { - var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - var args = toArray(arguments, 1); - args.unshift(this); - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args); - } else if (typeof plugin === 'function') { - plugin.apply(null, args); - } - installedPlugins.push(plugin); - return this - }; -} - -/* */ - -function initMixin$1 (Vue) { - Vue.mixin = function (mixin) { - this.options = mergeOptions(this.options, mixin); - return this - }; -} - -/* */ - -function initExtend (Vue) { - /** - * 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 - */ - Vue.extend = function (extendOptions) { - extendOptions = extendOptions || {}; - var Super = this; - var SuperId = Super.cid; - var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - 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 characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - } - - var Sub = function VueComponent (options) { - this._init(options); - }; - 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$1(Sub); - } - if (Sub.options.computed) { - initComputed$1(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$1 (Comp) { - var props = Comp.options.props; - for (var key in props) { - proxy(Comp.prototype, "_props", key); - } -} - -function initComputed$1 (Comp) { - var computed = Comp.options.computed; - for (var key in computed) { - defineComputed(Comp.prototype, key, computed[key]); - } -} - -/* */ - -function initAssetRegisters (Vue) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.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' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ); - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id; - definition = this.options._base.extend(definition); - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition }; - } - this.options[type + 's'][id] = definition; - return definition - } - }; - }); -} - -/* */ - -function getComponentName (opts) { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern, name) { - if (Array.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, filter) { - var cache = keepAliveInstance.cache; - var keys = keepAliveInstance.keys; - var _vnode = keepAliveInstance._vnode; - for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode); - } - } - } -} - -function pruneCacheEntry ( - cache, - key, - keys, - current -) { - var cached$$1 = cache[key]; - if (cached$$1 && cached$$1 !== current) { - cached$$1.componentInstance.$destroy(); - } - cache[key] = null; - remove(keys, key); -} - -var patternTypes = [String, RegExp, Array]; - -var KeepAlive = { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created: function created () { - this.cache = Object.create(null); - this.keys = []; - }, - - destroyed: function destroyed () { - var this$1 = this; - - for (var key in this$1.cache) { - pruneCacheEntry(this$1.cache, key, this$1.keys); - } - }, - - watch: { - include: function include (val) { - pruneCache(this, function (name) { return matches(val, name); }); - }, - exclude: function exclude (val) { - pruneCache(this, function (name) { return !matches(val, name); }); - } - }, - - render: function render () { - var vnode = getFirstComponentChild(this.$slots.default); - var componentOptions = vnode && vnode.componentOptions; - if (componentOptions) { - // check pattern - var name = getComponentName(componentOptions); - if (name && ( - (this.include && !matches(this.include, name)) || - (this.exclude && matches(this.exclude, name)) - )) { - return vnode - } - - var ref = this; - var cache = ref.cache; - var keys = ref.keys; - var 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 { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } - } - - vnode.data.keepAlive = true; - } - return vnode - } -}; - -var builtInComponents = { - KeepAlive: KeepAlive -}; - -/* */ - -function initGlobalAPI (Vue) { - // config - var configDef = {}; - configDef.get = function () { return config; }; - if (process.env.NODE_ENV !== 'production') { - configDef.set = function () { - 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: warn, - extend: extend, - mergeOptions: mergeOptions, - defineReactive: defineReactive - }; - - Vue.set = set; - Vue.delete = del; - Vue.nextTick = nextTick; - - Vue.options = Object.create(null); - ASSET_TYPES.forEach(function (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$1(Vue); - initExtend(Vue); - initAssetRegisters(Vue); -} - -initGlobalAPI(Vue$2); - -Object.defineProperty(Vue$2.prototype, '$isServer', { - get: isServerRendering -}); - -Object.defineProperty(Vue$2.prototype, '$ssrContext', { - get: function get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}); - -Vue$2.version = '2.5.0'; - -var latestNodeId = 1; - -function TextNode (text) { - this.instanceId = ''; - this.nodeId = latestNodeId++; - this.parentNode = null; - this.nodeType = 3; - this.text = text; -} - -/* globals document */ -// document is injected by weex factory wrapper - -var namespaceMap = {}; - -function createElement$1 (tagName) { - return document.createElement(tagName) -} - -function createElementNS (namespace, tagName) { - return document.createElement(namespace + ':' + tagName) -} - -function createTextNode (text) { - return new TextNode(text) -} - -function createComment (text) { - return document.createComment(text) -} - -function insertBefore (node, target, before) { - if (target.nodeType === 3) { - if (node.type === 'text') { - node.setAttr('value', target.text); - target.parentNode = node; - } else { - var text = createElement$1('text'); - text.setAttr('value', target.text); - node.insertBefore(text, before); - } - return - } - node.insertBefore(target, before); -} - -function removeChild (node, child) { - if (child.nodeType === 3) { - node.setAttr('value', ''); - return - } - node.removeChild(child); -} - -function appendChild (node, child) { - if (child.nodeType === 3) { - if (node.type === 'text') { - node.setAttr('value', child.text); - child.parentNode = node; - } else { - var text = createElement$1('text'); - text.setAttr('value', child.text); - node.appendChild(text); - } - return - } - - node.appendChild(child); -} - -function parentNode (node) { - return node.parentNode -} - -function nextSibling (node) { - return node.nextSibling -} - -function tagName (node) { - return node.type -} - -function setTextContent (node, text) { - node.parentNode.setAttr('value', text); -} - -function setAttribute (node, key, val) { - node.setAttr(key, val); -} - - -var nodeOps = Object.freeze({ - namespaceMap: namespaceMap, - createElement: createElement$1, - createElementNS: createElementNS, - createTextNode: createTextNode, - createComment: createComment, - insertBefore: insertBefore, - removeChild: removeChild, - appendChild: appendChild, - parentNode: parentNode, - nextSibling: nextSibling, - tagName: tagName, - setTextContent: setTextContent, - setAttribute: setAttribute -}); - -/* */ - -var ref = { - create: function create (_, vnode) { - registerRef(vnode); - }, - update: function update (oldVnode, vnode) { - if (oldVnode.data.ref !== vnode.data.ref) { - registerRef(oldVnode, true); - registerRef(vnode); - } - }, - destroy: function destroy (vnode) { - registerRef(vnode, true); - } -}; - -function registerRef (vnode, isRemoval) { - var key = vnode.data.ref; - if (!key) { return } - - var vm = vnode.context; - var ref = vnode.componentInstance || vnode.elm; - var refs = vm.$refs; - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref); - } else if (refs[key] === ref) { - refs[key] = undefined; - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref]; - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref); - } - } else { - refs[key] = ref; - } - } -} - -/* */ - - - -var 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. -var 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 -); - - - - - - - - - -var isTextInputType = makeMap('text,number,password,search,email,tel,url'); - -/** - * 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. - */ - -var emptyNode = new VNode('', {}, []); - -var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; - -function sameVnode (a, b) { - return ( - a.key === b.key && ( - ( - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - isUndef(b.asyncFactory.error) - ) - ) - ) -} - -function sameInputType (a, b) { - if (a.tag !== 'input') { return true } - var i; - var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type; - var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type; - return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) -} - -function createKeyToOldIdx (children, beginIdx, endIdx) { - var i, key; - var map = {}; - for (i = beginIdx; i <= endIdx; ++i) { - key = children[i].key; - if (isDef(key)) { map[key] = i; } - } - return map -} - -function createPatchFunction (backend) { - var i, j; - var cbs = {}; - - var modules = backend.modules; - var nodeOps = backend.nodeOps; - - 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) { - var parent = nodeOps.parentNode(el); - // element may have already been removed due to v-html / v-text - if (isDef(parent)) { - nodeOps.removeChild(parent, el); - } - } - - var inPre = 0; - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - vnode.isRootInsert = !nested; // for transition enter check - if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { - return - } - - var data = vnode.data; - var children = vnode.children; - var tag = vnode.tag; - if (isDef(tag)) { - if (process.env.NODE_ENV !== 'production') { - if (data && data.pre) { - inPre++; - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(function (ignore) { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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); - - /* istanbul ignore if */ - { - // in Weex, the default insertion order is parent-first. - // List items can be optimized to use children-first insertion - // with append="tree". - var appendAsTree = isDef(data) && isTrue(data.appendAsTree); - if (!appendAsTree) { - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - createChildren(vnode, children, insertedVnodeQueue); - if (appendAsTree) { - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - } - insert(parentElm, vnode.elm, refElm); - } - } - - if (process.env.NODE_ENV !== 'production' && data && data.pre) { - inPre--; - } - } 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) { - var i = vnode.data; - if (isDef(i)) { - var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; - if (isDef(i = i.hook) && isDef(i = i.init)) { - i(vnode, false /* hydrating */, parentElm, refElm); - } - // 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); - 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) { - var 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. - var 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$$1) { - if (isDef(parent)) { - if (isDef(ref$$1)) { - if (ref$$1.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref$$1); - } - } else { - nodeOps.appendChild(parent, elm); - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (var i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); - } - } - - function isPatchable (vnode) { - while (vnode.componentInstance) { - vnode = vnode.componentInstance._vnode; - } - return isDef(vnode.tag) - } - - function invokeCreateHooks (vnode, insertedVnodeQueue) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](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) { - var i; - if (isDef(i = vnode.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, ''); - } else { - var ancestor = vnode; - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, ''); - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm); - } - } - - function invokeDestroyHook (vnode) { - var i, j; - var 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 (parentElm, vnodes, startIdx, endIdx) { - for (; startIdx <= endIdx; ++startIdx) { - var ch = vnodes[startIdx]; - if (isDef(ch)) { - if (isDef(ch.tag)) { - removeAndInvokeRemoveHook(ch); - invokeDestroyHook(ch); - } else { // Text node - removeNode(ch.elm); - } - } - } - } - - function removeAndInvokeRemoveHook (vnode, rm) { - if (isDef(rm) || isDef(vnode.data)) { - var i; - var 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) { - var oldStartIdx = 0; - var newStartIdx = 0; - var oldEndIdx = oldCh.length - 1; - var oldStartVnode = oldCh[0]; - var oldEndVnode = oldCh[oldEndIdx]; - var newEndIdx = newCh.length - 1; - var newStartVnode = newCh[0]; - var newEndVnode = newCh[newEndIdx]; - var 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 - var canMove = !removeOnly; - - 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); - oldStartVnode = oldCh[++oldStartIdx]; - newStartVnode = newCh[++newStartIdx]; - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); - oldEndVnode = oldCh[--oldEndIdx]; - newEndVnode = newCh[--newEndIdx]; - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); - 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); - 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); - } else { - vnodeToMove = oldCh[idxInOld]; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ); - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue); - 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); - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx); - } - } - - function findIdxInOld (node, oldCh, start, end) { - for (var i = start; i < end; i++) { - var c = oldCh[i]; - if (isDef(c) && sameVnode(node, c)) { return i } - } - } - - function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { - if (oldVnode === vnode) { - return - } - - var 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 - } - - var i; - var data = vnode.data; - if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { - i(oldVnode, vnode); - } - - var oldCh = oldVnode.children; - var 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 (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 (var i = 0; i < queue.length; ++i) { - queue[i].data.hook.insert(queue[i]); - } - } - } - - var bailed = 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 - var isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key'); - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm; - vnode.isAsyncPlaceholder = true; - return true - } - if (process.env.NODE_ENV !== 'production') { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm; - var tag = vnode.tag; - var data = vnode.data; - var children = vnode.children; - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('server innerHTML: ', i); - console.warn('client innerHTML: ', elm.innerHTML); - } - return false - } - } else { - // iterate and compare children lists - var childrenMatch = true; - var childNode = elm.firstChild; - for (var i$1 = 0; i$1 < children.length; i$1++) { - if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue)) { - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true; - console.warn('Parent: ', elm); - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); - } - return false - } - } - } - } - if (isDef(data)) { - for (var key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue); - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text; - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); } - return - } - - var isInitialPatch = false; - var insertedVnodeQueue = []; - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true; - createElm(vnode, insertedVnodeQueue, parentElm, refElm); - } else { - var isRealElement = isDef(oldVnode.nodeType); - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 (process.env.NODE_ENV !== 'production') { - 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 - var oldElm = oldVnode.elm; - var parentElm$1 = nodeOps.parentNode(oldElm); - 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$1, - nodeOps.nextSibling(oldElm) - ); - - if (isDef(vnode.parent)) { - // component root element replaced. - // update parent placeholder node element, recursively - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - if (isDef(parentElm$1)) { - removeVnodes(parentElm$1, [oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } -} - -/* */ - -var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } -}; - -function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } -} - -function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } -} - -var emptyModifiers = Object.create(null); - -function normalizeDirectives$1 ( - dirs, - vm -) { - var res = Object.create(null); - if (!dirs) { - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - return res -} - -function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) -} - -function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } -} - -var baseModules = [ - ref, - directives -]; - -/* */ - -function updateAttrs (oldVnode, vnode) { - if (!oldVnode.data.attrs && !vnode.data.attrs) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (attrs.__ob__) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - elm.setAttr(key, cur); - } - } - for (key in oldAttrs) { - if (attrs[key] == null) { - elm.setAttr(key); - } - } -} - -var attrs = { - create: updateAttrs, - update: updateAttrs -}; - -/* */ - -function updateClass (oldVnode, vnode) { - var el = vnode.elm; - var ctx = vnode.context; - - var data = vnode.data; - var oldData = oldVnode.data; - if (!data.staticClass && - !data.class && - (!oldData || (!oldData.staticClass && !oldData.class)) - ) { - return - } - - var oldClassList = []; - // unlike web, weex vnode staticClass is an Array - var oldStaticClass = oldData.staticClass; - if (oldStaticClass) { - oldClassList.push.apply(oldClassList, oldStaticClass); - } - if (oldData.class) { - oldClassList.push.apply(oldClassList, oldData.class); - } - - var classList = []; - // unlike web, weex vnode staticClass is an Array - var staticClass = data.staticClass; - if (staticClass) { - classList.push.apply(classList, staticClass); - } - if (data.class) { - classList.push.apply(classList, data.class); - } - - var style = getStyle(oldClassList, classList, ctx); - for (var key in style) { - el.setStyle(key, style[key]); - } -} - -function getStyle (oldClassList, classList, ctx) { - // style is a weex-only injected object - // compiled from <style> tags in weex files - var stylesheet = ctx.$options.style || {}; - var result = {}; - classList.forEach(function (name) { - var style = stylesheet[name]; - extend(result, style); - }); - oldClassList.forEach(function (name) { - var style = stylesheet[name]; - for (var key in style) { - if (!result.hasOwnProperty(key)) { - result[key] = ''; - } - } - }); - return result -} - -var klass = { - create: updateClass, - update: updateClass -}; - -/* */ - -var target$1; - -function add$1 ( - event, - handler, - once, - capture -) { - if (capture) { - console.log('Weex do not support event in bubble phase.'); - return - } - if (once) { - var oldHandler = handler; - var _target = target$1; // save current target element in closure - handler = function (ev) { - var res = arguments.length === 1 - ? oldHandler(ev) - : oldHandler.apply(null, arguments); - if (res !== null) { - remove$2(event, null, null, _target); - } - }; - } - target$1.addEvent(event, handler); -} - -function remove$2 ( - event, - handler, - capture, - _target -) { - (_target || target$1).removeEvent(event); -} - -function updateDOMListeners (oldVnode, vnode) { - if (!oldVnode.data.on && !vnode.data.on) { - return - } - var on = vnode.data.on || {}; - var oldOn = oldVnode.data.on || {}; - target$1 = vnode.elm; - updateListeners(on, oldOn, add$1, remove$2, vnode.context); -} - -var events = { - create: updateDOMListeners, - update: updateDOMListeners -}; - -/* */ - -var normalize = cached(camelize); - -function createStyle (oldVnode, vnode) { - if (!vnode.data.staticStyle) { - updateStyle(oldVnode, vnode); - return - } - var elm = vnode.elm; - var staticStyle = vnode.data.staticStyle; - for (var name in staticStyle) { - if (staticStyle[name]) { - elm.setStyle(normalize(name), staticStyle[name]); - } - } - updateStyle(oldVnode, vnode); -} - -function updateStyle (oldVnode, vnode) { - if (!oldVnode.data.style && !vnode.data.style) { - return - } - var cur, name; - var elm = vnode.elm; - var oldStyle = oldVnode.data.style || {}; - var style = vnode.data.style || {}; - - var needClone = style.__ob__; - - // handle array syntax - if (Array.isArray(style)) { - style = vnode.data.style = toObject$1(style); - } - - // clone the style for future updates, - // in case the user mutates the style object in-place. - if (needClone) { - style = vnode.data.style = extend({}, style); - } - - for (name in oldStyle) { - if (!style[name]) { - elm.setStyle(normalize(name), ''); - } - } - for (name in style) { - cur = style[name]; - elm.setStyle(normalize(name), cur); - } -} - -function toObject$1 (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -var style = { - create: createStyle, - update: updateStyle -}; - -/* */ - -/** - * Add class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ - - -/** - * Remove class with compatibility for SVG since classList is not supported on - * SVG elements in IE - */ - -/* */ - -function resolveTransition (def) { - if (!def) { - return - } - /* istanbul ignore else */ - if (typeof def === 'object') { - var res = {}; - if (def.css !== false) { - extend(res, autoCssTransition(def.name || 'v')); - } - extend(res, def); - return res - } else if (typeof def === 'string') { - return autoCssTransition(def) - } -} - -var autoCssTransition = cached(function (name) { - return { - enterClass: (name + "-enter"), - enterToClass: (name + "-enter-to"), - enterActiveClass: (name + "-enter-active"), - leaveClass: (name + "-leave"), - leaveToClass: (name + "-leave-to"), - leaveActiveClass: (name + "-leave-active") - } -}); - - -// Transition property/event sniffing - - - - -// binding to window is necessary to make hot reload work in IE in strict mode -var raf = inBrowser - ? window.requestAnimationFrame - ? window.requestAnimationFrame.bind(window) - : setTimeout - : /* istanbul ignore next */ function (fn) { return fn(); }; - -var transition = { - create: enter, - activate: enter, - remove: leave -}; - -function enter (_, vnode) { - var el = vnode.elm; - - // call leave callback now - if (el._leaveCb) { - el._leaveCb.cancelled = true; - el._leaveCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (!data) { - return - } - - /* istanbul ignore if */ - if (el._enterCb) { - return - } - - var enterClass = data.enterClass; - var enterToClass = data.enterToClass; - var enterActiveClass = data.enterActiveClass; - var appearClass = data.appearClass; - var appearToClass = data.appearToClass; - var appearActiveClass = data.appearActiveClass; - var beforeEnter = data.beforeEnter; - var enter = data.enter; - var afterEnter = data.afterEnter; - var enterCancelled = data.enterCancelled; - var beforeAppear = data.beforeAppear; - var appear = data.appear; - var afterAppear = data.afterAppear; - var appearCancelled = data.appearCancelled; - - var context = activeInstance; - var transitionNode = activeInstance.$vnode; - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent; - context = transitionNode.context; - } - - var isAppear = !context._isMounted || !vnode.isRootInsert; - - if (isAppear && !appear && appear !== '') { - return - } - - var startClass = isAppear ? appearClass : enterClass; - var toClass = isAppear ? appearToClass : enterToClass; - var activeClass = isAppear ? appearActiveClass : enterActiveClass; - var beforeEnterHook = isAppear ? (beforeAppear || beforeEnter) : beforeEnter; - var enterHook = isAppear ? (typeof appear === 'function' ? appear : enter) : enter; - var afterEnterHook = isAppear ? (afterAppear || afterEnter) : afterEnter; - var enterCancelledHook = isAppear ? (appearCancelled || enterCancelled) : enterCancelled; - - var userWantsControl = - enterHook && - // enterHook may be a bound method which exposes - // the length of original fn as _length - (enterHook._length || enterHook.length) > 1; - - var stylesheet = vnode.context.$options.style || {}; - var startState = stylesheet[startClass]; - var transitionProperties = (stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][activeClass]) || {}; - var endState = getEnterTargetState(el, stylesheet, startClass, toClass, activeClass, vnode.context); - var needAnimation = Object.keys(endState).length > 0; - - var cb = el._enterCb = once(function () { - if (cb.cancelled) { - enterCancelledHook && enterCancelledHook(el); - } else { - afterEnterHook && afterEnterHook(el); - } - el._enterCb = null; - }); - - // We need to wait until the native element has been inserted, but currently - // there's no API to do that. So we have to wait "one frame" - not entirely - // sure if this is guaranteed to be enough (e.g. on slow devices?) - setTimeout(function () { - var parent = el.parentNode; - var pendingNode = parent && parent._pending && parent._pending[vnode.key]; - if (pendingNode && - pendingNode.context === vnode.context && - pendingNode.tag === vnode.tag && - pendingNode.elm._leaveCb - ) { - pendingNode.elm._leaveCb(); - } - enterHook && enterHook(el, cb); - - if (needAnimation) { - var animation = vnode.context.$requireWeexModule('animation'); - animation.transition(el.ref, { - styles: endState, - duration: transitionProperties.duration || 0, - delay: transitionProperties.delay || 0, - timingFunction: transitionProperties.timingFunction || 'linear' - }, userWantsControl ? noop : cb); - } else if (!userWantsControl) { - cb(); - } - }, 16); - - // start enter transition - beforeEnterHook && beforeEnterHook(el); - - if (startState) { - for (var key in startState) { - el.setStyle(key, startState[key]); - } - } - - if (!needAnimation && !userWantsControl) { - cb(); - } -} - -function leave (vnode, rm) { - var el = vnode.elm; - - // call enter callback now - if (el._enterCb) { - el._enterCb.cancelled = true; - el._enterCb(); - } - - var data = resolveTransition(vnode.data.transition); - if (!data) { - return rm() - } - - if (el._leaveCb) { - return - } - - var leaveClass = data.leaveClass; - var leaveToClass = data.leaveToClass; - var leaveActiveClass = data.leaveActiveClass; - var beforeLeave = data.beforeLeave; - var leave = data.leave; - var afterLeave = data.afterLeave; - var leaveCancelled = data.leaveCancelled; - var delayLeave = data.delayLeave; - - var userWantsControl = - leave && - // leave hook may be a bound method which exposes - // the length of original fn as _length - (leave._length || leave.length) > 1; - - var stylesheet = vnode.context.$options.style || {}; - var startState = stylesheet[leaveClass]; - var endState = stylesheet[leaveToClass] || stylesheet[leaveActiveClass]; - var transitionProperties = (stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][leaveActiveClass]) || {}; - - var cb = el._leaveCb = once(function () { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null; - } - if (cb.cancelled) { - leaveCancelled && leaveCancelled(el); - } else { - rm(); - afterLeave && afterLeave(el); - } - el._leaveCb = null; - }); - - if (delayLeave) { - delayLeave(performLeave); - } else { - performLeave(); - } - - function performLeave () { - var animation = vnode.context.$requireWeexModule('animation'); - // the delayed leave may have already been cancelled - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key] = vnode; - } - beforeLeave && beforeLeave(el); - - if (startState) { - animation.transition(el.ref, { - styles: startState - }, next); - } else { - next(); - } - - function next () { - animation.transition(el.ref, { - styles: endState, - duration: transitionProperties.duration || 0, - delay: transitionProperties.delay || 0, - timingFunction: transitionProperties.timingFunction || 'linear' - }, userWantsControl ? noop : cb); - } - - leave && leave(el, cb); - if (!endState && !userWantsControl) { - cb(); - } - } -} - -// determine the target animation style for an entering transition. -function getEnterTargetState (el, stylesheet, startClass, endClass, activeClass, vm) { - var targetState = {}; - var startState = stylesheet[startClass]; - var endState = stylesheet[endClass]; - var activeState = stylesheet[activeClass]; - // 1. fallback to element's default styling - if (startState) { - for (var key in startState) { - targetState[key] = el.style[key]; - if ( - process.env.NODE_ENV !== 'production' && - targetState[key] == null && - (!activeState || activeState[key] == null) && - (!endState || endState[key] == null) - ) { - warn( - "transition property \"" + key + "\" is declared in enter starting class (." + startClass + "), " + - "but not declared anywhere in enter ending class (." + endClass + "), " + - "enter active cass (." + activeClass + ") or the element's default styling. " + - "Note in Weex, CSS properties need explicit values to be transitionable." - ); - } - } - } - // 2. if state is mixed in active state, extract them while excluding - // transition properties - if (activeState) { - for (var key$1 in activeState) { - if (key$1.indexOf('transition') !== 0) { - targetState[key$1] = activeState[key$1]; - } - } - } - // 3. explicit endState has highest priority - if (endState) { - extend(targetState, endState); - } - return targetState -} - -var platformModules = [ - attrs, - klass, - events, - style, - transition -]; - -/* */ - -// the directive module should be applied last, after all -// built-in modules have been applied. -var modules = platformModules.concat(baseModules); - -var patch = createPatchFunction({ - nodeOps: nodeOps, - modules: modules, - LONG_LIST_THRESHOLD: 10 -}); - -var platformDirectives = { -}; - -function getVNodeType (vnode) { - if (!vnode.tag) { - return '' - } - return vnode.tag.replace(/vue\-component\-(\d+\-)?/, '') -} - -function isSimpleSpan (vnode) { - return vnode.children && vnode.children.length === 1 && !vnode.children[0].tag -} - -var cssLengthRE = /^([+-]?[0-9]+(\.[0-9]+)?)(px|em|ex|%|in|cm|mm|pt|pc)$/i; -function trimCSSUnit (prop) { - var res = String(prop).match(cssLengthRE); - if (res) { - return Number(res[1]) - } - return prop -} - -function parseStyle (vnode) { - if (!vnode || !vnode.data) { - return - } - - var ref = vnode.data; - var staticStyle = ref.staticStyle; - var staticClass = ref.staticClass; - if (vnode.data.style || vnode.data.class || staticStyle || staticClass) { - var styles = Object.assign({}, staticStyle, vnode.data.style); - - var cssMap = vnode.context.$options.style || {}; - var classList = [].concat(staticClass, vnode.data.class); - classList.forEach(function (name) { - if (name && cssMap[name]) { - Object.assign(styles, cssMap[name]); - } - }); - - for (var key in styles) { - styles[key] = trimCSSUnit(styles[key]); - } - return styles - } -} - -function convertVNodeChildren (children) { - if (!children.length) { - return - } - - return children.map(function (vnode) { - var type = getVNodeType(vnode); - var props = { type: type }; - - // convert raw text node - if (!type) { - props.type = 'span'; - props.attr = { - value: (vnode.text || '').trim() - }; - } else { - props.style = parseStyle(vnode); - if (vnode.data) { - props.attr = vnode.data.attrs; - if (vnode.data.on) { - props.events = vnode.data.on; - } - } - - if (type === 'span' && isSimpleSpan(vnode)) { - props.attr = props.attr || {}; - props.attr.value = vnode.children[0].text.trim(); - return props - } - } - - if (vnode.children && vnode.children.length) { - props.children = convertVNodeChildren(vnode.children); - } - - return props - }) -} - -var Richtext = { - name: 'richtext', - // abstract: true, - render: function render (h) { - return h('weex:richtext', { - on: this._events, - attrs: { - value: convertVNodeChildren(this.$options._renderChildren || []) - } - }) - } -}; - -/* */ - -// Provides transition support for a single element/component. -// supports transition mode (out-in / in-out) - -var 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) { - var compOptions = vnode && vnode.componentOptions; - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -function extractTransitionData (comp) { - var data = {}; - var options = comp.$options; - // props - for (var key in options.propsData) { - data[key] = comp[key]; - } - // events. - // extract listeners and pass them directly to the transition methods - var listeners = options._parentListeners; - for (var key$1 in listeners) { - data[camelize(key$1)] = listeners[key$1]; - } - return data -} - -function placeholder (h, rawChild) { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode) { - while ((vnode = vnode.parent)) { - if (vnode.data.transition) { - return true - } - } -} - -function isSameChild (child, oldChild) { - return oldChild.key === child.key && oldChild.tag === child.tag -} - -var Transition$1 = { - name: 'transition', - props: transitionProps, - abstract: true, - - render: function render (h) { - var this$1 = this; - - var children = this.$options._renderChildren; - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if (process.env.NODE_ENV !== 'production' && children.length > 1) { - warn( - '<transition> can only be used on a single element. Use ' + - '<transition-group> for lists.', - this.$parent - ); - } - - var mode = this.mode; - - // warn invalid mode - if (process.env.NODE_ENV !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in' - ) { - warn( - 'invalid <transition> mode: ' + mode, - this.$parent - ); - } - - var rawChild = 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 - var 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. - var id = "__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; - - var data = (child.data || (child.data = {})).transition = extractTransitionData(this); - var oldRawChild = this._vnode; - var 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(function (d) { return d.name === 'show'; })) { - child.data.show = true; - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // replace old child transition data with fresh one - // important for dynamic transitions! - var oldData = 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', function () { - this$1._leaving = false; - this$1.$forceUpdate(); - }); - return placeholder(h, rawChild) - } else if (mode === 'in-out') { - if (isAsyncPlaceholder(child)) { - return oldRawChild - } - var delayedLeave; - var performLeave = function () { delayedLeave(); }; - mergeVNodeHook(data, 'afterEnter', performLeave); - mergeVNodeHook(data, 'enterCancelled', performLeave); - mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; }); - } - } - - return rawChild - } -}; - -// reuse same transition component logic from web - -var props = extend({ - tag: String, - moveClass: String -}, transitionProps); - -delete props.mode; - -var TransitionGroup = { - props: props, - - created: function created () { - var dom = this.$requireWeexModule('dom'); - this.getPosition = function (el) { return new Promise(function (resolve, reject) { - dom.getComponentRect(el.ref, function (res) { - if (!res.result) { - reject(new Error(("failed to get rect for element: " + (el.tag)))); - } else { - resolve(res.size); - } - }); - }); }; - - var animation = this.$requireWeexModule('animation'); - this.animate = function (el, options) { return new Promise(function (resolve) { - animation.transition(el.ref, options, resolve); - }); }; - }, - - render: function render (h) { - var tag = this.tag || this.$vnode.data.tag || 'span'; - var map = Object.create(null); - var prevChildren = this.prevChildren = this.children; - var rawChildren = this.$slots.default || []; - var children = this.children = []; - var transitionData = extractTransitionData(this); - - for (var i = 0; i < rawChildren.length; i++) { - var c = 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 (process.env.NODE_ENV !== 'production') { - var opts = c.componentOptions; - var name = opts - ? (opts.Ctor.options.name || opts.tag) - : c.tag; - warn(("<transition-group> children must be keyed: <" + name + ">")); - } - } - } - - if (prevChildren) { - var kept = []; - var removed = []; - prevChildren.forEach(function (c) { - c.data.transition = transitionData; - - // TODO: record before patch positions - - 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) - }, - - beforeUpdate: function beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ); - this._vnode = this.kept; - }, - - updated: function updated () { - var children = this.prevChildren; - var moveClass = this.moveClass || ((this.name || 'v') + '-move'); - var moveData = children.length && this.getMoveData(children[0].context, moveClass); - if (!moveData) { - return - } - - // TODO: finish implementing move animations once - // we have access to sync getComponentRect() - - // children.forEach(callPendingCbs) - - // Promise.all(children.map(c => { - // 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 - // return this.animate(c.elm, { - // styles: { - // transform: `translate(${dx}px,${dy}px)` - // } - // }) - // } - // })).then(() => { - // children.forEach(c => { - // if (c.data.moved) { - // this.animate(c.elm, { - // styles: { - // transform: '' - // }, - // duration: moveData.duration || 0, - // delay: moveData.delay || 0, - // timingFunction: moveData.timingFunction || 'linear' - // }) - // } - // }) - // }) - }, - - methods: { - getMoveData: function getMoveData (context, moveClass) { - var stylesheet = context.$options.style || {}; - return stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][moveClass] - } - } -}; - -// function callPendingCbs (c) { -// /* istanbul ignore if */ -// if (c.elm._moveCb) { -// c.elm._moveCb() -// } -// /* istanbul ignore if */ -// if (c.elm._enterCb) { -// c.elm._enterCb() -// } -// } - -var platformComponents = { - Richtext: Richtext, - Transition: Transition$1, - TransitionGroup: TransitionGroup -}; - -/* globals document */ - -var isReservedTag$1 = makeMap( - 'template,script,style,element,content,slot,link,meta,svg,view,' + - 'a,div,img,image,text,span,input,switch,textarea,spinner,select,' + - 'slider,slider-neighbor,indicator,canvas,' + - 'list,cell,header,loading,loading-indicator,refresh,scrollable,scroller,' + - 'video,web,embed,tabbar,tabheader,datepicker,timepicker,marquee,countdown', - true -); - -// Elements that you can, intentionally, leave open (and which close themselves) -// more flexible than web -var canBeLeftOpenTag = makeMap( - 'web,spinner,switch,video,textarea,canvas,' + - 'indicator,marquee,countdown', - true -); - -var isRuntimeComponent = makeMap( - 'richtext,trisition,trisition-group', - true -); - -var isUnaryTag = makeMap( - 'embed,img,image,input,link,meta', - true -); - -function mustUseProp () { /* console.log('mustUseProp') */ } - -function isUnknownElement$1 () { /* console.log('isUnknownElement') */ } - -function query (el, document) { - // document is injected by weex factory wrapper - var placeholder = document.createComment('root'); - placeholder.hasAttribute = placeholder.removeAttribute = function () {}; // hack for patch - document.documentElement.appendChild(placeholder); - return placeholder -} - -/* */ - -// install platform specific utils -Vue$2.config.mustUseProp = mustUseProp; -Vue$2.config.isReservedTag = isReservedTag$1; -Vue$2.config.isRuntimeComponent = isRuntimeComponent; -Vue$2.config.isUnknownElement = isUnknownElement$1; - -// install platform runtime directives and components -Vue$2.options.directives = platformDirectives; -Vue$2.options.components = platformComponents; - -// install platform patch function -Vue$2.prototype.__patch__ = patch; - -// wrap mount -Vue$2.prototype.$mount = function ( - el, - hydrating -) { - return mountComponent( - this, - el && query(el, this.$document), - hydrating - ) -}; - -// this entry is built and wrapped with a factory function -// used to generate a fresh copy of Vue for every Weex instance. - -exports.Vue = Vue$2; - -} diff --git a/packages/weex-vue-framework/index.js b/packages/weex-vue-framework/index.js deleted file mode 100644 index acd095ebcb1..00000000000 --- a/packages/weex-vue-framework/index.js +++ /dev/null @@ -1,312 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -// this will be preserved during build -var VueFactory = require('./factory'); - -var instances = {}; - -/** - * Prepare framework config. - * Nothing need to do actually, just an interface provided to weex runtime. - */ -function init () {} - -/** - * Reset framework config and clear all registrations. - */ -function reset () { - clear(instances); -} - -/** - * Delete all keys of an object. - * @param {object} obj - */ -function clear (obj) { - for (var key in obj) { - delete obj[key]; - } -} - -/** - * Create an instance with id, code, config and external data. - * @param {string} instanceId - * @param {string} appCode - * @param {object} config - * @param {object} data - * @param {object} env { info, config, services } - */ -function createInstance ( - instanceId, - appCode, - config, - data, - env -) { - if ( appCode === void 0 ) appCode = ''; - if ( config === void 0 ) config = {}; - if ( env === void 0 ) env = {}; - - var weex = env.weex; - var document = weex.document; - var instance = instances[instanceId] = { - instanceId: instanceId, config: config, data: data, - document: document - }; - - var timerAPIs = getInstanceTimer(instanceId, weex.requireModule); - - // Each instance has a independent `Vue` module instance - var Vue = instance.Vue = createVueModuleInstance(instanceId, weex); - - // The function which create a closure the JS Bundle will run in. - // It will declare some instance variables like `Vue`, HTML5 Timer APIs etc. - var instanceVars = Object.assign({ - Vue: Vue, - weex: weex - }, timerAPIs, env.services); - - appCode = "(function(global){ \n" + appCode + "\n })(Object.create(this))"; - - callFunction(instanceVars, appCode); - - // Send `createFinish` signal to native. - document.taskCenter.send('dom', { action: 'createFinish' }, []); - - return instance -} - -/** - * Destroy an instance with id. It will make sure all memory of - * this instance released and no more leaks. - * @param {string} instanceId - */ -function destroyInstance (instanceId) { - var instance = instances[instanceId]; - if (instance && instance.app instanceof instance.Vue) { - instance.document.destroy(); - instance.app.$destroy(); - delete instance.document; - delete instance.app; - } - delete instances[instanceId]; -} - -/** - * Refresh an instance with id and new top-level component data. - * It will use `Vue.set` on all keys of the new data. So it's better - * define all possible meaningful keys when instance created. - * @param {string} instanceId - * @param {object} data - */ -function refreshInstance (instanceId, data) { - var instance = instances[instanceId]; - if (!instance || !(instance.app instanceof instance.Vue)) { - return new Error(("refreshInstance: instance " + instanceId + " not found!")) - } - for (var key in data) { - instance.Vue.set(instance.app, key, data[key]); - } - // Finally `refreshFinish` signal needed. - instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, []); -} - -/** - * Get the JSON object of the root element. - * @param {string} instanceId - */ -function getRoot (instanceId) { - var instance = instances[instanceId]; - if (!instance || !(instance.app instanceof instance.Vue)) { - return new Error(("getRoot: instance " + instanceId + " not found!")) - } - return instance.app.$el.toJSON() -} - -var jsHandlers = { - fireEvent: function (id) { - var args = [], len = arguments.length - 1; - while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - - return fireEvent.apply(void 0, [ instances[id] ].concat( args )) - }, - callback: function (id) { - var args = [], len = arguments.length - 1; - while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - - return callback.apply(void 0, [ instances[id] ].concat( args )) - } -}; - -function fireEvent (instance, nodeId, type, e, domChanges) { - var el = instance.document.getRef(nodeId); - if (el) { - return instance.document.fireEvent(el, type, e, domChanges) - } - return new Error(("invalid element reference \"" + nodeId + "\"")) -} - -function callback (instance, callbackId, data, ifKeepAlive) { - var result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive); - instance.document.taskCenter.send('dom', { action: 'updateFinish' }, []); - return result -} - -/** - * Accept calls from native (event or callback). - * - * @param {string} id - * @param {array} tasks list with `method` and `args` - */ -function receiveTasks (id, tasks) { - var instance = instances[id]; - if (instance && Array.isArray(tasks)) { - var results = []; - tasks.forEach(function (task) { - var handler = jsHandlers[task.method]; - var args = [].concat( task.args ); - /* istanbul ignore else */ - if (typeof handler === 'function') { - args.unshift(id); - results.push(handler.apply(void 0, args)); - } - }); - return results - } - return new Error(("invalid instance id \"" + id + "\" or tasks")) -} - -/** - * Create a fresh instance of Vue for each Weex instance. - */ -function createVueModuleInstance (instanceId, weex) { - var exports = {}; - VueFactory(exports, weex.document); - var Vue = exports.Vue; - - var instance = instances[instanceId]; - - // patch reserved tag detection to account for dynamically registered - // components - var weexRegex = /^weex:/i; - var isReservedTag = Vue.config.isReservedTag || (function () { return false; }); - var isRuntimeComponent = Vue.config.isRuntimeComponent || (function () { return false; }); - Vue.config.isReservedTag = function (name) { - return (!isRuntimeComponent(name) && weex.supports(("@component/" + name))) || - isReservedTag(name) || - weexRegex.test(name) - }; - Vue.config.parsePlatformTagName = function (name) { return name.replace(weexRegex, ''); }; - - // expose weex-specific info - Vue.prototype.$instanceId = instanceId; - Vue.prototype.$document = instance.document; - - // expose weex native module getter on subVue prototype so that - // vdom runtime modules can access native modules via vnode.context - Vue.prototype.$requireWeexModule = weex.requireModule; - - // Hack `Vue` behavior to handle instance information and data - // before root component created. - Vue.mixin({ - beforeCreate: function beforeCreate () { - var options = this.$options; - // root component (vm) - if (options.el) { - // set external data of instance - var dataOption = options.data; - var internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {}; - options.data = Object.assign(internalData, instance.data); - // record instance by id - instance.app = this; - } - } - }); - - /** - * @deprecated Just instance variable `weex.config` - * Get instance config. - * @return {object} - */ - Vue.prototype.$getConfig = function () { - if (instance.app instanceof Vue) { - return instance.config - } - }; - - return Vue -} - -/** - * Generate HTML5 Timer APIs. An important point is that the callback - * will be converted into callback id when sent to native. So the - * framework can make sure no side effect of the callback happened after - * an instance destroyed. - * @param {[type]} instanceId [description] - * @param {[type]} moduleGetter [description] - * @return {[type]} [description] - */ -function getInstanceTimer (instanceId, moduleGetter) { - var instance = instances[instanceId]; - var timer = moduleGetter('timer'); - var timerAPIs = { - setTimeout: function () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var handler = function () { - args[0].apply(args, args.slice(2)); - }; - - timer.setTimeout(handler, args[1]); - return instance.document.taskCenter.callbackManager.lastCallbackId.toString() - }, - setInterval: function () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var handler = function () { - args[0].apply(args, args.slice(2)); - }; - - timer.setInterval(handler, args[1]); - return instance.document.taskCenter.callbackManager.lastCallbackId.toString() - }, - clearTimeout: function (n) { - timer.clearTimeout(n); - }, - clearInterval: function (n) { - timer.clearInterval(n); - } - }; - return timerAPIs -} - -/** - * Call a new function body with some global objects. - * @param {object} globalObjects - * @param {string} code - * @return {any} - */ -function callFunction (globalObjects, body) { - var globalKeys = []; - var globalValues = []; - for (var key in globalObjects) { - globalKeys.push(key); - globalValues.push(globalObjects[key]); - } - globalKeys.push(body); - - var result = new (Function.prototype.bind.apply( Function, [ null ].concat( globalKeys) )); - return result.apply(void 0, globalValues) -} - -exports.init = init; -exports.reset = reset; -exports.createInstance = createInstance; -exports.destroyInstance = destroyInstance; -exports.refreshInstance = refreshInstance; -exports.getRoot = getRoot; -exports.receiveTasks = receiveTasks; diff --git a/packages/weex-vue-framework/package.json b/packages/weex-vue-framework/package.json deleted file mode 100644 index 71bd1d65c88..00000000000 --- a/packages/weex-vue-framework/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "weex-vue-framework", - "version": "2.4.2-weex.1", - "description": "Vue 2.0 Framework for Weex", - "main": "index.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/weex-vue-framework#readme" -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000000..5a9fd448a56 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,7017 @@ +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 new file mode 100644 index 00000000000..18ec407efca --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'packages/*' diff --git a/scripts/alias.js b/scripts/alias.js new file mode 100644 index 00000000000..874686da40d --- /dev/null +++ b/scripts/alias.js @@ -0,0 +1,13 @@ +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 new file mode 100644 index 00000000000..2468512af4c --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,97 @@ +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 new file mode 100644 index 00000000000..e9f519853f1 --- /dev/null +++ b/scripts/config.js @@ -0,0 +1,305 @@ +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 new file mode 100644 index 00000000000..7df14317ff1 --- /dev/null +++ b/scripts/feature-flags.js @@ -0,0 +1,4 @@ +module.exports = { + NEW_SLOT_SYNTAX: true, + VBIND_PROP_SHORTHAND: false +} diff --git a/build/gen-release-note.js b/scripts/gen-release-note.js similarity index 100% rename from build/gen-release-note.js rename to scripts/gen-release-note.js diff --git a/build/git-hooks/commit-msg b/scripts/git-hooks/commit-msg similarity index 100% rename from build/git-hooks/commit-msg rename to scripts/git-hooks/commit-msg diff --git a/build/git-hooks/pre-commit b/scripts/git-hooks/pre-commit similarity index 100% rename from build/git-hooks/pre-commit rename to scripts/git-hooks/pre-commit diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 00000000000..03c5cf0ad26 --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,202 @@ +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 new file mode 100644 index 00000000000..a5150a5f8e6 --- /dev/null +++ b/scripts/verify-commit-msg.js @@ -0,0 +1,24 @@ +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/compiler/codeframe.ts b/src/compiler/codeframe.ts new file mode 100644 index 00000000000..86b540ffc6f --- /dev/null +++ b/src/compiler/codeframe.ts @@ -0,0 +1,52 @@ +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.js b/src/compiler/codegen/events.js deleted file mode 100644 index b15d93160d3..00000000000 --- a/src/compiler/codegen/events.js +++ /dev/null @@ -1,134 +0,0 @@ -/* @flow */ - -const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/ -const simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/ - -// keyCode aliases -const keyCodes: { [key: string]: number | Array<number> } = { - esc: 27, - tab: 9, - enter: 13, - space: 32, - up: 38, - left: 37, - right: 39, - down: 40, - 'delete': [8, 46] -} - -// #4868: modifiers that prevent the execution of the listener -// need to explicitly return null so that we can determine whether to remove -// the listener for .once -const genGuard = condition => `if(${condition})return null;` - -const modifierCode: { [key: string]: string } = { - stop: '$event.stopPropagation();', - prevent: '$event.preventDefault();', - self: genGuard(`$event.target !== $event.currentTarget`), - ctrl: genGuard(`!$event.ctrlKey`), - shift: genGuard(`!$event.shiftKey`), - alt: genGuard(`!$event.altKey`), - meta: genGuard(`!$event.metaKey`), - left: genGuard(`'button' in $event && $event.button !== 0`), - middle: genGuard(`'button' in $event && $event.button !== 1`), - right: genGuard(`'button' in $event && $event.button !== 2`) -} - -export function genHandlers ( - events: ASTElementHandlers, - isNative: boolean, - warn: Function -): string { - let res = isNative ? 'nativeOn:{' : 'on:{' - for (const name in events) { - const handler = events[name] - // #5330: warn click.right, since right clicks do not actually fire click events. - if (process.env.NODE_ENV !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { - warn( - `Use "contextmenu" instead of "click.right" since right clicks ` + - `do not actually fire "click" events.` - ) - } - res += `"${name}":${genHandler(name, handler)},` - } - return res.slice(0, -1) + '}' -} - -function genHandler ( - name: string, - handler: ASTElementHandler | Array<ASTElementHandler> -): string { - if (!handler) { - return 'function(){}' - } - - if (Array.isArray(handler)) { - return `[${handler.map(handler => genHandler(name, handler)).join(',')}]` - } - - const isMethodPath = simplePathRE.test(handler.value) - const isFunctionExpression = fnExpRE.test(handler.value) - - if (!handler.modifiers) { - return isMethodPath || isFunctionExpression - ? handler.value - : `function($event){${handler.value}}` // inline statement - } else { - let code = '' - let genModifierCode = '' - const keys = [] - for (const key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key] - // left/right - if (keyCodes[key]) { - keys.push(key) - } - } else if (key === 'exact') { - const modifiers: ASTModifiers = (handler.modifiers: any) - 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 - ? handler.value + '($event)' - : isFunctionExpression - ? `(${handler.value})($event)` - : handler.value - return `function($event){${code}${handlerCode}}` - } -} - -function genKeyFilter (keys: Array<string>): string { - return `if(!('button' in $event)&&${keys.map(genFilterCode).join('&&')})return null;` -} - -function genFilterCode (key: string): string { - const keyVal = parseInt(key, 10) - if (keyVal) { - return `$event.keyCode!==${keyVal}` - } - const code = keyCodes[key] - return ( - `_k($event.keyCode,` + - `${JSON.stringify(key)},` + - `${JSON.stringify(code)},` + - `$event.key)` - ) -} diff --git a/src/compiler/codegen/events.ts b/src/compiler/codegen/events.ts new file mode 100644 index 00000000000..5ee53e54b17 --- /dev/null +++ b/src/compiler/codegen/events.ts @@ -0,0 +1,170 @@ +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.js b/src/compiler/codegen/index.js deleted file mode 100644 index 17b31e5b510..00000000000 --- a/src/compiler/codegen/index.js +++ /dev/null @@ -1,496 +0,0 @@ -/* @flow */ - -import { genHandlers } from './events' -import baseDirectives from '../directives/index' -import { camelize, no, extend } from 'shared/util' -import { baseWarn, pluckModuleFunction } from '../helpers' - -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>; - - 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) => !isReservedTag(el.tag) - this.onceId = 0 - this.staticRenderFns = [] - } -} - -export type CodegenResult = { - render: string, - staticRenderFns: Array<string> -}; - -export function generate ( - ast: ASTElement | void, - options: CompilerOptions -): CodegenResult { - const state = new CodegenState(options) - const code = ast ? genElement(ast, state) : '_c("div")' - return { - render: `with(this){return ${code}}`, - staticRenderFns: state.staticRenderFns - } -} - -export function genElement (el: ASTElement, state: CodegenState): string { - 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) { - 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 { - const data = el.plain ? undefined : genData(el, state) - - const children = el.inlineTemplate ? null : genChildren(el, state, true) - code = `_c('${el.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 - } -} - -// hoist static sub-trees out -function genStatic (el: ASTElement, state: CodegenState): string { - el.staticProcessed = true - state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`) - 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) { - process.env.NODE_ENV !== 'production' && state.warn( - `v-once can only be used inside v-for that is keyed. ` - ) - 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 (process.env.NODE_ENV !== 'production' && - 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://vuejs.org/guide/list.html#key for more info.`, - 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, state.warn)},` - } - if (el.nativeEvents) { - data += `${genHandlers(el.nativeEvents, true, state.warn)},` - } - // slot target - // only for non-scoped slots - if (el.slotTarget && !el.slotScope) { - data += `slot:${el.slotTarget},` - } - // scoped slots - if (el.scopedSlots) { - data += `${genScopedSlots(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 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.arg}"` : '' - }${ - dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : '' - }},` - } - } - if (hasRuntime) { - return res.slice(0, -1) + ']' - } -} - -function genInlineTemplate (el: ASTElement, state: CodegenState): ?string { - const ast = el.children[0] - if (process.env.NODE_ENV !== 'production' && ( - el.children.length !== 1 || ast.type !== 1 - )) { - state.warn('Inline-template components must have exactly one child element.') - } - if (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 ( - slots: { [key: string]: ASTElement }, - state: CodegenState -): string { - return `scopedSlots:_u([${ - Object.keys(slots).map(key => { - return genScopedSlot(key, slots[key], state) - }).join(',') - }])` -} - -function genScopedSlot ( - key: string, - el: ASTElement, - state: CodegenState -): string { - if (el.for && !el.forProcessed) { - return genForScopedSlot(key, el, state) - } - const fn = `function(${String(el.slotScope)}){` + - `return ${el.tag === 'template' - ? el.if - ? `${el.if}?${genChildren(el, state) || 'undefined'}:undefined` - : genChildren(el, state) || 'undefined' - : genElement(el, state) - }}` - return `{key:${key},fn:${fn}}` -} - -function genForScopedSlot ( - key: string, - el: any, - state: CodegenState -): string { - const exp = el.for - const alias = el.alias - const iterator1 = el.iterator1 ? `,${el.iterator1}` : '' - const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' - el.forProcessed = true // avoid recursion - return `_l((${exp}),` + - `function(${alias}${iterator1}${iterator2}){` + - `return ${genScopedSlot(key, el, state)}` + - '})' -} - -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' - ) { - return (altGenElement || genElement)(el, state) - } - 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) - } 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 ? `,${children}` : ''}` - const attrs = el.attrs && `{${el.attrs.map(a => `${camelize(a.name)}:${a.value}`).join(',')}}` - const bind = el.attrsMap['v-bind'] - if ((attrs || bind) && !children) { - res += `,null` - } - if (attrs) { - res += `,${attrs}` - } - if (bind) { - res += `${attrs ? '' : ',null'},${bind}` - } - return res + ')' -} - -// componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent ( - componentName: string, - el: ASTElement, - state: CodegenState -): string { - const children = el.inlineTemplate ? null : genChildren(el, state, true) - return `_c(${componentName},${genData(el, state)}${ - children ? `,${children}` : '' - })` -} - -function genProps (props: Array<{ name: string, value: string }>): string { - let res = '' - for (let i = 0; i < props.length; i++) { - const prop = props[i] - res += `"${prop.name}":${transformSpecialNewlines(prop.value)},` - } - return res.slice(0, -1) -} - -// #3895, #4268 -function transformSpecialNewlines (text: string): string { - return text - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') -} diff --git a/src/compiler/codegen/index.ts b/src/compiler/codegen/index.ts new file mode 100644 index 00000000000..b0daf352f82 --- /dev/null +++ b/src/compiler/codegen/index.ts @@ -0,0 +1,668 @@ +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/create-compiler.js b/src/compiler/create-compiler.js deleted file mode 100644 index b6690d8ad20..00000000000 --- a/src/compiler/create-compiler.js +++ /dev/null @@ -1,55 +0,0 @@ -/* @flow */ - -import { extend } from 'shared/util' -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 = [] - const tips = [] - finalOptions.warn = (msg, tip) => { - (tip ? tips : errors).push(msg) - } - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = - (baseOptions.modules || []).concat(options.modules) - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ) - } - // copy other options - for (const key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key] - } - } - } - - const compiled = baseCompile(template, finalOptions) - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)) - } - compiled.errors = errors - compiled.tips = tips - return compiled - } - - return { - compile, - compileToFunctions: createCompileToFunctionFn(compile) - } - } -} diff --git a/src/compiler/create-compiler.ts b/src/compiler/create-compiler.ts new file mode 100644 index 00000000000..79a1a112e9c --- /dev/null +++ b/src/compiler/create-compiler.ts @@ -0,0 +1,83 @@ +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.js b/src/compiler/directives/bind.js deleted file mode 100644 index e103572729e..00000000000 --- a/src/compiler/directives/bind.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -export default function bind (el: ASTElement, dir: ASTDirective) { - el.wrapData = (code: string) => { - return `_b(${code},'${el.tag}',${dir.value},${ - dir.modifiers && dir.modifiers.prop ? 'true' : 'false' - }${ - dir.modifiers && dir.modifiers.sync ? ',true' : '' - })` - } -} diff --git a/src/compiler/directives/bind.ts b/src/compiler/directives/bind.ts new file mode 100644 index 00000000000..173871a4949 --- /dev/null +++ b/src/compiler/directives/bind.ts @@ -0,0 +1,9 @@ +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.js b/src/compiler/directives/index.js deleted file mode 100644 index 8f4342c0da7..00000000000 --- a/src/compiler/directives/index.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -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/index.ts b/src/compiler/directives/index.ts new file mode 100644 index 00000000000..72cfb22ab11 --- /dev/null +++ b/src/compiler/directives/index.ts @@ -0,0 +1,9 @@ +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.js b/src/compiler/directives/model.js deleted file mode 100644 index 8c1f55bf663..00000000000 --- a/src/compiler/directives/model.js +++ /dev/null @@ -1,145 +0,0 @@ -/* @flow */ - -/** - * Cross-platform code generation for component v-model - */ -export function genComponentModel ( - el: ASTElement, - value: string, - modifiers: ?ASTModifiers -): ?boolean { - const { number, trim } = modifiers || {} - - const baseValueExpression = '$$v' - let valueExpression = baseValueExpression - if (trim) { - valueExpression = - `(typeof ${baseValueExpression} === 'string'` + - `? ${baseValueExpression}.trim()` + - `: ${baseValueExpression})` - } - if (number) { - valueExpression = `_n(${valueExpression})` - } - const assignment = genAssignmentCode(value, valueExpression) - - el.model = { - value: `(${value})`, - expression: `"${value}"`, - callback: `function (${baseValueExpression}) {${assignment}}` - } -} - -/** - * Cross-platform codegen helper for generating v-model value assignment code. - */ -export function genAssignmentCode ( - value: string, - assignment: string -): string { - const 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 { - 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/model.ts b/src/compiler/directives/model.ts new file mode 100644 index 00000000000..4538ccda91f --- /dev/null +++ b/src/compiler/directives/model.ts @@ -0,0 +1,145 @@ +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.js b/src/compiler/directives/on.js deleted file mode 100644 index 893a678ede8..00000000000 --- a/src/compiler/directives/on.js +++ /dev/null @@ -1,10 +0,0 @@ -/* @flow */ - -import { warn } from 'core/util/index' - -export default function on (el: ASTElement, dir: ASTDirective) { - if (process.env.NODE_ENV !== 'production' && dir.modifiers) { - warn(`v-on without argument does not support modifiers.`) - } - el.wrapListeners = (code: string) => `_g(${code},${dir.value})` -} diff --git a/src/compiler/directives/on.ts b/src/compiler/directives/on.ts new file mode 100644 index 00000000000..291d4da3a3f --- /dev/null +++ b/src/compiler/directives/on.ts @@ -0,0 +1,9 @@ +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.js b/src/compiler/error-detector.js deleted file mode 100644 index 6321f4f7a10..00000000000 --- a/src/compiler/error-detector.js +++ /dev/null @@ -1,102 +0,0 @@ -/* @flow */ - -import { dirRE, onRE } from './parser/index' - -// these keywords should not appear inside expressions, but operators like -// typeof, instanceof and in are allowed -const prohibitedKeywordRE = new RegExp('\\b' + ( - 'do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' + - 'super,throw,while,yield,delete,export,import,return,switch,default,' + - 'extends,finally,continue,debugger,function,arguments' -).split(',').join('\\b|\\b') + '\\b') - -// these unary operators should not be used as property/method names -const unaryOperatorsRE = new RegExp('\\b' + ( - 'delete,typeof,void' -).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)') - -// check valid identifier for v-for -const identRE = /[A-Za-z_$][\w$]*/ - -// strip strings in expressions -const stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g - -// detect problematic expressions in a template -export function detectErrors (ast: ?ASTNode): Array<string> { - const errors: Array<string> = [] - if (ast) { - checkNode(ast, errors) - } - return errors -} - -function checkNode (node: ASTNode, errors: Array<string>) { - if (node.type === 1) { - for (const name in node.attrsMap) { - if (dirRE.test(name)) { - const value = node.attrsMap[name] - if (value) { - if (name === 'v-for') { - checkFor(node, `v-for="${value}"`, errors) - } else if (onRE.test(name)) { - checkEvent(value, `${name}="${value}"`, errors) - } else { - checkExpression(value, `${name}="${value}"`, errors) - } - } - } - } - if (node.children) { - for (let i = 0; i < node.children.length; i++) { - checkNode(node.children[i], errors) - } - } - } else if (node.type === 2) { - checkExpression(node.expression, node.text, errors) - } -} - -function checkEvent (exp: string, text: string, errors: Array<string>) { - const stipped = exp.replace(stripStringRE, '') - const keywordMatch: any = stipped.match(unaryOperatorsRE) - if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { - errors.push( - `avoid using JavaScript unary operator as property name: ` + - `"${keywordMatch[0]}" in expression ${text.trim()}` - ) - } - checkExpression(exp, text, errors) -} - -function checkFor (node: ASTElement, text: string, errors: Array<string>) { - checkExpression(node.for || '', text, errors) - checkIdentifier(node.alias, 'v-for alias', text, errors) - checkIdentifier(node.iterator1, 'v-for iterator', text, errors) - checkIdentifier(node.iterator2, 'v-for iterator', text, errors) -} - -function checkIdentifier (ident: ?string, type: string, text: string, errors: Array<string>) { - if (typeof ident === 'string' && !identRE.test(ident)) { - errors.push(`invalid ${type} "${ident}" in expression: ${text.trim()}`) - } -} - -function checkExpression (exp: string, text: string, errors: Array<string>) { - try { - new Function(`return ${exp}`) - } catch (e) { - const keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE) - if (keywordMatch) { - errors.push( - `avoid using JavaScript keyword as property name: ` + - `"${keywordMatch[0]}"\n Raw expression: ${text.trim()}` - ) - } else { - errors.push( - `invalid expression: ${e.message} in\n\n` + - ` ${exp}\n\n` + - ` Raw expression: ${text.trim()}\n` - ) - } - } -} diff --git a/src/compiler/error-detector.ts b/src/compiler/error-detector.ts new file mode 100644 index 00000000000..d60452f7d99 --- /dev/null +++ b/src/compiler/error-detector.ts @@ -0,0 +1,158 @@ +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.js b/src/compiler/helpers.js deleted file mode 100644 index 5c844bd42e0..00000000000 --- a/src/compiler/helpers.js +++ /dev/null @@ -1,130 +0,0 @@ -/* @flow */ - -import { parseFilters } from './parser/filter-parser' - -export function baseWarn (msg: string) { - console.error(`[Vue compiler]: ${msg}`) -} - -export function pluckModuleFunction<F: Function> ( - modules: ?Array<Object>, - key: string -): Array<F> { - return modules - ? modules.map(m => m[key]).filter(_ => _) - : [] -} - -export function addProp (el: ASTElement, name: string, value: string) { - (el.props || (el.props = [])).push({ name, value }) -} - -export function addAttr (el: ASTElement, name: string, value: string) { - (el.attrs || (el.attrs = [])).push({ name, value }) -} - -export function addDirective ( - el: ASTElement, - name: string, - rawName: string, - value: string, - arg: ?string, - modifiers: ?ASTModifiers -) { - (el.directives || (el.directives = [])).push({ name, rawName, value, arg, modifiers }) -} - -export function addHandler ( - el: ASTElement, - name: string, - value: string, - modifiers: ?ASTModifiers, - important?: boolean, - warn?: Function -) { - // warn prevent and passive modifier - /* istanbul ignore if */ - if ( - process.env.NODE_ENV !== 'production' && warn && - modifiers && modifiers.prevent && modifiers.passive - ) { - warn( - 'passive and prevent can\'t be used together. ' + - 'Passive handler can\'t prevent default event.' - ) - } - // check capture modifier - if (modifiers && modifiers.capture) { - delete modifiers.capture - name = '!' + name // mark the event as captured - } - if (modifiers && modifiers.once) { - delete modifiers.once - name = '~' + name // mark the event as once - } - /* istanbul ignore if */ - if (modifiers && modifiers.passive) { - delete modifiers.passive - name = '&' + name // mark the event as passive - } - let events - if (modifiers && modifiers.native) { - delete modifiers.native - events = el.nativeEvents || (el.nativeEvents = {}) - } else { - events = el.events || (el.events = {}) - } - const newHandler = { value, modifiers } - const handlers = events[name] - /* istanbul ignore if */ - if (Array.isArray(handlers)) { - important ? handlers.unshift(newHandler) : handlers.push(newHandler) - } else if (handlers) { - events[name] = important ? [newHandler, handlers] : [handlers, newHandler] - } else { - events[name] = newHandler - } -} - -export function getBindingAttr ( - el: ASTElement, - name: string, - getStatic?: boolean -): ?string { - const dynamicValue = - getAndRemoveAttr(el, ':' + name) || - getAndRemoveAttr(el, 'v-bind:' + name) - if (dynamicValue != null) { - return parseFilters(dynamicValue) - } else if (getStatic !== false) { - const staticValue = getAndRemoveAttr(el, name) - if (staticValue != null) { - return JSON.stringify(staticValue) - } - } -} - -// 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 { - 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 -} diff --git a/src/compiler/helpers.ts b/src/compiler/helpers.ts new file mode 100644 index 00000000000..f07b28f35c8 --- /dev/null +++ b/src/compiler/helpers.ts @@ -0,0 +1,243 @@ +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 deleted file mode 100644 index 075917bdc37..00000000000 --- a/src/compiler/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/* @flow */ - -import { parse } from './parser/index' -import { optimize } from './optimizer' -import { generate } from './codegen/index' -import { createCompilerCreator } from './create-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) - optimize(ast, options) - const code = generate(ast, options) - return { - ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -}) diff --git a/src/compiler/index.ts b/src/compiler/index.ts new file mode 100644 index 00000000000..e8a17e11dc4 --- /dev/null +++ b/src/compiler/index.ts @@ -0,0 +1,24 @@ +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.js b/src/compiler/optimizer.js deleted file mode 100644 index ce43203500a..00000000000 --- a/src/compiler/optimizer.js +++ /dev/null @@ -1,128 +0,0 @@ -/* @flow */ - -import { makeMap, isBuiltInTag, cached, no } from 'shared/util' - -let isStaticKey -let isPlatformReservedTag - -const genStaticKeysCached = cached(genStaticKeys) - -/** - * Goal of the optimizer: walk the generated template AST tree - * and detect sub-trees that are purely static, i.e. parts of - * the DOM that never needs to change. - * - * Once we detect these sub-trees, we can: - * - * 1. Hoist them into constants, so that we no longer need to - * create fresh nodes for them on each re-render; - * 2. Completely skip them in the patching process. - */ -export function optimize (root: ?ASTElement, options: CompilerOptions) { - if (!root) return - isStaticKey = genStaticKeysCached(options.staticKeys || '') - isPlatformReservedTag = options.isReservedTag || no - // first pass: mark all non-static nodes. - markStatic(root) - // second pass: mark static roots. - markStaticRoots(root, false) -} - -function genStaticKeys (keys: string): Function { - return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + - (keys ? ',' + keys : '') - ) -} - -function markStatic (node: ASTNode) { - node.static = isStatic(node) - if (node.type === 1) { - // do not make component slot content static. this avoids - // 1. components not able to mutate slot nodes - // 2. static slot content fails for hot-reloading - if ( - !isPlatformReservedTag(node.tag) && - node.tag !== 'slot' && - node.attrsMap['inline-template'] == null - ) { - return - } - for (let i = 0, l = node.children.length; i < l; i++) { - const child = node.children[i] - markStatic(child) - if (!child.static) { - node.static = false - } - } - 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/optimizer.ts b/src/compiler/optimizer.ts new file mode 100644 index 00000000000..49ef6aee2f1 --- /dev/null +++ b/src/compiler/optimizer.ts @@ -0,0 +1,135 @@ +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.js b/src/compiler/parser/entity-decoder.js deleted file mode 100644 index 2836294f2c9..00000000000 --- a/src/compiler/parser/entity-decoder.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -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/entity-decoder.ts b/src/compiler/parser/entity-decoder.ts new file mode 100644 index 00000000000..031f3291763 --- /dev/null +++ b/src/compiler/parser/entity-decoder.ts @@ -0,0 +1,9 @@ +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.js b/src/compiler/parser/filter-parser.js deleted file mode 100644 index ca8ead850ae..00000000000 --- a/src/compiler/parser/filter-parser.js +++ /dev/null @@ -1,97 +0,0 @@ -/* @flow */ - -const validDivisionCharRE = /[\w).+\-_$\]]/ - -export function parseFilters (exp: string): string { - let inSingle = false - let inDouble = false - let inTemplateString = false - let inRegex = false - let curly = 0 - let square = 0 - let paren = 0 - let lastFilterIndex = 0 - let c, prev, i, expression, filters - - for (i = 0; i < exp.length; i++) { - prev = c - c = exp.charCodeAt(i) - if (inSingle) { - if (c === 0x27 && prev !== 0x5C) inSingle = false - } else if (inDouble) { - if (c === 0x22 && prev !== 0x5C) inDouble = false - } else if (inTemplateString) { - if (c === 0x60 && prev !== 0x5C) inTemplateString = false - } else if (inRegex) { - if (c === 0x2f && prev !== 0x5C) inRegex = false - } else if ( - c === 0x7C && // pipe - exp.charCodeAt(i + 1) !== 0x7C && - exp.charCodeAt(i - 1) !== 0x7C && - !curly && !square && !paren - ) { - if (expression === undefined) { - // first filter, end of expression - lastFilterIndex = i + 1 - expression = exp.slice(0, i).trim() - } else { - pushFilter() - } - } else { - switch (c) { - case 0x22: inDouble = true; break // " - case 0x27: inSingle = true; break // ' - case 0x60: inTemplateString = true; break // ` - case 0x28: paren++; break // ( - case 0x29: paren--; break // ) - case 0x5B: square++; break // [ - case 0x5D: square--; break // ] - case 0x7B: curly++; break // { - case 0x7D: curly--; break // } - } - if (c === 0x2f) { // / - let j = i - 1 - let p - // find first non-whitespace prev char - for (; j >= 0; j--) { - p = exp.charAt(j) - if (p !== ' ') break - } - if (!p || !validDivisionCharRE.test(p)) { - inRegex = true - } - } - } - } - - if (expression === undefined) { - expression = exp.slice(0, i).trim() - } else if (lastFilterIndex !== 0) { - pushFilter() - } - - function pushFilter () { - (filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim()) - lastFilterIndex = i + 1 - } - - if (filters) { - for (i = 0; i < filters.length; i++) { - expression = wrapFilter(expression, filters[i]) - } - } - - return expression -} - -function wrapFilter (exp: string, filter: string): string { - const i = filter.indexOf('(') - if (i < 0) { - // _f: resolveFilter - return `_f("${filter}")(${exp})` - } else { - const name = filter.slice(0, i) - const args = filter.slice(i + 1) - return `_f("${name}")(${exp},${args}` - } -} diff --git a/src/compiler/parser/filter-parser.ts b/src/compiler/parser/filter-parser.ts new file mode 100644 index 00000000000..b5aa5b7f4a6 --- /dev/null +++ b/src/compiler/parser/filter-parser.ts @@ -0,0 +1,116 @@ +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.js b/src/compiler/parser/html-parser.js deleted file mode 100644 index 86955e0e7ab..00000000000 --- a/src/compiler/parser/html-parser.js +++ /dev/null @@ -1,309 +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, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - */ - -import { makeMap, no } from 'shared/util' -import { isNonPhrasingTag } from 'web/compiler/util' - -// Regular Expressions for parsing tags and attributes -const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/ -// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName -// but for Vue templates we can enforce a simple charset -const ncname = '[a-zA-Z_][\\w\\-\\.]*' -const qnameCapture = `((?:${ncname}\\:)?${ncname})` -const startTagOpen = new RegExp(`^<${qnameCapture}`) -const startTagClose = /^\s*(\/?)>/ -const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`) -const doctype = /^<!DOCTYPE [^>]+>/i -const comment = /^<!--/ -const conditionalComment = /^<!\[/ - -let IS_REGEX_CAPTURING_BROKEN = false -'x'.replace(/x(.)?/g, function (m, g) { - IS_REGEX_CAPTURING_BROKEN = g === '' -}) - -// Special Elements (can contain anything) -export const isPlainTextElement = makeMap('script,style,textarea', true) -const reCache = {} - -const decodingMap = { - '<': '<', - '>': '>', - '"': '"', - '&': '&', - ' ': '\n', - '	': '\t' -} -const encodedAttr = /&(?:lt|gt|quot|amp);/g -const encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#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 function parseHTML (html, options) { - const stack = [] - 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(html.substring(4, commentEnd)) - } - advance(commentEnd + 3) - continue - } - } - - // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment - if (conditionalComment.test(html)) { - const conditionalEnd = html.indexOf(']>') - - if (conditionalEnd >= 0) { - advance(conditionalEnd + 2) - continue - } - } - - // Doctype: - const doctypeMatch = html.match(doctype) - if (doctypeMatch) { - advance(doctypeMatch[0].length) - continue - } - - // End tag: - const endTagMatch = html.match(endTag) - if (endTagMatch) { - const curIndex = index - advance(endTagMatch[0].length) - parseEndTag(endTagMatch[1], curIndex, index) - continue - } - - // Start tag: - const startTagMatch = parseStartTag() - if (startTagMatch) { - handleStartTag(startTagMatch) - if (shouldIgnoreFirstNewline(lastTag, 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) - advance(textEnd) - } - - if (textEnd < 0) { - text = html - html = '' - } - - if (options.chars && text) { - options.chars(text) - } - } 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') - .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 (process.env.NODE_ENV !== 'production' && !stack.length && options.warn) { - options.warn(`Mal-formatted tag at end of template: "${html}"`) - } - break - } - } - - // Clean up any remaining tags - parseEndTag() - - function advance (n) { - index += n - html = html.substring(n) - } - - function parseStartTag () { - const start = html.match(startTagOpen) - if (start) { - const match = { - tagName: start[1], - attrs: [], - start: index - } - advance(start[0].length) - let end, attr - while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { - advance(attr[0].length) - match.attrs.push(attr) - } - if (end) { - match.unarySlash = end[1] - advance(end[0].length) - match.end = index - return match - } - } - } - - function handleStartTag (match) { - const tagName = match.tagName - const unarySlash = match.unarySlash - - if (expectHTML) { - if (lastTag === 'p' && isNonPhrasingTag(tagName)) { - parseEndTag(lastTag) - } - if (canBeLeftOpenTag(tagName) && lastTag === tagName) { - parseEndTag(tagName) - } - } - - const unary = isUnaryTag(tagName) || !!unarySlash - - const l = match.attrs.length - const attrs = new Array(l) - for (let i = 0; i < l; i++) { - const args = match.attrs[i] - // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 - if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { - if (args[3] === '') { delete args[3] } - if (args[4] === '') { delete args[4] } - if (args[5] === '') { delete args[5] } - } - 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 (!unary) { - stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs }) - lastTag = tagName - } - - if (options.start) { - options.start(tagName, attrs, unary, match.start, match.end) - } - } - - function parseEndTag (tagName, start, end) { - let pos, lowerCasedTagName - if (start == null) start = index - if (end == null) end = index - - if (tagName) { - lowerCasedTagName = tagName.toLowerCase() - } - - // Find the closest opened tag of the same type - if (tagName) { - for (pos = stack.length - 1; pos >= 0; pos--) { - if (stack[pos].lowerCasedTag === lowerCasedTagName) { - break - } - } - } else { - // If no tag name is provided, clean shop - pos = 0 - } - - if (pos >= 0) { - // Close all the open elements, up the stack - for (let i = stack.length - 1; i >= pos; i--) { - if (process.env.NODE_ENV !== 'production' && - (i > pos || !tagName) && - options.warn - ) { - options.warn( - `tag <${stack[i].tag}> has no matching end tag.` - ) - } - if (options.end) { - options.end(stack[i].tag, start, end) - } - } - - // Remove the open elements from the stack - stack.length = pos - lastTag = pos && stack[pos - 1].tag - } else if (lowerCasedTagName === 'br') { - if (options.start) { - options.start(tagName, [], true, start, end) - } - } else if (lowerCasedTagName === 'p') { - if (options.start) { - options.start(tagName, [], false, start, end) - } - if (options.end) { - options.end(tagName, start, end) - } - } - } -} diff --git a/src/compiler/parser/html-parser.ts b/src/compiler/parser/html-parser.ts new file mode 100644 index 00000000000..29acbfc8fc5 --- /dev/null +++ b/src/compiler/parser/html-parser.ts @@ -0,0 +1,341 @@ +/** + * 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 = { + '<': '<', + '>': '>', + '"': '"', + '&': '&', + ' ': '\n', + '	': '\t', + ''': "'" +} +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.js b/src/compiler/parser/index.js deleted file mode 100644 index 1a28e86de2e..00000000000 --- a/src/compiler/parser/index.js +++ /dev/null @@ -1,649 +0,0 @@ -/* @flow */ - -import he from 'he' -import { parseHTML } from './html-parser' -import { parseText } from './text-parser' -import { parseFilters } from './filter-parser' -import { cached, no, camelize } from 'shared/util' -import { genAssignmentCode } from '../directives/model' -import { isIE, isEdge, isServerRendering } from 'core/util/env' - -import { - addProp, - addAttr, - baseWarn, - addHandler, - addDirective, - getBindingAttr, - getAndRemoveAttr, - pluckModuleFunction -} from '../helpers' - -export const onRE = /^@|^v-on:/ -export const dirRE = /^v-|^@|^:/ -export const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/ -export const forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/ - -const argRE = /:(.*)$/ -const bindRE = /^:|^v-bind:/ -const modifierRE = /\.[^.]+/g - -const decodeHTMLCached = cached(he.decode) - -// configurable state -export let warn: any -let delimiters -let transforms -let preTransforms -let postTransforms -let platformIsPreTag -let platformMustUseProp -let platformGetTagNamespace - -type Attr = { name: string; value: string }; - -export function createASTElement ( - tag: string, - attrs: Array<Attr>, - parent: ASTElement | void -): ASTElement { - return { - type: 1, - tag, - attrsList: attrs, - attrsMap: makeAttrsMap(attrs), - parent, - children: [] - } -} - -/** - * Convert HTML string to AST. - */ -export function parse ( - template: string, - options: CompilerOptions -): ASTElement | void { - warn = options.warn || baseWarn - - platformIsPreTag = options.isPreTag || no - platformMustUseProp = options.mustUseProp || no - platformGetTagNamespace = options.getTagNamespace || no - - transforms = pluckModuleFunction(options.modules, 'transformNode') - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode') - postTransforms = pluckModuleFunction(options.modules, 'postTransformNode') - - delimiters = options.delimiters - - const stack = [] - const preserveWhitespace = options.preserveWhitespace !== false - let root - let currentParent - let inVPre = false - let inPre = false - let warned = false - - function warnOnce (msg) { - if (!warned) { - warned = true - warn(msg) - } - } - - function endPre (element) { - // check pre state - if (element.pre) { - inVPre = false - } - if (platformIsPreTag(element.tag)) { - inPre = false - } - } - - parseHTML(template, { - warn, - expectHTML: options.expectHTML, - isUnaryTag: options.isUnaryTag, - canBeLeftOpenTag: options.canBeLeftOpenTag, - shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, - shouldKeepComment: options.comments, - start (tag, attrs, unary) { - // check namespace. - // inherit parent ns if there is one - const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag) - - // handle IE svg bug - /* istanbul ignore if */ - if (isIE && ns === 'svg') { - attrs = guardIESVGBug(attrs) - } - - let element: ASTElement = createASTElement(tag, attrs, currentParent) - if (ns) { - element.ns = ns - } - - if (isForbiddenTag(element) && !isServerRendering()) { - element.forbidden = true - process.env.NODE_ENV !== 'production' && warn( - 'Templates should only be responsible for mapping the state to the ' + - 'UI. Avoid placing tags with side-effects in your templates, such as ' + - `<${tag}>` + ', as they will not be parsed.' - ) - } - - // apply pre-transforms - for (let i = 0; i < preTransforms.length; i++) { - 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) - // element-scope stuff - processElement(element, options) - } - - function checkRootConstraints (el) { - if (process.env.NODE_ENV !== 'production') { - if (el.tag === 'slot' || el.tag === 'template') { - warnOnce( - `Cannot use <${el.tag}> as component root element because it may ` + - 'contain multiple nodes.' - ) - } - if (el.attrsMap.hasOwnProperty('v-for')) { - warnOnce( - 'Cannot use v-for on stateful component root element because ' + - 'it renders multiple elements.' - ) - } - } - } - - // tree management - if (!root) { - root = element - checkRootConstraints(root) - } else if (!stack.length) { - // allow root elements with v-if, v-else-if and v-else - if (root.if && (element.elseif || element.else)) { - checkRootConstraints(element) - addIfCondition(root, { - exp: element.elseif, - block: element - }) - } else if (process.env.NODE_ENV !== 'production') { - warnOnce( - `Component template should contain exactly one root element. ` + - `If you are using v-if on multiple elements, ` + - `use v-else-if to chain them instead.` - ) - } - } - if (currentParent && !element.forbidden) { - if (element.elseif || element.else) { - processIfConditions(element, currentParent) - } else if (element.slotScope) { // scoped slot - currentParent.plain = false - const name = element.slotTarget || '"default"' - ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element - } else { - currentParent.children.push(element) - element.parent = currentParent - } - } - if (!unary) { - currentParent = element - stack.push(element) - } else { - endPre(element) - } - // apply post-transforms - for (let i = 0; i < postTransforms.length; i++) { - postTransforms[i](element, options) - } - }, - - end () { - // remove trailing whitespace - const element = stack[stack.length - 1] - const lastNode = element.children[element.children.length - 1] - if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { - element.children.pop() - } - // pop stack - stack.length -= 1 - currentParent = stack[stack.length - 1] - endPre(element) - }, - - chars (text: string) { - if (!currentParent) { - if (process.env.NODE_ENV !== 'production') { - if (text === template) { - warnOnce( - 'Component template requires a root element, rather than just text.' - ) - } else if ((text = text.trim())) { - warnOnce( - `text "${text}" outside root element will be ignored.` - ) - } - } - return - } - // IE textarea placeholder bug - /* istanbul ignore if */ - if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text - ) { - return - } - const children = currentParent.children - text = inPre || text.trim() - ? isTextTag(currentParent) ? text : decodeHTMLCached(text) - // only preserve whitespace if its not right after a starting tag - : preserveWhitespace && children.length ? ' ' : '' - if (text) { - let expression - if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { - children.push({ - type: 2, - expression, - text - }) - } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') { - children.push({ - type: 3, - text - }) - } - } - }, - comment (text: string) { - currentParent.children.push({ - type: 3, - text, - isComment: true - }) - } - }) - return root -} - -function processPre (el) { - if (getAndRemoveAttr(el, 'v-pre') != null) { - el.pre = true - } -} - -function processRawAttrs (el) { - const l = el.attrsList.length - if (l) { - const attrs = el.attrs = new Array(l) - for (let i = 0; i < l; i++) { - attrs[i] = { - name: el.attrsList[i].name, - value: JSON.stringify(el.attrsList[i].value) - } - } - } else if (!el.pre) { - // non root node in pre blocks with no attributes - el.plain = true - } -} - -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.attrsList.length - - processRef(element) - processSlot(element) - processComponent(element) - for (let i = 0; i < transforms.length; i++) { - element = transforms[i](element, options) || element - } - processAttrs(element) -} - -function processKey (el) { - const exp = getBindingAttr(el, 'key') - if (exp) { - if (process.env.NODE_ENV !== 'production' && el.tag === 'template') { - warn(`<template> cannot be keyed. Place the key on real elements instead.`) - } - 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 inMatch = exp.match(forAliasRE) - if (!inMatch) { - process.env.NODE_ENV !== 'production' && warn( - `Invalid v-for expression: ${exp}` - ) - return - } - el.for = inMatch[2].trim() - const alias = inMatch[1].trim() - const iteratorMatch = alias.match(forIteratorRE) - if (iteratorMatch) { - el.alias = iteratorMatch[1].trim() - el.iterator1 = iteratorMatch[2].trim() - if (iteratorMatch[3]) { - el.iterator2 = iteratorMatch[3].trim() - } - } else { - el.alias = alias - } - } -} - -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 (process.env.NODE_ENV !== 'production') { - warn( - `v-${el.elseif ? ('else-if="' + el.elseif + '"') : 'else'} ` + - `used on element <${el.tag}> without corresponding v-if.` - ) - } -} - -function findPrevElement (children: Array<any>): ASTElement | void { - let i = children.length - while (i--) { - if (children[i].type === 1) { - return children[i] - } else { - if (process.env.NODE_ENV !== 'production' && children[i].text !== ' ') { - warn( - `text "${children[i].text.trim()}" between v-if and v-else(-if) ` + - `will be ignored.` - ) - } - 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 - } -} - -function processSlot (el) { - if (el.tag === 'slot') { - el.slotName = getBindingAttr(el, 'name') - if (process.env.NODE_ENV !== 'production' && 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.` - ) - } - } else { - let slotScope - if (el.tag === 'template') { - slotScope = getAndRemoveAttr(el, 'scope') - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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.`, - true - ) - } - el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope') - } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) { - el.slotScope = slotScope - } - const slotTarget = getBindingAttr(el, 'slot') - if (slotTarget) { - el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget - // 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) - } - } - } -} - -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, isProp - 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) - if (modifiers) { - name = name.replace(modifierRE, '') - } - if (bindRE.test(name)) { // v-bind - name = name.replace(bindRE, '') - value = parseFilters(value) - isProp = false - if (modifiers) { - if (modifiers.prop) { - isProp = true - name = camelize(name) - if (name === 'innerHtml') name = 'innerHTML' - } - if (modifiers.camel) { - name = camelize(name) - } - if (modifiers.sync) { - addHandler( - el, - `update:${camelize(name)}`, - genAssignmentCode(value, `$event`) - ) - } - } - if (isProp || ( - !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) - )) { - addProp(el, name, value) - } else { - addAttr(el, name, value) - } - } else if (onRE.test(name)) { // v-on - name = name.replace(onRE, '') - addHandler(el, name, value, modifiers, false, warn) - } else { // normal directives - name = name.replace(dirRE, '') - // parse arg - const argMatch = name.match(argRE) - const arg = argMatch && argMatch[1] - if (arg) { - name = name.slice(0, -(arg.length + 1)) - } - addDirective(el, name, rawName, value, arg, modifiers) - if (process.env.NODE_ENV !== 'production' && name === 'model') { - checkForAliasModel(el, value) - } - } - } else { - // literal attribute - if (process.env.NODE_ENV !== 'production') { - const expression = parseText(value, delimiters) - if (expression) { - 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">.' - ) - } - } - addAttr(el, name, JSON.stringify(value)) - // #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') - } - } - } -} - -function checkInFor (el: ASTElement): boolean { - let parent = 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<Object>): Object { - const map = {} - for (let i = 0, l = attrs.length; i < l; i++) { - if ( - process.env.NODE_ENV !== 'production' && - map[attrs[i].name] && !isIE && !isEdge - ) { - warn('duplicate attribute: ' + attrs[i].name) - } - 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 = [] - 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 = _el.parent - } -} diff --git a/src/compiler/parser/index.ts b/src/compiler/parser/index.ts new file mode 100644 index 00000000000..148cc0110f5 --- /dev/null +++ b/src/compiler/parser/index.ts @@ -0,0 +1,999 @@ +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.js b/src/compiler/parser/text-parser.js deleted file mode 100644 index 10613d11e0c..00000000000 --- a/src/compiler/parser/text-parser.js +++ /dev/null @@ -1,41 +0,0 @@ -/* @flow */ - -import { cached } from 'shared/util' -import { parseFilters } from './filter-parser' - -const defaultTagRE = /\{\{((?:.|\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') -}) - -export function parseText ( - text: string, - delimiters?: [string, string] -): string | void { - const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE - if (!tagRE.test(text)) { - return - } - const tokens = [] - let lastIndex = tagRE.lastIndex = 0 - let match, index - while ((match = tagRE.exec(text))) { - index = match.index - // push text token - if (index > lastIndex) { - tokens.push(JSON.stringify(text.slice(lastIndex, index))) - } - // tag token - const exp = parseFilters(match[1].trim()) - tokens.push(`_s(${exp})`) - lastIndex = index + match[0].length - } - if (lastIndex < text.length) { - tokens.push(JSON.stringify(text.slice(lastIndex))) - } - return tokens.join('+') -} diff --git a/src/compiler/parser/text-parser.ts b/src/compiler/parser/text-parser.ts new file mode 100644 index 00000000000..ea7387ffff8 --- /dev/null +++ b/src/compiler/parser/text-parser.ts @@ -0,0 +1,52 @@ +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/to-function.js b/src/compiler/to-function.js deleted file mode 100644 index d9f20056093..00000000000 --- a/src/compiler/to-function.js +++ /dev/null @@ -1,101 +0,0 @@ -/* @flow */ - -import { noop, extend } from 'shared/util' -import { warn as baseWarn, tip } from 'core/util/debug' - -type CompiledFunctionResult = { - render: Function; - staticRenderFns: Array<Function>; -}; - -function createFunction (code, errors) { - try { - return new Function(code) - } catch (err) { - errors.push({ err, code }) - return noop - } -} - -export function createCompileToFunctionFn (compile: Function): Function { - const cache: { - [key: string]: CompiledFunctionResult; - } = 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 (process.env.NODE_ENV !== 'production') { - // detect possible CSP restriction - try { - new Function('return 1') - } catch (e) { - if (e.toString().match(/unsafe-eval|CSP/)) { - warn( - 'It seems you are using the standalone build of Vue.js in an ' + - 'environment with Content Security Policy that prohibits unsafe-eval. ' + - 'The template compiler cannot work in this environment. Consider ' + - 'relaxing the policy to allow unsafe-eval or pre-compiling your ' + - 'templates into render functions.' - ) - } - } - } - - // check cache - const key = options.delimiters - ? String(options.delimiters) + template - : template - if (cache[key]) { - return cache[key] - } - - // compile - const compiled = compile(template, options) - - // check compilation errors/tips - if (process.env.NODE_ENV !== 'production') { - if (compiled.errors && compiled.errors.length) { - warn( - `Error compiling template:\n\n${template}\n\n` + - compiled.errors.map(e => `- ${e}`).join('\n') + '\n', - vm - ) - } - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(msg => tip(msg, vm)) - } - } - - // turn code into functions - const res = {} - const fnGenErrors = [] - res.render = 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 (process.env.NODE_ENV !== 'production') { - if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { - warn( - `Failed to generate render function:\n\n` + - fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'), - vm - ) - } - } - - return (cache[key] = res) - } -} diff --git a/src/compiler/to-function.ts b/src/compiler/to-function.ts new file mode 100644 index 00000000000..de0b95c1d35 --- /dev/null +++ b/src/compiler/to-function.ts @@ -0,0 +1,119 @@ +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/core/components/index.js b/src/core/components/index.ts similarity index 100% rename from src/core/components/index.js rename to src/core/components/index.ts diff --git a/src/core/components/keep-alive.js b/src/core/components/keep-alive.js deleted file mode 100644 index eb79b1f8fa6..00000000000 --- a/src/core/components/keep-alive.js +++ /dev/null @@ -1,120 +0,0 @@ -/* @flow */ - -import { isRegExp, remove } from 'shared/util' -import { getFirstComponentChild } from 'core/vdom/helpers/index' - -type VNodeCache = { [key: string]: ?VNode }; - -function getComponentName (opts: ?VNodeComponentOptions): ?string { - return opts && (opts.Ctor.options.name || opts.tag) -} - -function matches (pattern: string | RegExp | Array<string>, name: string): boolean { - if (Array.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: any, filter: Function) { - const { cache, keys, _vnode } = keepAliveInstance - for (const key in cache) { - const cachedNode: ?VNode = cache[key] - if (cachedNode) { - const name: ?string = getComponentName(cachedNode.componentOptions) - if (name && !filter(name)) { - pruneCacheEntry(cache, key, keys, _vnode) - } - } - } -} - -function pruneCacheEntry ( - cache: VNodeCache, - key: string, - keys: Array<string>, - current?: VNode -) { - const cached = cache[key] - if (cached && cached !== current) { - cached.componentInstance.$destroy() - } - cache[key] = null - remove(keys, key) -} - -const patternTypes: Array<Function> = [String, RegExp, Array] - -export default { - name: 'keep-alive', - abstract: true, - - props: { - include: patternTypes, - exclude: patternTypes, - max: [String, Number] - }, - - created () { - this.cache = Object.create(null) - this.keys = [] - }, - - destroyed () { - for (const key in this.cache) { - pruneCacheEntry(this.cache, key, this.keys) - } - }, - - watch: { - include (val: string | RegExp | Array<string>) { - pruneCache(this, name => matches(val, name)) - }, - exclude (val: string | RegExp | Array<string>) { - pruneCache(this, name => !matches(val, name)) - } - }, - - render () { - const vnode: VNode = getFirstComponentChild(this.$slots.default) - const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions - if (componentOptions) { - // check pattern - const name: ?string = getComponentName(componentOptions) - if (name && ( - (this.exclude && matches(this.exclude, name)) || - (this.include && !matches(this.include, name)) - )) { - return vnode - } - - const { cache, keys } = this - const key: ?string = 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 { - cache[key] = vnode - keys.push(key) - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode) - } - } - - vnode.data.keepAlive = true - } - return vnode - } -} diff --git a/src/core/components/keep-alive.ts b/src/core/components/keep-alive.ts new file mode 100644 index 00000000000..b82152987e5 --- /dev/null +++ b/src/core/components/keep-alive.ts @@ -0,0 +1,171 @@ +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.js b/src/core/config.js deleted file mode 100644 index 18fc3d947ee..00000000000 --- a/src/core/config.js +++ /dev/null @@ -1,119 +0,0 @@ -/* @flow */ - -import { - no, - noop, - identity -} from 'shared/util' - -import { LIFECYCLE_HOOKS } from 'shared/constants' - -export type Config = { - // user - optionMergeStrategies: { [key: string]: Function }; - silent: boolean; - productionTip: boolean; - performance: boolean; - devtools: boolean; - errorHandler: ?(err: Error, vm: Component, info: string) => void; - warnHandler: ?(msg: string, vm: Component, trace: string) => void; - ignoredElements: Array<string | RegExp>; - keyCodes: { [key: string]: number | Array<number> }; - - // platform - isReservedTag: (x?: string) => boolean; - isReservedAttr: (x?: string) => boolean; - parsePlatformTagName: (x: string) => string; - isUnknownElement: (x?: string) => boolean; - getTagNamespace: (x?: string) => string | void; - mustUseProp: (tag: string, type: ?string, name: string) => boolean; - - // legacy - _lifecycleHooks: Array<string>; -}; - -export default ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * 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 - */ - 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, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}: Config) diff --git a/src/core/config.ts b/src/core/config.ts new file mode 100644 index 00000000000..97b17adcf45 --- /dev/null +++ b/src/core/config.ts @@ -0,0 +1,128 @@ +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.js b/src/core/global-api/assets.js deleted file mode 100644 index 2db49bfd3ed..00000000000 --- a/src/core/global-api/assets.js +++ /dev/null @@ -1,40 +0,0 @@ -/* @flow */ - -import config from '../config' -import { ASSET_TYPES } from 'shared/constants' -import { warn, isPlainObject } from '../util/index' - -export function initAssetRegisters (Vue: GlobalAPI) { - /** - * Create asset registration methods. - */ - ASSET_TYPES.forEach(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 (process.env.NODE_ENV !== 'production') { - if (type === 'component' && config.isReservedTag(id)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + id - ) - } - } - if (type === 'component' && isPlainObject(definition)) { - definition.name = definition.name || id - definition = this.options._base.extend(definition) - } - if (type === 'directive' && typeof definition === 'function') { - definition = { bind: definition, update: definition } - } - this.options[type + 's'][id] = definition - return definition - } - } - }) -} diff --git a/src/core/global-api/assets.ts b/src/core/global-api/assets.ts new file mode 100644 index 00000000000..c26f284d4a8 --- /dev/null +++ b/src/core/global-api/assets.ts @@ -0,0 +1,35 @@ +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.js b/src/core/global-api/extend.js deleted file mode 100644 index c8035a3d1a4..00000000000 --- a/src/core/global-api/extend.js +++ /dev/null @@ -1,101 +0,0 @@ -/* @flow */ - -import { ASSET_TYPES } from 'shared/constants' -import { warn, extend, mergeOptions } from '../util/index' -import { defineComputed, proxy } from '../instance/state' - -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: Object): Function { - extendOptions = extendOptions || {} - const Super = this - const SuperId = Super.cid - const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}) - if (cachedCtors[SuperId]) { - return cachedCtors[SuperId] - } - - const 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 characters and the hyphen, ' + - 'and must start with a letter.' - ) - } - } - - const Sub = function VueComponent (options) { - this._init(options) - } - 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) { - const props = Comp.options.props - for (const key in props) { - proxy(Comp.prototype, `_props`, key) - } -} - -function initComputed (Comp) { - const computed = Comp.options.computed - for (const key in computed) { - defineComputed(Comp.prototype, key, computed[key]) - } -} diff --git a/src/core/global-api/extend.ts b/src/core/global-api/extend.ts new file mode 100644 index 00000000000..800e8c2329e --- /dev/null +++ b/src/core/global-api/extend.ts @@ -0,0 +1,94 @@ +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.js b/src/core/global-api/index.js deleted file mode 100644 index 03ffa0fbf31..00000000000 --- a/src/core/global-api/index.js +++ /dev/null @@ -1,62 +0,0 @@ -/* @flow */ - -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 { - warn, - extend, - nextTick, - mergeOptions, - defineReactive -} from '../util/index' - -export function initGlobalAPI (Vue: GlobalAPI) { - // config - const configDef = {} - configDef.get = () => config - if (process.env.NODE_ENV !== 'production') { - 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 - - 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/index.ts b/src/core/global-api/index.ts new file mode 100644 index 00000000000..ca95ccf95c7 --- /dev/null +++ b/src/core/global-api/index.ts @@ -0,0 +1,68 @@ +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.js b/src/core/global-api/mixin.js deleted file mode 100644 index 36017a324a2..00000000000 --- a/src/core/global-api/mixin.js +++ /dev/null @@ -1,10 +0,0 @@ -/* @flow */ - -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/mixin.ts b/src/core/global-api/mixin.ts new file mode 100644 index 00000000000..7a4b00d97e9 --- /dev/null +++ b/src/core/global-api/mixin.ts @@ -0,0 +1,9 @@ +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.js b/src/core/global-api/use.js deleted file mode 100644 index a5b69988ddc..00000000000 --- a/src/core/global-api/use.js +++ /dev/null @@ -1,23 +0,0 @@ -/* @flow */ - -import { toArray } from '../util/index' - -export function initUse (Vue: GlobalAPI) { - Vue.use = function (plugin: Function | Object) { - const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) - if (installedPlugins.indexOf(plugin) > -1) { - return this - } - - // additional parameters - const args = toArray(arguments, 1) - args.unshift(this) - if (typeof plugin.install === 'function') { - plugin.install.apply(plugin, args) - } else if (typeof plugin === 'function') { - plugin.apply(null, args) - } - installedPlugins.push(plugin) - return this - } -} diff --git a/src/core/global-api/use.ts b/src/core/global-api/use.ts new file mode 100644 index 00000000000..6ee4d518af4 --- /dev/null +++ b/src/core/global-api/use.ts @@ -0,0 +1,23 @@ +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.js b/src/core/index.js deleted file mode 100644 index b2a0cb8c5a2..00000000000 --- a/src/core/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import Vue from './instance/index' -import { initGlobalAPI } from './global-api/index' -import { isServerRendering } from 'core/util/env' - -initGlobalAPI(Vue) - -Object.defineProperty(Vue.prototype, '$isServer', { - get: isServerRendering -}) - -Object.defineProperty(Vue.prototype, '$ssrContext', { - get () { - /* istanbul ignore next */ - return this.$vnode && this.$vnode.ssrContext - } -}) - -Vue.version = '__VERSION__' - -export default Vue diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 00000000000..6dc63950ccb --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,27 @@ +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.js b/src/core/instance/events.js deleted file mode 100644 index 587799969c2..00000000000 --- a/src/core/instance/events.js +++ /dev/null @@ -1,142 +0,0 @@ -/* @flow */ - -import { - tip, - toArray, - hyphenate, - handleError, - formatComponentName -} 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, once) { - if (once) { - target.$once(event, fn) - } else { - target.$on(event, fn) - } -} - -function remove (event, fn) { - target.$off(event, fn) -} - -export function updateComponentListeners ( - vm: Component, - listeners: Object, - oldListeners: ?Object -) { - target = vm - updateListeners(listeners, oldListeners || {}, add, remove, vm) - target = undefined -} - -export function eventsMixin (Vue: Class<Component>) { - const hookRE = /^hook:/ - Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { - const vm: Component = this - if (Array.isArray(event)) { - for (let i = 0, l = event.length; i < l; i++) { - this.$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 (Array.isArray(event)) { - for (let i = 0, l = event.length; i < l; i++) { - this.$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 - } - if (fn) { - // 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 (process.env.NODE_ENV !== 'production') { - 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) - for (let i = 0, l = cbs.length; i < l; i++) { - try { - cbs[i].apply(vm, args) - } catch (e) { - handleError(e, vm, `event handler for "${event}"`) - } - } - } - return vm - } -} diff --git a/src/core/instance/events.ts b/src/core/instance/events.ts new file mode 100644 index 00000000000..3e6d6ab1df8 --- /dev/null +++ b/src/core/instance/events.ts @@ -0,0 +1,160 @@ +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.js b/src/core/instance/index.js deleted file mode 100644 index 8a4402317f5..00000000000 --- a/src/core/instance/index.js +++ /dev/null @@ -1,23 +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' - -function Vue (options) { - if (process.env.NODE_ENV !== 'production' && - !(this instanceof Vue) - ) { - warn('Vue is a constructor and should be called with the `new` keyword') - } - this._init(options) -} - -initMixin(Vue) -stateMixin(Vue) -eventsMixin(Vue) -lifecycleMixin(Vue) -renderMixin(Vue) - -export default Vue diff --git a/src/core/instance/index.ts b/src/core/instance/index.ts new file mode 100644 index 00000000000..4e7aab2d571 --- /dev/null +++ b/src/core/instance/index.ts @@ -0,0 +1,27 @@ +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.js b/src/core/instance/init.js deleted file mode 100644 index f4b4db5742a..00000000000 --- a/src/core/instance/init.js +++ /dev/null @@ -1,146 +0,0 @@ -/* @flow */ - -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' - -let uid = 0 - -export function initMixin (Vue: Class<Component>) { - Vue.prototype._init = function (options?: Object) { - const vm: Component = this - // a uid - vm._uid = uid++ - - let startTag, endTag - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - startTag = `vue-perf-start:${vm._uid}` - endTag = `vue-perf-end:${vm._uid}` - mark(startTag) - } - - // a flag to avoid this being observed - vm._isVue = 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) - } else { - vm.$options = mergeOptions( - resolveConstructorOptions(vm.constructor), - options || {}, - vm - ) - } - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - initProxy(vm) - } else { - vm._renderProxy = vm - } - // expose real self - vm._self = vm - initLifecycle(vm) - initEvents(vm) - initRender(vm) - callHook(vm, 'beforeCreate') - initInjections(vm) // resolve injections before data/props - initState(vm) - initProvide(vm) // resolve provide after data/props - callHook(vm, 'created') - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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) - } - } -} - -function initInternalComponent (vm: Component, options: InternalComponentOptions) { - const opts = vm.$options = Object.create(vm.constructor.options) - // doing this because it's faster than dynamic enumeration. - opts.parent = options.parent - opts.propsData = options.propsData - opts._parentVnode = options._parentVnode - opts._parentListeners = options._parentListeners - opts._renderChildren = options._renderChildren - opts._componentTag = options._componentTag - opts._parentElm = options._parentElm - opts._refElm = options._refElm - if (options.render) { - opts.render = options.render - opts.staticRenderFns = options.staticRenderFns - } -} - -export function resolveConstructorOptions (Ctor: Class<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: Class<Component>): ?Object { - let modified - const latest = Ctor.options - const extended = Ctor.extendOptions - const sealed = Ctor.sealedOptions - for (const key in latest) { - if (latest[key] !== sealed[key]) { - if (!modified) modified = {} - modified[key] = dedupe(latest[key], extended[key], sealed[key]) - } - } - return modified -} - -function dedupe (latest, extended, sealed) { - // compare latest and sealed to ensure lifecycle hooks won't be duplicated - // between merges - if (Array.isArray(latest)) { - const res = [] - sealed = Array.isArray(sealed) ? sealed : [sealed] - extended = Array.isArray(extended) ? extended : [extended] - for (let i = 0; i < latest.length; i++) { - // push original options and not sealed options to exclude duplicated options - if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) { - res.push(latest[i]) - } - } - return res - } else { - return latest - } -} diff --git a/src/core/instance/init.ts b/src/core/instance/init.ts new file mode 100644 index 00000000000..c38eb9f9840 --- /dev/null +++ b/src/core/instance/init.ts @@ -0,0 +1,143 @@ +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.js b/src/core/instance/inject.js deleted file mode 100644 index 8bed0cd31b5..00000000000 --- a/src/core/instance/inject.js +++ /dev/null @@ -1,74 +0,0 @@ -/* @flow */ - -import { warn } from '../util/index' -import { hasSymbol } from 'core/util/env' -import { defineReactive, observerState } from '../observer/index' - -export function initProvide (vm: Component) { - const provide = vm.$options.provide - if (provide) { - vm._provided = typeof provide === 'function' - ? provide.call(vm) - : provide - } -} - -export function initInjections (vm: Component) { - const result = resolveInject(vm.$options.inject, vm) - if (result) { - observerState.shouldConvert = false - Object.keys(result).forEach(key => { - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - 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]) - } - }) - observerState.shouldConvert = true - } -} - -export function resolveInject (inject: any, vm: Component): ?Object { - 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).filter(key => { - /* istanbul ignore next */ - return Object.getOwnPropertyDescriptor(inject, key).enumerable - }) - : Object.keys(inject) - - for (let i = 0; i < keys.length; i++) { - const key = keys[i] - const provideKey = inject[key].from - let source = vm - while (source) { - if (source._provided && provideKey in source._provided) { - result[key] = source._provided[provideKey] - break - } - source = source.$parent - } - if (!source) { - if ('default' in inject[key]) { - const provideDefault = inject[key].default - result[key] = typeof provideDefault === 'function' - ? provideDefault.call(vm) - : provideDefault - } else if (process.env.NODE_ENV !== 'production') { - warn(`Injection "${key}" not found`, vm) - } - } - } - return result - } -} diff --git a/src/core/instance/inject.ts b/src/core/instance/inject.ts new file mode 100644 index 00000000000..d5fa2b5ba71 --- /dev/null +++ b/src/core/instance/inject.ts @@ -0,0 +1,80 @@ +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.js b/src/core/instance/lifecycle.js deleted file mode 100644 index db7bf286f11..00000000000 --- a/src/core/instance/lifecycle.js +++ /dev/null @@ -1,328 +0,0 @@ -/* @flow */ - -import config from '../config' -import Watcher from '../observer/watcher' -import { mark, measure } from '../util/perf' -import { createEmptyVNode } from '../vdom/vnode' -import { observerState } from '../observer/index' -import { updateComponentListeners } from './events' -import { resolveSlots } from './render-helpers/resolve-slots' - -import { - warn, - noop, - remove, - handleError, - emptyObject, - validateProp -} from '../util/index' - -export let activeInstance: any = null -export let isUpdatingChildComponent: boolean = false - -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._watcher = null - vm._inactive = null - vm._directInactive = false - vm._isMounted = false - vm._isDestroyed = false - vm._isBeingDestroyed = false -} - -export function lifecycleMixin (Vue: Class<Component>) { - Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) { - const vm: Component = this - if (vm._isMounted) { - callHook(vm, 'beforeUpdate') - } - const prevEl = vm.$el - const prevVnode = vm._vnode - const prevActiveInstance = activeInstance - activeInstance = 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 */, - vm.$options._parentElm, - vm.$options._refElm - ) - // no need for the ref nodes after initial patch - // this prevents keeping a detached DOM tree in memory (#5851) - vm.$options._parentElm = vm.$options._refElm = null - } else { - // updates - vm.$el = vm.__patch__(prevVnode, vnode) - } - activeInstance = prevActiveInstance - // 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 - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el - } - // 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 watchers - if (vm._watcher) { - vm._watcher.teardown() - } - let i = vm._watchers.length - while (i--) { - vm._watchers[i].teardown() - } - // 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, - hydrating?: boolean -): Component { - vm.$el = el - if (!vm.$options.render) { - vm.$options.render = createEmptyVNode - if (process.env.NODE_ENV !== 'production') { - /* 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 (process.env.NODE_ENV !== 'production' && 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) - } - } - - vm._watcher = new Watcher(vm, updateComponent, noop) - hydrating = false - - // 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: ?Object, - listeners: ?Object, - parentVnode: VNode, - renderChildren: ?Array<VNode> -) { - if (process.env.NODE_ENV !== 'production') { - isUpdatingChildComponent = true - } - - // determine whether component has slot children - // we need to do this before overwriting $options._renderChildren - const hasChildren = !!( - renderChildren || // has new static slots - vm.$options._renderChildren || // has old static slots - parentVnode.data.scopedSlots || // has new scoped slots - vm.$scopedSlots !== emptyObject // has old scoped slots - ) - - 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 - vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject - vm.$listeners = listeners || emptyObject - - // update props - if (propsData && vm.$options.props) { - observerState.shouldConvert = false - const props = vm._props - const propKeys = vm.$options._propKeys || [] - for (let i = 0; i < propKeys.length; i++) { - const key = propKeys[i] - props[key] = validateProp(key, vm.$options.props, propsData, vm) - } - observerState.shouldConvert = true - // keep a copy of raw propsData - vm.$options.propsData = propsData - } - - // update listeners - if (listeners) { - const oldListeners = vm.$options._parentListeners - vm.$options._parentListeners = listeners - updateComponentListeners(vm, listeners, oldListeners) - } - // resolve slots + force update if has children - if (hasChildren) { - vm.$slots = resolveSlots(renderChildren, parentVnode.context) - vm.$forceUpdate() - } - - if (process.env.NODE_ENV !== 'production') { - 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) { - const handlers = vm.$options[hook] - if (handlers) { - for (let i = 0, j = handlers.length; i < j; i++) { - try { - handlers[i].call(vm) - } catch (e) { - handleError(e, vm, `${hook} hook`) - } - } - } - if (vm._hasHookEvent) { - vm.$emit('hook:' + hook) - } -} diff --git a/src/core/instance/lifecycle.ts b/src/core/instance/lifecycle.ts new file mode 100644 index 00000000000..d1b5a76d990 --- /dev/null +++ b/src/core/instance/lifecycle.ts @@ -0,0 +1,421 @@ +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.js b/src/core/instance/proxy.js deleted file mode 100644 index 7454712f994..00000000000 --- a/src/core/instance/proxy.js +++ /dev/null @@ -1,80 +0,0 @@ -/* not type checking this file because flow doesn't play well with Proxy */ - -import config from 'core/config' -import { warn, makeMap } from '../util/index' - -let initProxy - -if (process.env.NODE_ENV !== 'production') { - 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,' + - '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://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ) - } - - const hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/) - - if (hasProxy) { - const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact') - config.keyCodes = new Proxy(config.keyCodes, { - set (target, key, 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) || key.charAt(0) === '_' - if (!has && !isAllowed) { - warnNonPresent(target, key) - } - return has || !isAllowed - } - } - - const getHandler = { - get (target, key) { - if (typeof key === 'string' && !(key in target)) { - 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/proxy.ts b/src/core/instance/proxy.ts new file mode 100644 index 00000000000..685d9651fcc --- /dev/null +++ b/src/core/instance/proxy.ts @@ -0,0 +1,97 @@ +/* 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 new file mode 100644 index 00000000000..a1fefa65816 --- /dev/null +++ b/src/core/instance/render-helpers/bind-dynamic-keys.ts @@ -0,0 +1,36 @@ +// 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.js b/src/core/instance/render-helpers/bind-object-listeners.js deleted file mode 100644 index 5bfc7514323..00000000000 --- a/src/core/instance/render-helpers/bind-object-listeners.js +++ /dev/null @@ -1,22 +0,0 @@ -/* @flow */ - -import { warn, extend, isPlainObject } from 'core/util/index' - -export function bindObjectListeners (data: any, value: any): VNodeData { - if (value) { - if (!isPlainObject(value)) { - process.env.NODE_ENV !== 'production' && 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-listeners.ts b/src/core/instance/render-helpers/bind-object-listeners.ts new file mode 100644 index 00000000000..0cdd8cafcf9 --- /dev/null +++ b/src/core/instance/render-helpers/bind-object-listeners.ts @@ -0,0 +1,18 @@ +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.js b/src/core/instance/render-helpers/bind-object-props.js deleted file mode 100644 index c71dce5c4c2..00000000000 --- a/src/core/instance/render-helpers/bind-object-props.js +++ /dev/null @@ -1,60 +0,0 @@ -/* @flow */ - -import config from 'core/config' - -import { - warn, - isObject, - toObject, - isReservedAttribute -} from 'core/util/index' - -/** - * 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)) { - process.env.NODE_ENV !== 'production' && warn( - 'v-bind without argument expects an Object or Array value', - this - ) - } else { - if (Array.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 = {}) - } - if (!(key 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/bind-object-props.ts b/src/core/instance/render-helpers/bind-object-props.ts new file mode 100644 index 00000000000..d9009a45dce --- /dev/null +++ b/src/core/instance/render-helpers/bind-object-props.ts @@ -0,0 +1,59 @@ +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.js b/src/core/instance/render-helpers/check-keycodes.js deleted file mode 100644 index 8e77f29b9e4..00000000000 --- a/src/core/instance/render-helpers/check-keycodes.js +++ /dev/null @@ -1,27 +0,0 @@ -/* @flow */ - -import config from 'core/config' -import { hyphenate } from 'shared/util' - -/** - * 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, - builtInAlias?: number | Array<number>, - eventKeyName?: string -): ?boolean { - const keyCodes = config.keyCodes[key] || builtInAlias - if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } - } else if (eventKeyName) { - return hyphenate(eventKeyName) !== key - } -} diff --git a/src/core/instance/render-helpers/check-keycodes.ts b/src/core/instance/render-helpers/check-keycodes.ts new file mode 100644 index 00000000000..482051ffd19 --- /dev/null +++ b/src/core/instance/render-helpers/check-keycodes.ts @@ -0,0 +1,33 @@ +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.js b/src/core/instance/render-helpers/index.js deleted file mode 100644 index 38414a9ab53..00000000000 --- a/src/core/instance/render-helpers/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* @flow */ - -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-slots' - -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 -} diff --git a/src/core/instance/render-helpers/index.ts b/src/core/instance/render-helpers/index.ts new file mode 100644 index 00000000000..27ea60e9c8e --- /dev/null +++ b/src/core/instance/render-helpers/index.ts @@ -0,0 +1,31 @@ +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.js b/src/core/instance/render-helpers/render-list.js deleted file mode 100644 index abe99db4d6d..00000000000 --- a/src/core/instance/render-helpers/render-list.js +++ /dev/null @@ -1,39 +0,0 @@ -/* @flow */ - -import { isObject, isDef } from 'core/util/index' - -/** - * Runtime helper for rendering v-for lists. - */ -export function renderList ( - val: any, - render: ( - val: any, - keyOrIndex: string | number, - index?: number - ) => VNode -): ?Array<VNode> { - let ret: ?Array<VNode>, i, l, keys, key - if (Array.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)) { - 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: any)._isVList = true - } - return ret -} diff --git a/src/core/instance/render-helpers/render-list.ts b/src/core/instance/render-helpers/render-list.ts new file mode 100644 index 00000000000..ba9318d3ded --- /dev/null +++ b/src/core/instance/render-helpers/render-list.ts @@ -0,0 +1,49 @@ +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.js b/src/core/instance/render-helpers/render-slot.js deleted file mode 100644 index a58daa74e62..00000000000 --- a/src/core/instance/render-helpers/render-slot.js +++ /dev/null @@ -1,50 +0,0 @@ -/* @flow */ - -import { extend, warn, isObject } from 'core/util/index' - -/** - * Runtime helper for rendering <slot> - */ -export function renderSlot ( - name: string, - fallback: ?Array<VNode>, - props: ?Object, - bindObject: ?Object -): ?Array<VNode> { - const scopedSlotFn = this.$scopedSlots[name] - let nodes - if (scopedSlotFn) { // scoped slot - props = props || {} - if (bindObject) { - if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ) - } - props = extend(extend({}, bindObject), props) - } - nodes = scopedSlotFn(props) || fallback - } else { - const slotNodes = this.$slots[name] - // warn duplicate slot usage - if (slotNodes) { - if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { - warn( - `Duplicate presence of slot "${name}" found in the same render tree ` + - `- this will likely cause render errors.`, - this - ) - } - slotNodes._rendered = true - } - nodes = slotNodes || fallback - } - - 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-slot.ts b/src/core/instance/render-helpers/render-slot.ts new file mode 100644 index 00000000000..501143ee3f5 --- /dev/null +++ b/src/core/instance/render-helpers/render-slot.ts @@ -0,0 +1,39 @@ +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.js b/src/core/instance/render-helpers/render-static.js deleted file mode 100644 index 1236c94792c..00000000000 --- a/src/core/instance/render-helpers/render-static.js +++ /dev/null @@ -1,63 +0,0 @@ -/* @flow */ - -import { cloneVNode, cloneVNodes } from 'core/vdom/vnode' - -/** - * Runtime helper for rendering static trees. - */ -export function renderStatic ( - index: number, - isInFor?: boolean -): VNode | Array<VNode> { - // static trees can be rendered once and cached on the contructor options - // so every instance shares the same cached trees - const options = this.$options - const cached = options.cached || (options.cached = []) - let tree = cached[index] - // if has already-rendered static tree and not inside v-for, - // we can reuse the same tree by doing a shallow clone. - if (tree && !isInFor) { - return Array.isArray(tree) - ? cloneVNodes(tree) - : cloneVNode(tree) - } - // otherwise, render a fresh tree. - tree = cached[index] = options.staticRenderFns[index].call(this._renderProxy, null, this) - 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 (Array.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/render-static.ts b/src/core/instance/render-helpers/render-static.ts new file mode 100644 index 00000000000..9de7ab7ec70 --- /dev/null +++ b/src/core/instance/render-helpers/render-static.ts @@ -0,0 +1,57 @@ +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.js b/src/core/instance/render-helpers/resolve-filter.js deleted file mode 100644 index 08a81e1495d..00000000000 --- a/src/core/instance/render-helpers/resolve-filter.js +++ /dev/null @@ -1,10 +0,0 @@ -/* @flow */ - -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-filter.ts b/src/core/instance/render-helpers/resolve-filter.ts new file mode 100644 index 00000000000..c9de5ce9c60 --- /dev/null +++ b/src/core/instance/render-helpers/resolve-filter.ts @@ -0,0 +1,8 @@ +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 new file mode 100644 index 00000000000..6a25d084a36 --- /dev/null +++ b/src/core/instance/render-helpers/resolve-scoped-slots.ts @@ -0,0 +1,30 @@ +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.js b/src/core/instance/render-helpers/resolve-slots.js deleted file mode 100644 index 629177e4771..00000000000 --- a/src/core/instance/render-helpers/resolve-slots.js +++ /dev/null @@ -1,63 +0,0 @@ -/* @flow */ - -/** - * Runtime helper for resolving raw children VNodes into a slot object. - */ -export function resolveSlots ( - children: ?Array<VNode>, - context: ?Component -): { [key: string]: Array<VNode> } { - const slots = {} - if (!children) { - return slots - } - 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.functionalContext === context) && - data && data.slot != null - ) { - const name = child.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.text === ' ' -} - -export function resolveScopedSlots ( - fns: ScopedSlotsData, // see flow/vnode - res?: Object -): { [key: string]: Function } { - res = res || {} - for (let i = 0; i < fns.length; i++) { - if (Array.isArray(fns[i])) { - resolveScopedSlots(fns[i], res) - } else { - res[fns[i].key] = fns[i].fn - } - } - return res -} diff --git a/src/core/instance/render-helpers/resolve-slots.ts b/src/core/instance/render-helpers/resolve-slots.ts new file mode 100644 index 00000000000..5536f919145 --- /dev/null +++ b/src/core/instance/render-helpers/resolve-slots.ts @@ -0,0 +1,51 @@ +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.js b/src/core/instance/render.js deleted file mode 100644 index 5b7df25c8dd..00000000000 --- a/src/core/instance/render.js +++ /dev/null @@ -1,119 +0,0 @@ -/* @flow */ - -import { - warn, - nextTick, - emptyObject, - handleError, - defineReactive -} from '../util/index' - -import { createElement } from '../vdom/create-element' -import { installRenderHelpers } from './render-helpers/index' -import { resolveSlots } from './render-helpers/resolve-slots' -import VNode, { cloneVNodes, createEmptyVNode } from '../vdom/vnode' - -import { isUpdatingChildComponent } from './lifecycle' - -export function initRender (vm: Component) { - vm._vnode = null // the root of the child tree - const options = vm.$options - const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree - const renderContext = parentVnode && parentVnode.context - vm.$slots = resolveSlots(options._renderChildren, renderContext) - vm.$scopedSlots = 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 - 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. - 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 (process.env.NODE_ENV !== 'production') { - 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 function renderMixin (Vue: Class<Component>) { - // install runtime convenience helpers - installRenderHelpers(Vue.prototype) - - Vue.prototype.$nextTick = function (fn: Function) { - return nextTick(fn, this) - } - - Vue.prototype._render = function (): VNode { - const vm: Component = this - const { render, _parentVnode } = vm.$options - - if (vm._isMounted) { - // if the parent didn't update, the slot nodes will be the ones from - // last render. They need to be cloned to ensure "freshness" for this render. - for (const key in vm.$slots) { - const slot = vm.$slots[key] - if (slot._rendered) { - vm.$slots[key] = cloneVNodes(slot, true /* deep */) - } - } - } - - vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject - - // set parent vnode. this allows render functions to have access - // to the data on the placeholder node. - vm.$vnode = _parentVnode - // render self - let vnode - try { - vnode = render.call(vm._renderProxy, vm.$createElement) - } catch (e) { - handleError(e, vm, `render`) - // return error render result, - // or previous vnode to prevent render error causing blank component - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - if (vm.$options.renderError) { - try { - vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e) - } catch (e) { - handleError(e, vm, `renderError`) - vnode = vm._vnode - } - } else { - vnode = vm._vnode - } - } else { - vnode = vm._vnode - } - } - // return empty vnode in case the render function errored out - if (!(vnode instanceof VNode)) { - if (process.env.NODE_ENV !== 'production' && Array.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/render.ts b/src/core/instance/render.ts new file mode 100644 index 00000000000..6553f217480 --- /dev/null +++ b/src/core/instance/render.ts @@ -0,0 +1,172 @@ +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.js b/src/core/instance/state.js deleted file mode 100644 index 4fa6f15bac8..00000000000 --- a/src/core/instance/state.js +++ /dev/null @@ -1,353 +0,0 @@ -/* @flow */ - -import config from '../config' -import Dep from '../observer/dep' -import Watcher from '../observer/watcher' -import { isUpdatingChildComponent } from './lifecycle' - -import { - set, - del, - observe, - observerState, - defineReactive -} from '../observer/index' - -import { - warn, - bind, - noop, - hasOwn, - hyphenate, - isReserved, - handleError, - nativeWatch, - validateProp, - isPlainObject, - isServerRendering, - isReservedAttribute -} from '../util/index' - -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) { - vm._watchers = [] - const opts = vm.$options - if (opts.props) initProps(vm, opts.props) - if (opts.methods) initMethods(vm, opts.methods) - if (opts.data) { - initData(vm) - } else { - observe(vm._data = {}, true /* asRootData */) - } - 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 = {} - // cache prop keys so that future props updates can iterate using Array - // instead of dynamic object key enumeration. - const keys = vm.$options._propKeys = [] - const isRoot = !vm.$parent - // root instance props should be converted - observerState.shouldConvert = isRoot - for (const key in propsOptions) { - keys.push(key) - const value = validateProp(key, propsOptions, propsData, vm) - /* istanbul ignore else */ - if (process.env.NODE_ENV !== 'production') { - 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 (vm.$parent && !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 - ) - } - }) - } else { - defineReactive(props, key, value) - } - // 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) - } - } - observerState.shouldConvert = true -} - -function initData (vm: Component) { - let data = vm.$options.data - data = vm._data = typeof data === 'function' - ? getData(data, vm) - : data || {} - if (!isPlainObject(data)) { - data = {} - process.env.NODE_ENV !== 'production' && warn( - 'data functions should return an object:\n' + - 'https://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 (process.env.NODE_ENV !== 'production') { - if (methods && hasOwn(methods, key)) { - warn( - `Method "${key}" has already been defined as a data property.`, - vm - ) - } - } - if (props && hasOwn(props, key)) { - process.env.NODE_ENV !== 'production' && 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 - observe(data, true /* asRootData */) -} - -function getData (data: Function, vm: Component): any { - try { - return data.call(vm, vm) - } catch (e) { - handleError(e, vm, `data()`) - return {} - } -} - -const computedWatcherOptions = { lazy: true } - -function initComputed (vm: Component, computed: Object) { - 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 = typeof userDef === 'function' ? userDef : userDef.get - if (process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production') { - 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) - } - } - } -} - -export function defineComputed ( - target: any, - key: string, - userDef: Object | Function -) { - const shouldCache = !isServerRendering() - if (typeof userDef === 'function') { - sharedPropertyDefinition.get = shouldCache - ? createComputedGetter(key) - : userDef - sharedPropertyDefinition.set = noop - } else { - sharedPropertyDefinition.get = userDef.get - ? shouldCache && userDef.cache !== false - ? createComputedGetter(key) - : userDef.get - : noop - sharedPropertyDefinition.set = userDef.set - ? userDef.set - : noop - } - if (process.env.NODE_ENV !== 'production' && - 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) { - watcher.depend() - } - return watcher.value - } - } -} - -function initMethods (vm: Component, methods: Object) { - const props = vm.$options.props - for (const key in methods) { - if (process.env.NODE_ENV !== 'production') { - if (methods[key] == null) { - warn( - `Method "${key}" has an undefined value 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] = methods[key] == null ? noop : bind(methods[key], vm) - } -} - -function initWatch (vm: Component, watch: Object) { - for (const key in watch) { - const handler = watch[key] - if (Array.isArray(handler)) { - for (let i = 0; i < handler.length; i++) { - createWatcher(vm, key, handler[i]) - } - } else { - createWatcher(vm, key, handler) - } - } -} - -function createWatcher ( - vm: Component, - keyOrFn: string | Function, - handler: any, - options?: Object -) { - if (isPlainObject(handler)) { - options = handler - handler = handler.handler - } - if (typeof handler === 'string') { - handler = vm[handler] - } - return vm.$watch(keyOrFn, handler, options) -} - -export function stateMixin (Vue: Class<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 = {} - dataDef.get = function () { return this._data } - const propsDef = {} - propsDef.get = function () { return this._props } - if (process.env.NODE_ENV !== 'production') { - dataDef.set = function (newData: Object) { - 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 | Function, - cb: any, - options?: Object - ): 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) { - cb.call(vm, watcher.value) - } - return function unwatchFn () { - watcher.teardown() - } - } -} diff --git a/src/core/instance/state.ts b/src/core/instance/state.ts new file mode 100644 index 00000000000..f59dda13dd2 --- /dev/null +++ b/src/core/instance/state.ts @@ -0,0 +1,393 @@ +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.js b/src/core/observer/array.js deleted file mode 100644 index 1d22870a9c6..00000000000 --- a/src/core/observer/array.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -import { def } 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 - 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 - ob.dep.notify() - return result - }) -}) diff --git a/src/core/observer/array.ts b/src/core/observer/array.ts new file mode 100644 index 00000000000..b2ff2385555 --- /dev/null +++ b/src/core/observer/array.ts @@ -0,0 +1,54 @@ +/* + * 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.js b/src/core/observer/dep.js deleted file mode 100644 index 5d55e9cd421..00000000000 --- a/src/core/observer/dep.js +++ /dev/null @@ -1,58 +0,0 @@ -/* @flow */ - -import type Watcher from './watcher' -import { remove } from '../util/index' - -let uid = 0 - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -export default class Dep { - static target: ?Watcher; - id: number; - subs: Array<Watcher>; - - constructor () { - this.id = uid++ - this.subs = [] - } - - addSub (sub: Watcher) { - this.subs.push(sub) - } - - removeSub (sub: Watcher) { - remove(this.subs, sub) - } - - depend () { - if (Dep.target) { - Dep.target.addDep(this) - } - } - - notify () { - // stabilize the subscriber list first - const subs = this.subs.slice() - for (let i = 0, l = subs.length; i < l; i++) { - subs[i].update() - } - } -} - -// 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 -const targetStack = [] - -export function pushTarget (_target: Watcher) { - if (Dep.target) targetStack.push(Dep.target) - Dep.target = _target -} - -export function popTarget () { - Dep.target = targetStack.pop() -} diff --git a/src/core/observer/dep.ts b/src/core/observer/dep.ts new file mode 100644 index 00000000000..205efbbf5a0 --- /dev/null +++ b/src/core/observer/dep.ts @@ -0,0 +1,108 @@ +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.js b/src/core/observer/index.js deleted file mode 100644 index 86a21f9e4f2..00000000000 --- a/src/core/observer/index.js +++ /dev/null @@ -1,258 +0,0 @@ -/* @flow */ - -import Dep from './dep' -import VNode from '../vdom/vnode' -import { arrayMethods } from './array' -import { - def, - warn, - hasOwn, - hasProto, - isObject, - isPlainObject, - isValidArrayIndex, - isServerRendering -} 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 when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -export const observerState = { - 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. - */ -export class Observer { - value: any; - dep: Dep; - vmCount: number; // number of vms that has this object as root $data - - constructor (value: any) { - this.value = value - this.dep = new Dep() - this.vmCount = 0 - def(value, '__ob__', this) - if (Array.isArray(value)) { - const augment = hasProto - ? protoAugment - : copyAugment - augment(value, arrayMethods, arrayKeys) - this.observeArray(value) - } else { - this.walk(value) - } - } - - /** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ - walk (obj: Object) { - const keys = Object.keys(obj) - for (let i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]) - } - } - - /** - * Observe a list of Array items. - */ - observeArray (items: Array<any>) { - for (let i = 0, l = items.length; i < l; i++) { - observe(items[i]) - } - } -} - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src: Object, keys: any) { - /* eslint-disable no-proto */ - target.__proto__ = src - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -function copyAugment (target: Object, src: Object, keys: Array<string>) { - for (let i = 0, l = keys.length; i < l; i++) { - const 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. - */ -export function observe (value: any, asRootData: ?boolean): Observer | void { - if (!isObject(value) || value instanceof VNode) { - return - } - let ob: Observer | void - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__ - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value) - } - if (asRootData && ob) { - ob.vmCount++ - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -export function defineReactive ( - obj: Object, - key: string, - val: any, - customSetter?: ?Function, - shallow?: boolean -) { - 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 - - let childOb = !shallow && observe(val) - Object.defineProperty(obj, key, { - enumerable: true, - configurable: true, - get: function reactiveGetter () { - const value = getter ? getter.call(obj) : val - if (Dep.target) { - dep.depend() - if (childOb) { - childOb.dep.depend() - if (Array.isArray(value)) { - dependArray(value) - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - const value = getter ? getter.call(obj) : val - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter() - } - if (setter) { - setter.call(obj, newVal) - } else { - val = newVal - } - childOb = !shallow && observe(newVal) - dep.notify() - } - }) -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -export function set (target: Array<any> | Object, key: any, val: any): any { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key) - target.splice(key, 1, val) - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val - return val - } - const ob = (target: any).__ob__ - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && 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) - ob.dep.notify() - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -export function del (target: Array<any> | Object, key: any) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1) - return - } - const ob = (target: any).__ob__ - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ) - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key] - if (!ob) { - return - } - 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] - e && e.__ob__ && e.__ob__.dep.depend() - if (Array.isArray(e)) { - dependArray(e) - } - } -} diff --git a/src/core/observer/index.ts b/src/core/observer/index.ts new file mode 100644 index 00000000000..dc6efae4d1b --- /dev/null +++ b/src/core/observer/index.ts @@ -0,0 +1,339 @@ +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.js b/src/core/observer/scheduler.js deleted file mode 100644 index fce86e5f40b..00000000000 --- a/src/core/observer/scheduler.js +++ /dev/null @@ -1,148 +0,0 @@ -/* @flow */ - -import type Watcher from './watcher' -import config from '../config' -import { callHook, activateChildComponent } from '../instance/lifecycle' - -import { - warn, - nextTick, - devtools -} from '../util/index' - -export const MAX_UPDATE_COUNT = 100 - -const queue: Array<Watcher> = [] -const activatedChildren: Array<Component> = [] -let has: { [key: number]: ?true } = {} -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 (process.env.NODE_ENV !== 'production') { - circular = {} - } - waiting = flushing = false -} - -/** - * Flush both queues and run the watchers. - */ -function flushSchedulerQueue () { - 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((a, b) => a.id - b.id) - - // 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] - 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] > 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) - - // devtool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush') - } -} - -function callUpdatedHooks (queue) { - let i = queue.length - while (i--) { - const watcher = queue[i] - const vm = watcher.vm - if (vm._watcher === watcher && vm._isMounted) { - 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) { - 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 - nextTick(flushSchedulerQueue) - } - } -} diff --git a/src/core/observer/scheduler.ts b/src/core/observer/scheduler.ts new file mode 100644 index 00000000000..8fc82b48cf4 --- /dev/null +++ b/src/core/observer/scheduler.ts @@ -0,0 +1,199 @@ +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 new file mode 100644 index 00000000000..c681e0c9285 --- /dev/null +++ b/src/core/observer/traverse.ts @@ -0,0 +1,47 @@ +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.js b/src/core/observer/watcher.js deleted file mode 100644 index 40253149c34..00000000000 --- a/src/core/observer/watcher.js +++ /dev/null @@ -1,269 +0,0 @@ -/* @flow */ - -import { queueWatcher } from './scheduler' -import Dep, { pushTarget, popTarget } from './dep' - -import { - warn, - remove, - isObject, - parsePath, - _Set as Set, - handleError -} from '../util/index' - -import type { ISet } 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. - */ -export default class Watcher { - vm: Component; - 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: ISet; - newDepIds: ISet; - getter: Function; - value: any; - - constructor ( - vm: Component, - expOrFn: string | Function, - cb: Function, - options?: Object - ) { - this.vm = vm - vm._watchers.push(this) - // options - if (options) { - this.deep = !!options.deep - this.user = !!options.user - this.lazy = !!options.lazy - this.sync = !!options.sync - } else { - this.deep = this.user = this.lazy = this.sync = false - } - 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.expression = process.env.NODE_ENV !== 'production' - ? expOrFn.toString() - : '' - // parse expression for getter - if (typeof expOrFn === 'function') { - this.getter = expOrFn - } else { - this.getter = parsePath(expOrFn) - if (!this.getter) { - this.getter = function () {} - process.env.NODE_ENV !== 'production' && 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) { - 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 = 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) { - try { - this.cb.call(this.vm, value, oldValue) - } catch (e) { - handleError(e, this.vm, `callback for watcher "${this.expression}"`) - } - } 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.active) { - // remove self from vm's watcher list - // this is a somewhat expensive operation so we skip it - // if the vm is being destroyed. - if (!this.vm._isBeingDestroyed) { - remove(this.vm._watchers, this) - } - let i = this.deps.length - while (i--) { - this.deps[i].removeSub(this) - } - this.active = false - } - } -} - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -const seenObjects = new Set() -function traverse (val: any) { - seenObjects.clear() - _traverse(val, seenObjects) -} - -function _traverse (val: any, seen: ISet) { - let i, keys - const isA = Array.isArray(val) - if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { - 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 { - 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 new file mode 100644 index 00000000000..b2989b53772 --- /dev/null +++ b/src/core/observer/watcher.ts @@ -0,0 +1,278 @@ +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.js b/src/core/util/debug.js deleted file mode 100644 index 6b7315f4b20..00000000000 --- a/src/core/util/debug.js +++ /dev/null @@ -1,100 +0,0 @@ -/* @flow */ - -import config from '../config' -import { noop } from 'shared/util' - -export let warn = noop -export let tip = noop -export let generateComponentTrace = (noop: any) // work around flow check -export let formatComponentName = (noop: any) - -if (process.env.NODE_ENV !== 'production') { - const hasConsole = typeof console !== 'undefined' - const classifyRE = /(?:^|[-_])(\w)/g - const classify = str => str - .replace(classifyRE, c => c.toUpperCase()) - .replace(/[-_]/g, '') - - warn = (msg, vm) => { - 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 = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {} - let name = options.name || options._componentTag - 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 => { - if (vm._isVue && vm.$parent) { - const tree = [] - 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) - }${ - Array.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/debug.ts b/src/core/util/debug.ts new file mode 100644 index 00000000000..891f0177c68 --- /dev/null +++ b/src/core/util/debug.ts @@ -0,0 +1,105 @@ +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.js b/src/core/util/env.js deleted file mode 100644 index 30767958657..00000000000 --- a/src/core/util/env.js +++ /dev/null @@ -1,93 +0,0 @@ -/* @flow */ - -// 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 - -// Firefox has a "watch" function on Object.prototype... -export const nativeWatch = ({}).watch - -export let supportsPassive = false -if (inBrowser) { - try { - const opts = {} - Object.defineProperty(opts, 'passive', ({ - get () { - /* istanbul ignore next */ - supportsPassive = true - } - }: Object)) // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts) - } catch (e) {} -} - -// 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'].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 -/* istanbul ignore if */ // $flow-disable-line -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 ISet { - set: Object; - constructor () { - this.set = 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) - } - } -} - -interface ISet { - has(key: string | number): boolean; - add(key: string | number): mixed; - clear(): void; -} - -export { _Set } -export type { ISet } diff --git a/src/core/util/env.ts b/src/core/util/env.ts new file mode 100644 index 00000000000..cf64f50b7a2 --- /dev/null +++ b/src/core/util/env.ts @@ -0,0 +1,93 @@ +// 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.js b/src/core/util/error.js deleted file mode 100644 index abf5b6a5aa1..00000000000 --- a/src/core/util/error.js +++ /dev/null @@ -1,48 +0,0 @@ -/* @flow */ - -import config from '../config' -import { warn } from './debug' -import { inBrowser } from './env' - -export function handleError (err: Error, vm: any, info: string) { - 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) { - globalHandleError(e, cur, 'errorCaptured hook') - } - } - } - } - } - globalHandleError(err, vm, info) -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler') - } - } - logError(err, vm, info) -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - 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/error.ts b/src/core/util/error.ts new file mode 100644 index 00000000000..d97f847fd21 --- /dev/null +++ b/src/core/util/error.ts @@ -0,0 +1,81 @@ +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/index.js b/src/core/util/index.js deleted file mode 100644 index 115d77cd175..00000000000 --- a/src/core/util/index.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -export * from 'shared/util' -export * from './lang' -export * from './env' -export * from './options' -export * from './debug' -export * from './props' -export * from './error' -export * from './next-tick' -export { defineReactive } from '../observer/index' diff --git a/src/core/util/index.ts b/src/core/util/index.ts new file mode 100644 index 00000000000..c47b8dbe937 --- /dev/null +++ b/src/core/util/index.ts @@ -0,0 +1,9 @@ +export * from 'shared/util' +export * from './lang' +export * from './env' +export * from './options' +export * from './debug' +export * from './props' +export * from './error' +export * from './next-tick' +export { defineReactive } from '../observer/index' diff --git a/src/core/util/lang.js b/src/core/util/lang.js deleted file mode 100644 index 3be32d1076b..00000000000 --- a/src/core/util/lang.js +++ /dev/null @@ -1,41 +0,0 @@ -/* @flow */ - -export const emptyObject = Object.freeze({}) - -/** - * 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 = /[^\w.$]/ -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/lang.ts b/src/core/util/lang.ts new file mode 100644 index 00000000000..1d332f66027 --- /dev/null +++ b/src/core/util/lang.ts @@ -0,0 +1,45 @@ +/** + * 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.js b/src/core/util/next-tick.js deleted file mode 100644 index 9e4f476d65b..00000000000 --- a/src/core/util/next-tick.js +++ /dev/null @@ -1,117 +0,0 @@ -/* @flow */ -/* globals MessageChannel */ - -import { noop } from 'shared/util' -import { handleError } from './error' -import { isIOS, isNative } from './env' - -const callbacks = [] -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 both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -let microTimerFunc -let macroTimerFunc -let useMacroTask = false - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = () => { - setImmediate(flushCallbacks) - } -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - const channel = new MessageChannel() - const port = channel.port2 - channel.port1.onmessage = flushCallbacks - macroTimerFunc = () => { - port.postMessage(1) - } -} else { - /* istanbul ignore next */ - macroTimerFunc = () => { - setTimeout(flushCallbacks, 0) - } -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - const p = Promise.resolve() - microTimerFunc = () => { - 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) - } -} else { - // fallback to macro - microTimerFunc = macroTimerFunc -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -export function withMacroTask (fn: Function): Function { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true - const res = fn.apply(null, arguments) - useMacroTask = false - return res - }) -} - -export function nextTick (cb?: Function, ctx?: Object) { - let _resolve - callbacks.push(() => { - if (cb) { - try { - cb.call(ctx) - } catch (e) { - handleError(e, ctx, 'nextTick') - } - } else if (_resolve) { - _resolve(ctx) - } - }) - if (!pending) { - pending = true - if (useMacroTask) { - macroTimerFunc() - } else { - microTimerFunc() - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(resolve => { - _resolve = resolve - }) - } -} diff --git a/src/core/util/next-tick.ts b/src/core/util/next-tick.ts new file mode 100644 index 00000000000..5e67bd7d2e7 --- /dev/null +++ b/src/core/util/next-tick.ts @@ -0,0 +1,117 @@ +/* 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.js b/src/core/util/options.js deleted file mode 100644 index ad36bb94151..00000000000 --- a/src/core/util/options.js +++ /dev/null @@ -1,427 +0,0 @@ -/* @flow */ - -import config from '../config' -import { warn } from './debug' -import { nativeWatch } from './env' -import { set } from '../observer/index' - -import { - ASSET_TYPES, - LIFECYCLE_HOOKS -} from 'shared/constants' - -import { - extend, - hasOwn, - camelize, - toRawType, - capitalize, - isBuiltInTag, - isPlainObject -} from 'shared/util' - -/** - * 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 (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - 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: Object, from: ?Object): Object { - if (!from) return to - let key, toVal, fromVal - const keys = Object.keys(from) - for (let i = 0; i < keys.length; i++) { - key = keys[i] - toVal = to[key] - fromVal = from[key] - if (!hasOwn(to, key)) { - set(to, key, fromVal) - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal) - } - } - return to -} - -/** - * Data - */ -export function mergeDataOrFn ( - parentVal: any, - childVal: any, - vm?: Component -): ?Function { - 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( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - const instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal - const defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal: any, - childVal: any, - vm?: Component -): ?Function { - if (!vm) { - if (childVal && 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 - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -} - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal: ?Array<Function>, - childVal: ?Function | ?Array<Function> -): ?Array<Function> { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(hook => { - strats[hook] = mergeHook -}) - -/** - * 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, - childVal: ?Object, - vm?: Component, - key: string -): Object { - const res = Object.create(parentVal || null) - if (childVal) { - process.env.NODE_ENV !== 'production' && 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: ?Object, - childVal: ?Object, - vm?: Component, - key: string -): ?Object { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) parentVal = undefined - if (childVal === nativeWatch) childVal = undefined - /* istanbul ignore if */ - if (!childVal) return Object.create(parentVal || null) - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm) - } - if (!parentVal) return childVal - const ret = {} - extend(ret, parentVal) - for (const key in childVal) { - let parent = ret[key] - const child = childVal[key] - if (parent && !Array.isArray(parent)) { - parent = [parent] - } - ret[key] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child] - } - return ret -} - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal: ?Object, - childVal: ?Object, - vm?: Component, - key: string -): ?Object { - if (childVal && process.env.NODE_ENV !== 'production') { - 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 = mergeDataOrFn - -/** - * Default strategy. - */ -const defaultStrat = function (parentVal: any, childVal: any): any { - return childVal === undefined - ? parentVal - : childVal -} - -/** - * Validate component names - */ -function checkComponents (options: Object) { - for (const key in options.components) { - const lower = key.toLowerCase() - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ) - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options: Object, vm: ?Component) { - const props = options.props - if (!props) return - const res = {} - let i, val, name - if (Array.isArray(props)) { - i = props.length - while (i--) { - val = props[i] - if (typeof val === 'string') { - name = camelize(val) - res[name] = { type: null } - } else if (process.env.NODE_ENV !== 'production') { - 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 (process.env.NODE_ENV !== 'production') { - 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: Object, vm: ?Component) { - const inject = options.inject - const normalized = options.inject = {} - if (Array.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 (process.env.NODE_ENV !== 'production' && inject) { - 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: Object) { - const dirs = options.directives - if (dirs) { - for (const key in dirs) { - const def = dirs[key] - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def } - } - } - } -} - -function assertObjectType (name: string, value: any, vm: ?Component) { - 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: Object, - child: Object, - vm?: Component -): Object { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child) - } - - if (typeof child === 'function') { - child = child.options - } - - normalizeProps(child, vm) - normalizeInject(child, vm) - normalizeDirectives(child) - const extendsFrom = child.extends - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm) - } - if (child.mixins) { - for (let i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm) - } - } - const options = {} - let key - for (key in parent) { - mergeField(key) - } - for (key in child) { - if (!hasOwn(parent, key)) { - mergeField(key) - } - } - function mergeField (key) { - 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: Object, - 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 (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ) - } - return res -} diff --git a/src/core/util/options.ts b/src/core/util/options.ts new file mode 100644 index 00000000000..ef6a10342a9 --- /dev/null +++ b/src/core/util/options.ts @@ -0,0 +1,489 @@ +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.js b/src/core/util/perf.js deleted file mode 100644 index 4a537b2dada..00000000000 --- a/src/core/util/perf.js +++ /dev/null @@ -1,24 +0,0 @@ -import { inBrowser } from './env' - -export let mark -export let measure - -if (process.env.NODE_ENV !== 'production') { - const perf = inBrowser && window.performance - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - 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/perf.ts b/src/core/util/perf.ts new file mode 100644 index 00000000000..6b6f0a00a3d --- /dev/null +++ b/src/core/util/perf.ts @@ -0,0 +1,28 @@ +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.js b/src/core/util/props.js deleted file mode 100644 index 310514d7df6..00000000000 --- a/src/core/util/props.js +++ /dev/null @@ -1,189 +0,0 @@ -/* @flow */ - -import { warn } from './debug' -import { observe, observerState } from '../observer/index' -import { - hasOwn, - isObject, - toRawType, - hyphenate, - capitalize, - isPlainObject -} from 'shared/util' - -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] - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - 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 prevShouldConvert = observerState.shouldConvert - observerState.shouldConvert = true - observe(value) - observerState.shouldConvert = prevShouldConvert - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent) - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm: ?Component, 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 (process.env.NODE_ENV !== 'production' && 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 typeof def === 'function' && 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 === true - const expectedTypes = [] - if (type) { - if (!Array.isArray(type)) { - type = [type] - } - for (let i = 0; i < type.length && !valid; i++) { - const assertedType = assertType(value, type[i]) - expectedTypes.push(assertedType.expectedType || '') - valid = assertedType.valid - } - } - if (!valid) { - warn( - `Invalid prop: type check failed for prop "${name}".` + - ` Expected ${expectedTypes.map(capitalize).join(', ')}` + - `, got ${toRawType(value)}.`, - 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)$/ - -function assertType (value: any, type: Function): { - 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 = Array.isArray(value) - } else { - valid = value instanceof type - } - return { - valid, - expectedType - } -} - -/** - * 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(/^\s*function (\w+)/) - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (let i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} diff --git a/src/core/util/props.ts b/src/core/util/props.ts new file mode 100644 index 00000000000..795f5ac37a6 --- /dev/null +++ b/src/core/util/props.ts @@ -0,0 +1,254 @@ +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.js b/src/core/vdom/create-component.js deleted file mode 100644 index d3de4fb4c20..00000000000 --- a/src/core/vdom/create-component.js +++ /dev/null @@ -1,255 +0,0 @@ -/* @flow */ - -import VNode from './vnode' -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' - -// hooks to be invoked on component VNodes during patch -const componentVNodeHooks = { - init ( - vnode: VNodeWithData, - hydrating: boolean, - parentElm: ?Node, - refElm: ?Node - ): ?boolean { - if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) { - const child = vnode.componentInstance = createComponentInstanceForVnode( - vnode, - activeInstance, - parentElm, - refElm - ) - child.$mount(hydrating ? vnode.elm : undefined, hydrating) - } else if (vnode.data.keepAlive) { - // kept-alive components, treat as a patch - const mountedNode: any = vnode // work around flow - componentVNodeHooks.prepatch(mountedNode, mountedNode) - } - }, - - 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: Class<Component> | Function | Object | void, - data: ?VNodeData, - context: Component, - children: ?Array<VNode>, - tag?: string -): 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) - } - - // if at this stage it's not a constructor or an async component factory, - // reject. - if (typeof Ctor !== 'function') { - if (process.env.NODE_ENV !== 'production') { - warn(`Invalid Component definition: ${String(Ctor)}`, context) - } - return - } - - // async component - let asyncFactory - if (isUndef(Ctor.cid)) { - asyncFactory = Ctor - Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context) - 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) - - // transform component v-model data into props & events - if (isDef(data.model)) { - transformModel(Ctor.options, data) - } - - // extract props - const propsData = extractPropsFromVNodeData(data, Ctor, tag) - - // functional component - if (isTrue(Ctor.options.functional)) { - return createFunctionalComponent(Ctor, 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 - - 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 - } - } - - // merge component management hooks onto the placeholder node - mergeHooks(data) - - // return a placeholder vnode - const name = Ctor.options.name || tag - const vnode = new VNode( - `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`, - data, undefined, undefined, undefined, context, - { Ctor, propsData, listeners, tag, children }, - asyncFactory - ) - return vnode -} - -export function createComponentInstanceForVnode ( - vnode: any, // we know it's MountedComponentVNode but flow doesn't - parent: any, // activeInstance in lifecycle state - parentElm?: ?Node, - refElm?: ?Node -): Component { - const vnodeComponentOptions = vnode.componentOptions - const options: InternalComponentOptions = { - _isComponent: true, - parent, - propsData: vnodeComponentOptions.propsData, - _componentTag: vnodeComponentOptions.tag, - _parentVnode: vnode, - _parentListeners: vnodeComponentOptions.listeners, - _renderChildren: vnodeComponentOptions.children, - _parentElm: parentElm || null, - _refElm: refElm || null - } - // check inline-template render functions - const inlineTemplate = vnode.data.inlineTemplate - if (isDef(inlineTemplate)) { - options.render = inlineTemplate.render - options.staticRenderFns = inlineTemplate.staticRenderFns - } - return new vnodeComponentOptions.Ctor(options) -} - -function mergeHooks (data: VNodeData) { - if (!data.hook) { - data.hook = {} - } - for (let i = 0; i < hooksToMerge.length; i++) { - const key = hooksToMerge[i] - const fromParent = data.hook[key] - const ours = componentVNodeHooks[key] - data.hook[key] = fromParent ? mergeHook(ours, fromParent) : ours - } -} - -function mergeHook (one: Function, two: Function): Function { - return function (a, b, c, d) { - one(a, b, c, d) - two(a, b, c, d) - } -} - -// 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.props || (data.props = {}))[prop] = data.model.value - const on = data.on || (data.on = {}) - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]) - } else { - on[event] = data.model.callback - } -} diff --git a/src/core/vdom/create-component.ts b/src/core/vdom/create-component.ts new file mode 100644 index 00000000000..9e48c575230 --- /dev/null +++ b/src/core/vdom/create-component.ts @@ -0,0 +1,275 @@ +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.js b/src/core/vdom/create-element.js deleted file mode 100644 index adeac2d61ff..00000000000 --- a/src/core/vdom/create-element.js +++ /dev/null @@ -1,140 +0,0 @@ -/* @flow */ - -import config from '../config' -import VNode, { createEmptyVNode } from './vnode' -import { createComponent } from './create-component' - -import { - warn, - isDef, - isUndef, - isTrue, - isPrimitive, - resolveAsset -} from '../util/index' - -import { - normalizeChildren, - simpleNormalizeChildren -} from './helpers/index' - -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 { - if (Array.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 | Class<Component> | Function | Object, - data?: VNodeData, - children?: any, - normalizationType?: number -): VNode { - if (isDef(data) && isDef((data: any).__ob__)) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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 (Array.isArray(children) && - typeof children[0] === 'function' - ) { - 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 - vnode = new VNode( - config.parsePlatformTagName(tag), data, children, - undefined, undefined, context - ) - } else if (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, data, context, children) - } - if (isDef(vnode)) { - if (ns) applyNS(vnode, ns) - return vnode - } else { - return createEmptyVNode() - } -} - -function applyNS (vnode, ns, force) { - 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))) { - applyNS(child, ns, force) - } - } - } -} diff --git a/src/core/vdom/create-element.ts b/src/core/vdom/create-element.ts new file mode 100644 index 00000000000..62dd004c34a --- /dev/null +++ b/src/core/vdom/create-element.ts @@ -0,0 +1,172 @@ +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.js b/src/core/vdom/create-functional-component.js deleted file mode 100644 index d749ab9a7de..00000000000 --- a/src/core/vdom/create-functional-component.js +++ /dev/null @@ -1,108 +0,0 @@ -/* @flow */ - -import VNode from './vnode' -import { createElement } from './create-element' -import { resolveInject } from '../instance/inject' -import { resolveSlots } from '../instance/render-helpers/resolve-slots' -import { installRenderHelpers } from '../instance/render-helpers/index' - -import { - isDef, - isTrue, - camelize, - emptyObject, - validateProp -} from '../util/index' - -function FunctionalRenderContext ( - data, - props, - children, - parent, - Ctor -) { - const options = Ctor.options - 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 = () => resolveSlots(children, parent) - - // ensure the createElement function in functional components - // gets a unique context - this is necessary for correct named slot check - const contextVm = Object.create(parent) - const isCompiled = isTrue(options._compiled) - const needNormalization = !isCompiled - - // 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 = data.scopedSlots || emptyObject - } - - if (options._scopeId) { - this._c = (a, b, c, d) => { - const vnode: ?VNode = createElement(contextVm, a, b, c, d, needNormalization) - if (vnode) { - vnode.functionalScopeId = options._scopeId - vnode.functionalContext = parent - } - return vnode - } - } else { - this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization) - } -} - -installRenderHelpers(FunctionalRenderContext.prototype) - -export function createFunctionalComponent ( - Ctor: Class<Component>, - propsData: ?Object, - data: VNodeData, - contextVm: Component, - children: ?Array<VNode> -): 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) { - vnode.functionalContext = contextVm - vnode.functionalOptions = options - if (data.slot) { - (vnode.data || (vnode.data = {})).slot = data.slot - } - } - - return vnode -} - -function mergeProps (to, from) { - for (const key in from) { - to[camelize(key)] = from[key] - } -} diff --git a/src/core/vdom/create-functional-component.ts b/src/core/vdom/create-functional-component.ts new file mode 100644 index 00000000000..55bc5bd1ba3 --- /dev/null +++ b/src/core/vdom/create-functional-component.ts @@ -0,0 +1,180 @@ +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.js b/src/core/vdom/helpers/extract-props.js deleted file mode 100644 index f2a4166135d..00000000000 --- a/src/core/vdom/helpers/extract-props.js +++ /dev/null @@ -1,75 +0,0 @@ -/* @flow */ - -import { - tip, - hasOwn, - isDef, - isUndef, - hyphenate, - formatComponentName -} from 'core/util/index' - -export function extractPropsFromVNodeData ( - data: VNodeData, - Ctor: Class<Component>, - tag?: string -): ?Object { - // 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 (process.env.NODE_ENV !== 'production') { - const keyInLowerCase = key.toLowerCase() - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - `Prop "${keyInLowerCase}" is passed to component ` + - `${formatComponentName(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, - 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/extract-props.ts b/src/core/vdom/helpers/extract-props.ts new file mode 100644 index 00000000000..a8015aa6e75 --- /dev/null +++ b/src/core/vdom/helpers/extract-props.ts @@ -0,0 +1,75 @@ +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.js b/src/core/vdom/helpers/get-first-component-child.js deleted file mode 100644 index f8649dd8728..00000000000 --- a/src/core/vdom/helpers/get-first-component-child.js +++ /dev/null @@ -1,15 +0,0 @@ -/* @flow */ - -import { isDef } from 'shared/util' -import { isAsyncPlaceholder } from './is-async-placeholder' - -export function getFirstComponentChild (children: ?Array<VNode>): ?VNode { - if (Array.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/get-first-component-child.ts b/src/core/vdom/helpers/get-first-component-child.ts new file mode 100644 index 00000000000..ab4543af3eb --- /dev/null +++ b/src/core/vdom/helpers/get-first-component-child.ts @@ -0,0 +1,16 @@ +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.js b/src/core/vdom/helpers/index.js deleted file mode 100644 index 320cf4c4343..00000000000 --- a/src/core/vdom/helpers/index.js +++ /dev/null @@ -1,9 +0,0 @@ -/* @flow */ - -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/index.ts b/src/core/vdom/helpers/index.ts new file mode 100644 index 00000000000..12a72ee848b --- /dev/null +++ b/src/core/vdom/helpers/index.ts @@ -0,0 +1,7 @@ +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.js b/src/core/vdom/helpers/is-async-placeholder.js deleted file mode 100644 index 6c4a6596b6a..00000000000 --- a/src/core/vdom/helpers/is-async-placeholder.js +++ /dev/null @@ -1,5 +0,0 @@ -/* @flow */ - -export function isAsyncPlaceholder (node: VNode): boolean { - return node.isComment && node.asyncFactory -} diff --git a/src/core/vdom/helpers/is-async-placeholder.ts b/src/core/vdom/helpers/is-async-placeholder.ts new file mode 100644 index 00000000000..6e0d602758b --- /dev/null +++ b/src/core/vdom/helpers/is-async-placeholder.ts @@ -0,0 +1,6 @@ +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.js b/src/core/vdom/helpers/merge-hook.js deleted file mode 100644 index 0bb96e8bacb..00000000000 --- a/src/core/vdom/helpers/merge-hook.js +++ /dev/null @@ -1,38 +0,0 @@ -/* @flow */ - -import VNode from '../vnode' -import { createFnInvoker } from './update-listeners' -import { remove, isDef, isUndef, isTrue } from 'shared/util' - -export function mergeVNodeHook (def: Object, 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/merge-hook.ts b/src/core/vdom/helpers/merge-hook.ts new file mode 100644 index 00000000000..6881f914170 --- /dev/null +++ b/src/core/vdom/helpers/merge-hook.ts @@ -0,0 +1,40 @@ +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.js b/src/core/vdom/helpers/normalize-children.js deleted file mode 100644 index a4622ef44dc..00000000000 --- a/src/core/vdom/helpers/normalize-children.js +++ /dev/null @@ -1,89 +0,0 @@ -/* @flow */ - -import VNode, { createTextVNode } from 'core/vdom/vnode' -import { isFalse, isTrue, 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 (Array.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> { - return isPrimitive(children) - ? [createTextVNode(children)] - : Array.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 = [] - 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 (Array.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]: any).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-children.ts b/src/core/vdom/helpers/normalize-children.ts new file mode 100644 index 00000000000..6908ab1d15d --- /dev/null +++ b/src/core/vdom/helpers/normalize-children.ts @@ -0,0 +1,99 @@ +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 new file mode 100644 index 00000000000..10a0d1186e1 --- /dev/null +++ b/src/core/vdom/helpers/normalize-scoped-slots.ts @@ -0,0 +1,97 @@ +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.js b/src/core/vdom/helpers/resolve-async-component.js deleted file mode 100644 index 99ce5ce47ed..00000000000 --- a/src/core/vdom/helpers/resolve-async-component.js +++ /dev/null @@ -1,140 +0,0 @@ -/* @flow */ - -import { - warn, - once, - isDef, - isUndef, - isTrue, - isObject, - hasSymbol -} from 'core/util/index' - -import { createEmptyVNode } from 'core/vdom/vnode' - -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, - context: Component, - children: ?Array<VNode>, - tag: ?string -): VNode { - const node = createEmptyVNode() - node.asyncFactory = factory - node.asyncMeta = { data, context, children, tag } - return node -} - -export function resolveAsyncComponent ( - factory: Function, - baseCtor: Class<Component>, - context: Component -): Class<Component> | void { - if (isTrue(factory.error) && isDef(factory.errorComp)) { - return factory.errorComp - } - - if (isDef(factory.resolved)) { - return factory.resolved - } - - if (isTrue(factory.loading) && isDef(factory.loadingComp)) { - return factory.loadingComp - } - - if (isDef(factory.contexts)) { - // already pending - factory.contexts.push(context) - } else { - const contexts = factory.contexts = [context] - let sync = true - - const forceRender = () => { - for (let i = 0, l = contexts.length; i < l; i++) { - contexts[i].$forceUpdate() - } - } - - const resolve = once((res: Object | Class<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() - } - }) - - const reject = once(reason => { - process.env.NODE_ENV !== 'production' && warn( - `Failed to resolve async component: ${String(factory)}` + - (reason ? `\nReason: ${reason}` : '') - ) - if (isDef(factory.errorComp)) { - factory.error = true - forceRender() - } - }) - - const res = factory(resolve, reject) - - if (isObject(res)) { - if (typeof res.then === 'function') { - // () => Promise - if (isUndef(factory.resolved)) { - res.then(resolve, reject) - } - } else if (isDef(res.component) && typeof res.component.then === 'function') { - 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 { - setTimeout(() => { - if (isUndef(factory.resolved) && isUndef(factory.error)) { - factory.loading = true - forceRender() - } - }, res.delay || 200) - } - } - - if (isDef(res.timeout)) { - setTimeout(() => { - if (isUndef(factory.resolved)) { - reject( - process.env.NODE_ENV !== 'production' - ? `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/resolve-async-component.ts b/src/core/vdom/helpers/resolve-async-component.ts new file mode 100644 index 00000000000..b7c66ae6459 --- /dev/null +++ b/src/core/vdom/helpers/resolve-async-component.ts @@ -0,0 +1,157 @@ +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.js b/src/core/vdom/helpers/update-listeners.js deleted file mode 100644 index d587eecf38a..00000000000 --- a/src/core/vdom/helpers/update-listeners.js +++ /dev/null @@ -1,76 +0,0 @@ -/* @flow */ - -import { warn } from 'core/util/index' -import { cached, isUndef } from 'shared/util' - -const normalizeEvent = cached((name: string): { - name: string, - once: boolean, - capture: boolean, - passive: boolean -} => { - 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>): Function { - function invoker () { - const fns = invoker.fns - if (Array.isArray(fns)) { - const cloned = fns.slice() - for (let i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments) - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns - return invoker -} - -export function updateListeners ( - on: Object, - oldOn: Object, - add: Function, - remove: Function, - vm: Component -) { - let name, cur, old, event - for (name in on) { - cur = on[name] - old = oldOn[name] - event = normalizeEvent(name) - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - `Invalid handler for event "${event.name}": got ` + String(cur), - vm - ) - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur) - } - add(event.name, cur, event.once, event.capture, event.passive) - } 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/helpers/update-listeners.ts b/src/core/vdom/helpers/update-listeners.ts new file mode 100644 index 00000000000..f7c3c03efb4 --- /dev/null +++ b/src/core/vdom/helpers/update-listeners.ts @@ -0,0 +1,101 @@ +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.js b/src/core/vdom/modules/directives.js deleted file mode 100644 index 42e33f9e981..00000000000 --- a/src/core/vdom/modules/directives.js +++ /dev/null @@ -1,116 +0,0 @@ -/* @flow */ - -import { emptyNode } from 'core/vdom/patch' -import { resolveAsset, handleError } from 'core/util/index' -import { mergeVNodeHook } from 'core/vdom/helpers/index' - -export default { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode: 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 = [] - const dirsWithPostpatch = [] - - 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 - 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>, - vm: Component -): { [key: string]: VNodeDirective } { - const res = Object.create(null) - if (!dirs) { - return res - } - let i, dir - for (i = 0; i < dirs.length; i++) { - dir = dirs[i] - if (!dir.modifiers) { - dir.modifiers = emptyModifiers - } - res[getRawDirName(dir)] = dir - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true) - } - return res -} - -function getRawDirName (dir: VNodeDirective): string { - return dir.rawName || `${dir.name}.${Object.keys(dir.modifiers || {}).join('.')}` -} - -function callHook (dir, hook, vnode, oldVnode, isDestroy) { - const fn = dir.def && dir.def[hook] - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy) - } catch (e) { - handleError(e, vnode.context, `directive ${dir.name} ${hook} hook`) - } - } -} diff --git a/src/core/vdom/modules/directives.ts b/src/core/vdom/modules/directives.ts new file mode 100644 index 00000000000..853b20021e7 --- /dev/null +++ b/src/core/vdom/modules/directives.ts @@ -0,0 +1,137 @@ +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.js b/src/core/vdom/modules/index.js deleted file mode 100644 index 727c864604d..00000000000 --- a/src/core/vdom/modules/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import directives from './directives' -import ref from './ref' - -export default [ - ref, - directives -] diff --git a/src/core/vdom/modules/index.ts b/src/core/vdom/modules/index.ts new file mode 100644 index 00000000000..327693d098c --- /dev/null +++ b/src/core/vdom/modules/index.ts @@ -0,0 +1,4 @@ +import directives from './directives' +import ref from './template-ref' + +export default [ref, directives] diff --git a/src/core/vdom/modules/ref.js b/src/core/vdom/modules/ref.js deleted file mode 100644 index 00bb5f52709..00000000000 --- a/src/core/vdom/modules/ref.js +++ /dev/null @@ -1,45 +0,0 @@ -/* @flow */ - -import { remove } from 'shared/util' - -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 key = vnode.data.ref - if (!key) return - - const vm = vnode.context - const ref = vnode.componentInstance || vnode.elm - const refs = vm.$refs - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref) - } else if (refs[key] === ref) { - refs[key] = undefined - } - } else { - if (vnode.data.refInFor) { - if (!Array.isArray(refs[key])) { - refs[key] = [ref] - } else if (refs[key].indexOf(ref) < 0) { - // $flow-disable-line - refs[key].push(ref) - } - } else { - refs[key] = ref - } - } -} diff --git a/src/core/vdom/modules/template-ref.ts b/src/core/vdom/modules/template-ref.ts new file mode 100644 index 00000000000..27461fd94ba --- /dev/null +++ b/src/core/vdom/modules/template-ref.ts @@ -0,0 +1,94 @@ +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.js b/src/core/vdom/patch.js deleted file mode 100644 index 1a764f20cf8..00000000000 --- a/src/core/vdom/patch.js +++ /dev/null @@ -1,734 +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 from './vnode' -import config from '../config' -import { SSR_ATTR } from 'shared/constants' -import { registerRef } from './modules/ref' -import { activeInstance } from '../instance/lifecycle' -import { isTextInputType } from 'web/util/element' - -import { - warn, - isDef, - isUndef, - isTrue, - 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.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) - ) || ( - isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && - 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 = {} - - 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) - } - } - - let inPre = 0 - function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { - 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 (process.env.NODE_ENV !== 'production') { - if (data && data.pre) { - inPre++ - } - if ( - !inPre && - !vnode.ns && - !( - config.ignoredElements.length && - config.ignoredElements.some(ignore => { - return isRegExp(ignore) - ? ignore.test(tag) - : ignore === tag - }) - ) && - config.isUnknownElement(tag) - ) { - 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) - - /* istanbul ignore if */ - if (__WEEX__) { - // in Weex, the default insertion order is parent-first. - // List items can be optimized to use children-first insertion - // with append="tree". - const appendAsTree = isDef(data) && isTrue(data.appendAsTree) - if (!appendAsTree) { - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue) - } - insert(parentElm, vnode.elm, refElm) - } - createChildren(vnode, children, insertedVnodeQueue) - if (appendAsTree) { - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue) - } - insert(parentElm, vnode.elm, refElm) - } - } else { - createChildren(vnode, children, insertedVnodeQueue) - if (isDef(data)) { - invokeCreateHooks(vnode, insertedVnodeQueue) - } - insert(parentElm, vnode.elm, refElm) - } - - if (process.env.NODE_ENV !== 'production' && data && data.pre) { - inPre-- - } - } 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 */, parentElm, refElm) - } - // 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) - 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 (ref.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref) - } - } else { - nodeOps.appendChild(parent, elm) - } - } - } - - function createChildren (vnode, children, insertedVnodeQueue) { - if (Array.isArray(children)) { - for (let i = 0; i < children.length; ++i) { - createElm(children[i], insertedVnodeQueue, vnode.elm, null, true) - } - } else if (isPrimitive(vnode.text)) { - nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(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.functionalScopeId)) { - nodeOps.setAttribute(vnode.elm, i, '') - } else { - let ancestor = vnode - while (ancestor) { - if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) { - nodeOps.setAttribute(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.functionalContext && - isDef(i = i.$options._scopeId) - ) { - nodeOps.setAttribute(vnode.elm, i, '') - } - } - - function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) { - for (; startIdx <= endIdx; ++startIdx) { - createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm) - } - } - - 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 (parentElm, 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) { - 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 - - 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) - oldStartVnode = oldCh[++oldStartIdx] - newStartVnode = newCh[++newStartIdx] - } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue) - oldEndVnode = oldCh[--oldEndIdx] - newEndVnode = newCh[--newEndIdx] - } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue) - 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) - 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) - } else { - vnodeToMove = oldCh[idxInOld] - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ) - } - if (sameVnode(vnodeToMove, newStartVnode)) { - patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue) - 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) - } - } - 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(parentElm, oldCh, oldStartIdx, oldEndIdx) - } - } - - 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, removeOnly) { - if (oldVnode === vnode) { - return - } - - 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 (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '') - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue) - } else if (isDef(oldCh)) { - removeVnodes(elm, 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 bailed = 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 - const isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key') - - // Note: this is a browser-only function so we can assume elms are DOM nodes. - function hydrate (elm, vnode, insertedVnodeQueue) { - if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { - vnode.elm = elm - vnode.isAsyncPlaceholder = true - return true - } - if (process.env.NODE_ENV !== 'production') { - if (!assertNodeMatch(elm, vnode)) { - return false - } - } - vnode.elm = elm - const { tag, data, children } = vnode - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = 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)) { - 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 (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed - ) { - bailed = true - console.warn('Parent: ', elm) - console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children) - } - return false - } - } - } - } - if (isDef(data)) { - for (const key in data) { - if (!isRenderedModule(key)) { - invokeCreateHooks(vnode, insertedVnodeQueue) - break - } - } - } - } else if (elm.data !== vnode.text) { - elm.data = vnode.text - } - return true - } - - function assertNodeMatch (node, vnode) { - if (isDef(vnode.tag)) { - return ( - vnode.tag.indexOf('vue-component') === 0 || - vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase()) - ) - } else { - return node.nodeType === (vnode.isComment ? 8 : 3) - } - } - - return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { - if (isUndef(vnode)) { - if (isDef(oldVnode)) invokeDestroyHook(oldVnode) - return - } - - let isInitialPatch = false - const insertedVnodeQueue = [] - - if (isUndef(oldVnode)) { - // empty mount (likely as component), create new root element - isInitialPatch = true - createElm(vnode, insertedVnodeQueue, parentElm, refElm) - } else { - const isRealElement = isDef(oldVnode.nodeType) - if (!isRealElement && sameVnode(oldVnode, vnode)) { - // patch existing root node - patchVnode(oldVnode, vnode, insertedVnodeQueue, 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 (process.env.NODE_ENV !== 'production') { - 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 - for (let i = 1; i < insert.fns.length; i++) { - insert.fns[i]() - } - } - } else { - registerRef(ancestor) - } - ancestor = ancestor.parent - } - } - - // destroy old node - if (isDef(parentElm)) { - removeVnodes(parentElm, [oldVnode], 0, 0) - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode) - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch) - return vnode.elm - } -} diff --git a/src/core/vdom/patch.ts b/src/core/vdom/patch.ts new file mode 100644 index 00000000000..173840787bc --- /dev/null +++ b/src/core/vdom/patch.ts @@ -0,0 +1,907 @@ +/** + * 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.js b/src/core/vdom/vnode.js deleted file mode 100644 index 33b5612fe6e..00000000000 --- a/src/core/vdom/vnode.js +++ /dev/null @@ -1,123 +0,0 @@ -/* @flow */ - -export default class VNode { - tag: string | void; - data: VNodeData | void; - children: ?Array<VNode>; - text: string | void; - elm: Node | void; - ns: string | void; - context: Component | void; // rendered in this component's scope - key: string | number | void; - componentOptions: VNodeComponentOptions | void; - componentInstance: Component | void; // component instance - parent: VNode | void; // 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 | void; // async component factory function - asyncMeta: Object | void; - isAsyncPlaceholder: boolean; - ssrContext: Object | void; - functionalContext: Component | void; // real context vm for functional nodes - functionalOptions: ?ComponentOptions; // for SSR caching - functionalScopeId: ?string; // functioanl scope id support - - constructor ( - tag?: string, - data?: VNodeData, - children?: ?Array<VNode>, - 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.functionalContext = undefined - this.functionalOptions = undefined - this.functionalScopeId = 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, deep?: boolean): VNode { - const componentOptions = vnode.componentOptions - const cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ) - cloned.ns = vnode.ns - cloned.isStatic = vnode.isStatic - cloned.key = vnode.key - cloned.isComment = vnode.isComment - cloned.isCloned = true - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true) - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true) - } - } - return cloned -} - -export function cloneVNodes (vnodes: Array<VNode>, deep?: boolean): Array<VNode> { - const len = vnodes.length - const res = new Array(len) - for (let i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep) - } - return res -} diff --git a/src/core/vdom/vnode.ts b/src/core/vdom/vnode.ts new file mode 100644 index 00000000000..3b57f9aca0c --- /dev/null +++ b/src/core/vdom/vnode.ts @@ -0,0 +1,119 @@ +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/global.d.ts b/src/global.d.ts new file mode 100644 index 00000000000..badbd1e02e8 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,17 @@ +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/platforms/web/compiler/directives/html.js b/src/platforms/web/compiler/directives/html.js deleted file mode 100644 index 58569d8e47e..00000000000 --- a/src/platforms/web/compiler/directives/html.js +++ /dev/null @@ -1,9 +0,0 @@ -/* @flow */ - -import { addProp } from 'compiler/helpers' - -export default function html (el: ASTElement, dir: ASTDirective) { - if (dir.value) { - addProp(el, 'innerHTML', `_s(${dir.value})`) - } -} diff --git a/src/platforms/web/compiler/directives/html.ts b/src/platforms/web/compiler/directives/html.ts new file mode 100644 index 00000000000..4aeac3fc4a1 --- /dev/null +++ b/src/platforms/web/compiler/directives/html.ts @@ -0,0 +1,8 @@ +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.js b/src/platforms/web/compiler/directives/index.ts similarity index 100% rename from src/platforms/web/compiler/directives/index.js rename to src/platforms/web/compiler/directives/index.ts diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js deleted file mode 100644 index 98d4259cf98..00000000000 --- a/src/platforms/web/compiler/directives/model.js +++ /dev/null @@ -1,157 +0,0 @@ -/* @flow */ - -import config from 'core/config' -import { addHandler, addProp, getBindingAttr } from 'compiler/helpers' -import { genComponentModel, genAssignmentCode } from 'compiler/directives/model' - -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 { - warn = _warn - const value = dir.value - const modifiers = dir.modifiers - const tag = el.tag - const type = el.attrsMap.type - - if (process.env.NODE_ENV !== 'production') { - // 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.` - ) - } - } - - 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 (process.env.NODE_ENV !== 'production') { - 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.' - ) - } - - // ensure runtime directive metadata - return true -} - -function genCheckboxModel ( - el: ASTElement, - value: string, - modifiers: ?ASTModifiers -) { - 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&&(${value}=$$a.concat([$$v]))}` + - `else{$$i>-1&&(${value}=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}` + - `}else{${genAssignmentCode(value, '$$c')}}`, - null, true - ) -} - -function genRadioModel ( - el: ASTElement, - value: string, - modifiers: ?ASTModifiers -) { - 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 -) { - 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 -): ?boolean { - const type = el.attrsMap.type - 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/model.ts b/src/platforms/web/compiler/directives/model.ts new file mode 100644 index 00000000000..ba6bb28ca2d --- /dev/null +++ b/src/platforms/web/compiler/directives/model.ts @@ -0,0 +1,181 @@ +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.js b/src/platforms/web/compiler/directives/text.js deleted file mode 100644 index 1af914f8ea0..00000000000 --- a/src/platforms/web/compiler/directives/text.js +++ /dev/null @@ -1,9 +0,0 @@ -/* @flow */ - -import { addProp } from 'compiler/helpers' - -export default function text (el: ASTElement, dir: ASTDirective) { - if (dir.value) { - addProp(el, 'textContent', `_s(${dir.value})`) - } -} diff --git a/src/platforms/web/compiler/directives/text.ts b/src/platforms/web/compiler/directives/text.ts new file mode 100644 index 00000000000..cabfd583124 --- /dev/null +++ b/src/platforms/web/compiler/directives/text.ts @@ -0,0 +1,8 @@ +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.js b/src/platforms/web/compiler/index.js deleted file mode 100644 index 7ace0936d46..00000000000 --- a/src/platforms/web/compiler/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* @flow */ - -import { baseOptions } from './options' -import { createCompiler } from 'compiler/index' - -const { compile, compileToFunctions } = createCompiler(baseOptions) - -export { compile, compileToFunctions } diff --git a/src/platforms/web/compiler/index.ts b/src/platforms/web/compiler/index.ts new file mode 100644 index 00000000000..857351f15f5 --- /dev/null +++ b/src/platforms/web/compiler/index.ts @@ -0,0 +1,6 @@ +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.js b/src/platforms/web/compiler/modules/class.js deleted file mode 100644 index eb93895fb43..00000000000 --- a/src/platforms/web/compiler/modules/class.js +++ /dev/null @@ -1,48 +0,0 @@ -/* @flow */ - -import { parseText } from 'compiler/parser/text-parser' -import { - getAndRemoveAttr, - getBindingAttr, - baseWarn -} from 'compiler/helpers' - -function transformNode (el: ASTElement, options: CompilerOptions) { - const warn = options.warn || baseWarn - const staticClass = getAndRemoveAttr(el, 'class') - if (process.env.NODE_ENV !== 'production' && staticClass) { - const expression = parseText(staticClass, options.delimiters) - if (expression) { - 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">.' - ) - } - } - if (staticClass) { - el.staticClass = JSON.stringify(staticClass) - } - 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 -} diff --git a/src/platforms/web/compiler/modules/class.ts b/src/platforms/web/compiler/modules/class.ts new file mode 100644 index 00000000000..b7837871459 --- /dev/null +++ b/src/platforms/web/compiler/modules/class.ts @@ -0,0 +1,44 @@ +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.js b/src/platforms/web/compiler/modules/index.js deleted file mode 100644 index 29114a530c2..00000000000 --- a/src/platforms/web/compiler/modules/index.js +++ /dev/null @@ -1,9 +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/index.ts b/src/platforms/web/compiler/modules/index.ts new file mode 100644 index 00000000000..438f93d2953 --- /dev/null +++ b/src/platforms/web/compiler/modules/index.ts @@ -0,0 +1,5 @@ +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.js b/src/platforms/web/compiler/modules/model.js deleted file mode 100644 index 5e882d72545..00000000000 --- a/src/platforms/web/compiler/modules/model.js +++ /dev/null @@ -1,87 +0,0 @@ -/* @flow */ - -/** - * Expand input[v-model] with dyanmic 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 { - getBindingAttr, - getAndRemoveAttr -} from 'compiler/helpers' - -import { - processFor, - processElement, - addIfCondition, - createASTElement -} from 'compiler/parser/index' - -function preTransformNode (el: ASTElement, options: CompilerOptions) { - if (el.tag === 'input') { - const map = el.attrsMap - if (map['v-model'] && (map['v-bind:type'] || map[':type'])) { - const typeBinding: any = getBindingAttr(el, 'type') - 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) -} - -function addRawAttr (el, name, value) { - el.attrsMap[name] = value - el.attrsList.push({ name, value }) -} - -export default { - preTransformNode -} diff --git a/src/platforms/web/compiler/modules/model.ts b/src/platforms/web/compiler/modules/model.ts new file mode 100644 index 00000000000..131483a83a1 --- /dev/null +++ b/src/platforms/web/compiler/modules/model.ts @@ -0,0 +1,89 @@ +/** + * 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.js b/src/platforms/web/compiler/modules/style.js deleted file mode 100644 index 425c50540e2..00000000000 --- a/src/platforms/web/compiler/modules/style.js +++ /dev/null @@ -1,51 +0,0 @@ -/* @flow */ - -import { parseText } from 'compiler/parser/text-parser' -import { parseStyleText } from 'web/util/style' -import { - getAndRemoveAttr, - getBindingAttr, - baseWarn -} from 'compiler/helpers' - -function transformNode (el: ASTElement, options: CompilerOptions) { - const warn = options.warn || baseWarn - const staticStyle = getAndRemoveAttr(el, 'style') - if (staticStyle) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production') { - const expression = parseText(staticStyle, options.delimiters) - if (expression) { - 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.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 -} diff --git a/src/platforms/web/compiler/modules/style.ts b/src/platforms/web/compiler/modules/style.ts new file mode 100644 index 00000000000..67e8524a4bb --- /dev/null +++ b/src/platforms/web/compiler/modules/style.ts @@ -0,0 +1,47 @@ +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.js b/src/platforms/web/compiler/options.js deleted file mode 100644 index 70c0c488358..00000000000 --- a/src/platforms/web/compiler/options.js +++ /dev/null @@ -1,26 +0,0 @@ -/* @flow */ - -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' - -export const baseOptions: CompilerOptions = { - expectHTML: true, - modules, - directives, - isPreTag, - isUnaryTag, - mustUseProp, - canBeLeftOpenTag, - isReservedTag, - getTagNamespace, - staticKeys: genStaticKeys(modules) -} diff --git a/src/platforms/web/compiler/options.ts b/src/platforms/web/compiler/options.ts new file mode 100644 index 00000000000..29e570ce3b8 --- /dev/null +++ b/src/platforms/web/compiler/options.ts @@ -0,0 +1,25 @@ +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.js b/src/platforms/web/compiler/util.js deleted file mode 100644 index 7d9394d3783..00000000000 --- a/src/platforms/web/compiler/util.js +++ /dev/null @@ -1,24 +0,0 @@ -/* @flow */ - -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/compiler/util.ts b/src/platforms/web/compiler/util.ts new file mode 100644 index 00000000000..097e0a47c97 --- /dev/null +++ b/src/platforms/web/compiler/util.ts @@ -0,0 +1,22 @@ +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.js b/src/platforms/web/entry-compiler.js deleted file mode 100644 index ee63476e1eb..00000000000 --- a/src/platforms/web/entry-compiler.js +++ /dev/null @@ -1,5 +0,0 @@ -/* @flow */ - -export { parseComponent } from 'sfc/parser' -export { compile, compileToFunctions } from './compiler/index' -export { ssrCompile, ssrCompileToFunctions } from './server/compiler' diff --git a/src/platforms/web/entry-compiler.ts b/src/platforms/web/entry-compiler.ts new file mode 100644 index 00000000000..659766493d0 --- /dev/null +++ b/src/platforms/web/entry-compiler.ts @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..f858d03d0df --- /dev/null +++ b/src/platforms/web/entry-runtime-esm.ts @@ -0,0 +1,5 @@ +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 new file mode 100644 index 00000000000..ff2ac5c928e --- /dev/null +++ b/src/platforms/web/entry-runtime-with-compiler-esm.ts @@ -0,0 +1,5 @@ +import Vue from './runtime-with-compiler' + +export default Vue + +export * from 'v3' diff --git a/src/platforms/web/entry-runtime-with-compiler.js b/src/platforms/web/entry-runtime-with-compiler.js deleted file mode 100644 index 1ebc102df71..00000000000 --- a/src/platforms/web/entry-runtime-with-compiler.js +++ /dev/null @@ -1,100 +0,0 @@ -/* @flow */ - -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' - -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) { - process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && !template) { - warn( - `Template element not found or is empty: ${options.template}`, - this - ) - } - } - } else if (template.nodeType) { - template = template.innerHTML - } else { - if (process.env.NODE_ENV !== 'production') { - warn('invalid template option:' + template, this) - } - return this - } - } else if (el) { - template = getOuterHTML(el) - } - if (template) { - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.performance && mark) { - mark('compile') - } - - const { render, staticRenderFns } = compileToFunctions(template, { - shouldDecodeNewlines, - shouldDecodeNewlinesForHref, - delimiters: options.delimiters, - comments: options.comments - }, this) - options.render = render - options.staticRenderFns = staticRenderFns - - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && 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 diff --git a/src/platforms/web/entry-runtime-with-compiler.ts b/src/platforms/web/entry-runtime-with-compiler.ts new file mode 100644 index 00000000000..a28aea80dc4 --- /dev/null +++ b/src/platforms/web/entry-runtime-with-compiler.ts @@ -0,0 +1,10 @@ +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.js b/src/platforms/web/entry-runtime.js deleted file mode 100644 index 27c97ff014b..00000000000 --- a/src/platforms/web/entry-runtime.js +++ /dev/null @@ -1,5 +0,0 @@ -/* @flow */ - -import Vue from './runtime/index' - -export default Vue diff --git a/src/platforms/web/entry-runtime.ts b/src/platforms/web/entry-runtime.ts new file mode 100644 index 00000000000..86cea399ec5 --- /dev/null +++ b/src/platforms/web/entry-runtime.ts @@ -0,0 +1,7 @@ +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/entry-server-basic-renderer.js b/src/platforms/web/entry-server-basic-renderer.js deleted file mode 100644 index c4b973bd10c..00000000000 --- a/src/platforms/web/entry-server-basic-renderer.js +++ /dev/null @@ -1,13 +0,0 @@ -/* @flow */ - -import modules from './server/modules/index' -import directives from './server/directives/index' -import { isUnaryTag, canBeLeftOpenTag } from './compiler/util' -import { createBasicRenderer } from 'server/create-basic-renderer' - -export default createBasicRenderer({ - modules, - directives, - isUnaryTag, - canBeLeftOpenTag -}) diff --git a/src/platforms/web/entry-server-renderer.js b/src/platforms/web/entry-server-renderer.js deleted file mode 100644 index f61f601b174..00000000000 --- a/src/platforms/web/entry-server-renderer.js +++ /dev/null @@ -1,27 +0,0 @@ -/* @flow */ - -process.env.VUE_ENV = 'server' - -import { extend } from 'shared/util' -import modules from './server/modules/index' -import baseDirectives from './server/directives/index' -import { isUnaryTag, canBeLeftOpenTag } from './compiler/util' - -import { createRenderer as _createRenderer } from 'server/create-renderer' -import { createBundleRendererCreator } from 'server/bundle-renderer/create-bundle-renderer' - -export function createRenderer (options?: Object = {}): { - renderToString: Function, - renderToStream: Function -} { - 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/src/platforms/web/runtime-with-compiler.ts b/src/platforms/web/runtime-with-compiler.ts new file mode 100644 index 00000000000..0dc1d132d38 --- /dev/null +++ b/src/platforms/web/runtime-with-compiler.ts @@ -0,0 +1,110 @@ +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.js b/src/platforms/web/runtime/class-util.js deleted file mode 100644 index 709526c238a..00000000000 --- a/src/platforms/web/runtime/class-util.js +++ /dev/null @@ -1,61 +0,0 @@ -/* @flow */ - -/** - * 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(/\s+/).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(/\s+/).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/class-util.ts b/src/platforms/web/runtime/class-util.ts new file mode 100644 index 00000000000..e66ffae5878 --- /dev/null +++ b/src/platforms/web/runtime/class-util.ts @@ -0,0 +1,61 @@ +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.js b/src/platforms/web/runtime/components/index.ts similarity index 100% rename from src/platforms/web/runtime/components/index.js rename to src/platforms/web/runtime/components/index.ts diff --git a/src/platforms/web/runtime/components/transition-group.js b/src/platforms/web/runtime/components/transition-group.js deleted file mode 100644 index 219ff01cf54..00000000000 --- a/src/platforms/web/runtime/components/transition-group.js +++ /dev/null @@ -1,180 +0,0 @@ -/* @flow */ - -// 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 '../class-util' -import { transitionProps, extractTransitionData } from './transition' - -import { - hasTransition, - getTransitionInfo, - transitionEndEvent, - addTransitionClass, - removeTransitionClass -} from '../transition-util' - -const props = extend({ - tag: String, - moveClass: String -}, transitionProps) - -delete props.mode - -export default { - props, - - render (h: Function) { - const tag: string = this.tag || this.$vnode.data.tag || 'span' - const map: Object = 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: Object = 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 (process.env.NODE_ENV !== 'production') { - const opts: ?VNodeComponentOptions = c.componentOptions - const name: string = opts ? (opts.Ctor.options.name || 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 - 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) - }, - - beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ) - this._vnode = this.kept - }, - - updated () { - const children: Array<VNode> = 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) { - var el: any = c.elm - var s: any = el.style - addTransitionClass(el, moveClass) - s.transform = s.WebkitTransform = s.transitionDuration = '' - el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { - 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: Object = getTransitionInfo(clone) - this.$el.removeChild(clone) - return (this._hasMove = info.hasTransform) - } - } -} - -function callPendingCbs (c: VNode) { - /* istanbul ignore if */ - if (c.elm._moveCb) { - c.elm._moveCb() - } - /* istanbul ignore if */ - if (c.elm._enterCb) { - c.elm._enterCb() - } -} - -function recordPosition (c: VNode) { - c.data.newPos = c.elm.getBoundingClientRect() -} - -function applyTranslation (c: VNode) { - 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-group.ts b/src/platforms/web/runtime/components/transition-group.ts new file mode 100644 index 00000000000..8588ea80561 --- /dev/null +++ b/src/platforms/web/runtime/components/transition-group.ts @@ -0,0 +1,204 @@ +// 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.js b/src/platforms/web/runtime/components/transition.js deleted file mode 100644 index 857c88f35e5..00000000000 --- a/src/platforms/web/runtime/components/transition.js +++ /dev/null @@ -1,192 +0,0 @@ -/* @flow */ - -// 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' - -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 { - const compOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions - if (compOptions && compOptions.Ctor.options.abstract) { - return getRealChild(getFirstComponentChild(compOptions.children)) - } else { - return vnode - } -} - -export function extractTransitionData (comp: Component): Object { - const data = {} - const options: ComponentOptions = 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: ?Object = options._parentListeners - for (const key in listeners) { - data[camelize(key)] = listeners[key] - } - return data -} - -function placeholder (h: Function, rawChild: VNode): ?VNode { - if (/\d-keep-alive$/.test(rawChild.tag)) { - return h('keep-alive', { - props: rawChild.componentOptions.propsData - }) - } -} - -function hasParentTransition (vnode: VNode): ?boolean { - 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 -} - -export default { - name: 'transition', - props: transitionProps, - abstract: true, - - render (h: Function) { - let children: ?Array<VNode> = this.$options._renderChildren - if (!children) { - return - } - - // filter out text nodes (possible whitespaces) - children = children.filter((c: VNode) => c.tag || isAsyncPlaceholder(c)) - /* istanbul ignore if */ - if (!children.length) { - return - } - - // warn multiple elements - if (process.env.NODE_ENV !== 'production' && 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 (process.env.NODE_ENV !== 'production' && - 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: ?VNode = 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: VNode = 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(d => d.name === 'show')) { - child.data.show = true - } - - if ( - oldChild && - oldChild.data && - !isSameChild(child, oldChild) && - !isAsyncPlaceholder(oldChild) - ) { - // 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/components/transition.ts b/src/platforms/web/runtime/components/transition.ts new file mode 100644 index 00000000000..8f5c05679ef --- /dev/null +++ b/src/platforms/web/runtime/components/transition.ts @@ -0,0 +1,205 @@ +// 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.js b/src/platforms/web/runtime/directives/index.ts similarity index 100% rename from src/platforms/web/runtime/directives/index.js rename to src/platforms/web/runtime/directives/index.ts diff --git a/src/platforms/web/runtime/directives/model.js b/src/platforms/web/runtime/directives/model.js deleted file mode 100644 index c3b669f3bb5..00000000000 --- a/src/platforms/web/runtime/directives/model.js +++ /dev/null @@ -1,149 +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, isAndroid, 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 - 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) { - // 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) - if (!isAndroid) { - el.addEventListener('compositionstart', onCompositionStart) - el.addEventListener('compositionend', 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)) { - process.env.NODE_ENV !== 'production' && 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/model.ts b/src/platforms/web/runtime/directives/model.ts new file mode 100644 index 00000000000..b5430894f6e --- /dev/null +++ b/src/platforms/web/runtime/directives/model.ts @@ -0,0 +1,148 @@ +/** + * 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.js b/src/platforms/web/runtime/directives/show.js deleted file mode 100644 index 703aa5b6afe..00000000000 --- a/src/platforms/web/runtime/directives/show.js +++ /dev/null @@ -1,60 +0,0 @@ -/* @flow */ - -import { enter, leave } from '../modules/transition' - -// recursively search for possible transition defined inside the component root -function locateNode (vnode: VNode): VNodeWithData { - 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/directives/show.ts b/src/platforms/web/runtime/directives/show.ts new file mode 100644 index 00000000000..e36178a0cd8 --- /dev/null +++ b/src/platforms/web/runtime/directives/show.ts @@ -0,0 +1,61 @@ +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.js b/src/platforms/web/runtime/index.js deleted file mode 100644 index 3751acecc53..00000000000 --- a/src/platforms/web/runtime/index.js +++ /dev/null @@ -1,70 +0,0 @@ -/* @flow */ - -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, isChrome } 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' - -// 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 */ -Vue.nextTick(() => { - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue) - } else if (process.env.NODE_ENV !== 'production' && isChrome) { - console[console.info ? 'info' : 'log']( - 'Download the Vue Devtools extension for a better development experience:\n' + - 'https://github.com/vuejs/vue-devtools' - ) - } - } - if (process.env.NODE_ENV !== 'production' && - config.productionTip !== false && - inBrowser && typeof console !== 'undefined' - ) { - 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/index.ts b/src/platforms/web/runtime/index.ts new file mode 100644 index 00000000000..d8cef39291b --- /dev/null +++ b/src/platforms/web/runtime/index.ts @@ -0,0 +1,75 @@ +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.js b/src/platforms/web/runtime/modules/attrs.js deleted file mode 100644 index 317316d8507..00000000000 --- a/src/platforms/web/runtime/modules/attrs.js +++ /dev/null @@ -1,95 +0,0 @@ -/* @flow */ - -import { isIE9, isEdge } from 'core/util/env' - -import { - extend, - isDef, - isUndef -} from 'shared/util' - -import { - isXlink, - xlinkNS, - getXlinkProp, - isBooleanAttr, - isEnumeratedAttr, - isFalsyAttrValue -} 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__)) { - attrs = vnode.data.attrs = extend({}, attrs) - } - - for (key in attrs) { - cur = attrs[key] - old = oldAttrs[key] - if (old !== cur) { - setAttr(elm, key, cur) - } - } - // #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 ((isIE9 || 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) { - 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, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true') - } else if (isXlink(key)) { - if (isFalsyAttrValue(value)) { - el.removeAttributeNS(xlinkNS, getXlinkProp(key)) - } else { - el.setAttributeNS(xlinkNS, key, value) - } - } else { - if (isFalsyAttrValue(value)) { - el.removeAttribute(key) - } else { - el.setAttribute(key, value) - } - } -} - -export default { - create: updateAttrs, - update: updateAttrs -} diff --git a/src/platforms/web/runtime/modules/attrs.ts b/src/platforms/web/runtime/modules/attrs.ts new file mode 100644 index 00000000000..431dc51ef63 --- /dev/null +++ b/src/platforms/web/runtime/modules/attrs.ts @@ -0,0 +1,115 @@ +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.js b/src/platforms/web/runtime/modules/class.js deleted file mode 100644 index 29fd2c111d2..00000000000 --- a/src/platforms/web/runtime/modules/class.js +++ /dev/null @@ -1,48 +0,0 @@ -/* @flow */ - -import { - isDef, - isUndef -} from 'shared/util' - -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/class.ts b/src/platforms/web/runtime/modules/class.ts new file mode 100644 index 00000000000..d1720c33d5f --- /dev/null +++ b/src/platforms/web/runtime/modules/class.ts @@ -0,0 +1,37 @@ +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.js b/src/platforms/web/runtime/modules/dom-props.js deleted file mode 100644 index c6ba3d0e924..00000000000 --- a/src/platforms/web/runtime/modules/dom-props.js +++ /dev/null @@ -1,89 +0,0 @@ -/* @flow */ - -import { isDef, isUndef, extend, toNumber } from 'shared/util' - -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__)) { - props = vnode.data.domProps = extend({}, props) - } - - for (key in oldProps) { - if (isUndef(props[key])) { - 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') { - // 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 { - elm[key] = cur - } - } -} - -// check platforms/web/util/attrs.js acceptValue -type acceptValueElm = HTMLInputElement | HTMLSelectElement | HTMLOptionElement; - -function shouldUpdateValue (elm: acceptValueElm, checkVal: string): boolean { - return (!elm.composing && ( - elm.tagName === 'OPTION' || - isDirty(elm, checkVal) || - isInputChanged(elm, checkVal) - )) -} - -function isDirty (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) {} - return notInFocus && elm.value !== checkVal -} - -function isInputChanged (elm: any, newVal: string): boolean { - const value = elm.value - const modifiers = elm._vModifiers // injected by v-model runtime - if (isDef(modifiers) && modifiers.number) { - return toNumber(value) !== toNumber(newVal) - } - if (isDef(modifiers) && modifiers.trim) { - return value.trim() !== newVal.trim() - } - return value !== newVal -} - -export default { - create: updateDOMProps, - update: updateDOMProps -} diff --git a/src/platforms/web/runtime/modules/dom-props.ts b/src/platforms/web/runtime/modules/dom-props.ts new file mode 100644 index 00000000000..b182211b9af --- /dev/null +++ b/src/platforms/web/runtime/modules/dom-props.ts @@ -0,0 +1,123 @@ +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.js b/src/platforms/web/runtime/modules/events.js deleted file mode 100644 index 1e71b667286..00000000000 --- a/src/platforms/web/runtime/modules/events.js +++ /dev/null @@ -1,87 +0,0 @@ -/* @flow */ - -import { isDef, isUndef } from 'shared/util' -import { updateListeners } from 'core/vdom/helpers/index' -import { withMacroTask, isIE, supportsPassive } from 'core/util/index' -import { RANGE_TOKEN, CHECKBOX_RADIO_TOKEN } from 'web/compiler/directives/model' - -// 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 (handler, event, 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) - } - } -} - -function add ( - event: string, - handler: Function, - once: boolean, - capture: boolean, - passive: boolean -) { - handler = withMacroTask(handler) - if (once) handler = createOnceHandler(handler, event, capture) - target.addEventListener( - event, - handler, - supportsPassive - ? { capture, passive } - : capture - ) -} - -function remove ( - event: string, - handler: Function, - capture: boolean, - _target?: HTMLElement -) { - (_target || target).removeEventListener( - event, - handler._withTask || 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 || {} - target = vnode.elm - normalizeEvents(on) - updateListeners(on, oldOn, add, remove, vnode.context) - target = undefined -} - -export default { - create: updateDOMListeners, - update: updateDOMListeners -} diff --git a/src/platforms/web/runtime/modules/events.ts b/src/platforms/web/runtime/modules/events.ts new file mode 100644 index 00000000000..ee71333ec03 --- /dev/null +++ b/src/platforms/web/runtime/modules/events.ts @@ -0,0 +1,127 @@ +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.js b/src/platforms/web/runtime/modules/index.js deleted file mode 100644 index 9f6ad8f956f..00000000000 --- a/src/platforms/web/runtime/modules/index.js +++ /dev/null @@ -1,15 +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/index.ts b/src/platforms/web/runtime/modules/index.ts new file mode 100644 index 00000000000..f9b21c2da2a --- /dev/null +++ b/src/platforms/web/runtime/modules/index.ts @@ -0,0 +1,8 @@ +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.js b/src/platforms/web/runtime/modules/style.js deleted file mode 100644 index 29e1c3c1e6e..00000000000 --- a/src/platforms/web/runtime/modules/style.js +++ /dev/null @@ -1,93 +0,0 @@ -/* @flow */ - -import { getStyle, normalizeStyleBinding } from 'web/util/style' -import { cached, camelize, extend, isDef, isUndef } from 'shared/util' - -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(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] - if (cur !== oldStyle[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/style.ts b/src/platforms/web/runtime/modules/style.ts new file mode 100644 index 00000000000..13898eabfd1 --- /dev/null +++ b/src/platforms/web/runtime/modules/style.ts @@ -0,0 +1,102 @@ +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.js b/src/platforms/web/runtime/modules/transition.js deleted file mode 100644 index 9ff1c1d2f70..00000000000 --- a/src/platforms/web/runtime/modules/transition.js +++ /dev/null @@ -1,339 +0,0 @@ -/* @flow */ - -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 -} from 'shared/util' - -import { - nextFrame, - resolveTransition, - whenTransitionEnds, - addTransitionClass, - removeTransitionClass -} from '../transition-util' - -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) { - transitionNode = transitionNode.parent - context = transitionNode.context - } - - 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 - ? (typeof appear === 'function' ? 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 (process.env.NODE_ENV !== 'production' && 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) - } - 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(() => { - addTransitionClass(el, toClass) - removeTransitionClass(el, startClass) - if (!cb.cancelled && !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)) { - return rm() - } - - /* istanbul ignore if */ - if (isDef(el._leaveCb) || el.nodeType !== 1) { - 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 (process.env.NODE_ENV !== 'production' && 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) - } - 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 - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key: any)] = vnode - } - beforeLeave && beforeLeave(el) - if (expectsCSS) { - addTransitionClass(el, leaveClass) - addTransitionClass(el, leaveActiveClass) - nextFrame(() => { - addTransitionClass(el, leaveToClass) - removeTransitionClass(el, leaveClass) - if (!cb.cancelled && !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 - } - const invokerFns = fn.fns - if (isDef(invokerFns)) { - // invoker - return getHookArgumentsLength( - Array.isArray(invokerFns) - ? invokerFns[0] - : invokerFns - ) - } else { - 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) { - leave(vnode, rm) - } else { - rm() - } - } -} : {} diff --git a/src/platforms/web/runtime/modules/transition.ts b/src/platforms/web/runtime/modules/transition.ts new file mode 100644 index 00000000000..ba38cf1a316 --- /dev/null +++ b/src/platforms/web/runtime/modules/transition.ts @@ -0,0 +1,341 @@ +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.js b/src/platforms/web/runtime/node-ops.js deleted file mode 100644 index 1516d86bee5..00000000000 --- a/src/platforms/web/runtime/node-ops.js +++ /dev/null @@ -1,59 +0,0 @@ -/* @flow */ - -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): ?Node { - return node.parentNode -} - -export function nextSibling (node: 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 setAttribute (node: Element, key: string, val: string) { - node.setAttribute(key, val) -} diff --git a/src/platforms/web/runtime/node-ops.ts b/src/platforms/web/runtime/node-ops.ts new file mode 100644 index 00000000000..fed3abc8cb2 --- /dev/null +++ b/src/platforms/web/runtime/node-ops.ts @@ -0,0 +1,66 @@ +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.js b/src/platforms/web/runtime/patch.js deleted file mode 100644 index d982afe92f6..00000000000 --- a/src/platforms/web/runtime/patch.js +++ /dev/null @@ -1,12 +0,0 @@ -/* @flow */ - -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/patch.ts b/src/platforms/web/runtime/patch.ts new file mode 100644 index 00000000000..a56e67f0ce0 --- /dev/null +++ b/src/platforms/web/runtime/patch.ts @@ -0,0 +1,10 @@ +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.js b/src/platforms/web/runtime/transition-util.js deleted file mode 100644 index 47668f0c37d..00000000000 --- a/src/platforms/web/runtime/transition-util.js +++ /dev/null @@ -1,185 +0,0 @@ -/* @flow */ - -import { inBrowser, isIE9 } from 'core/util/index' -import { addClass, removeClass } from './class-util' -import { remove, extend, cached } from 'shared/util' - -export function resolveTransition (def?: string | Object): ?Object { - 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(() => { - 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, - 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; - propCount: number; - timeout: number; - hasTransform: boolean; -} { - const styles: any = window.getComputedStyle(el) - 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 - 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]) - })) -} - -function toMs (s: string): number { - return Number(s.slice(0, -1)) * 1000 -} diff --git a/src/platforms/web/runtime/transition-util.ts b/src/platforms/web/runtime/transition-util.ts new file mode 100644 index 00000000000..6174c6f37e4 --- /dev/null +++ b/src/platforms/web/runtime/transition-util.ts @@ -0,0 +1,215 @@ +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/server/compiler.js b/src/platforms/web/server/compiler.js deleted file mode 100644 index 20e57367708..00000000000 --- a/src/platforms/web/server/compiler.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -import { baseOptions } from '../compiler/options' -import { createCompiler } from 'server/optimizing-compiler/index' - -const { compile, compileToFunctions } = createCompiler(baseOptions) - -export { - compile as ssrCompile, - compileToFunctions as ssrCompileToFunctions -} diff --git a/src/platforms/web/server/directives/model.js b/src/platforms/web/server/directives/model.js deleted file mode 100644 index 7962d6c8ac1..00000000000 --- a/src/platforms/web/server/directives/model.js +++ /dev/null @@ -1,44 +0,0 @@ -/* @flow */ - -import { looseEqual, looseIndexOf } from 'shared/util' - -// 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/src/platforms/web/server/directives/show.js b/src/platforms/web/server/directives/show.js deleted file mode 100644 index 5473fb2bd4d..00000000000 --- a/src/platforms/web/server/directives/show.js +++ /dev/null @@ -1,8 +0,0 @@ -/* @flow */ - -export default function show (node: VNodeWithData, dir: VNodeDirective) { - if (!dir.value) { - const style: any = node.data.style || (node.data.style = {}) - style.display = 'none' - } -} diff --git a/src/platforms/web/server/modules/attrs.js b/src/platforms/web/server/modules/attrs.js deleted file mode 100644 index 9e7cc06160b..00000000000 --- a/src/platforms/web/server/modules/attrs.js +++ /dev/null @@ -1,57 +0,0 @@ -/* @flow */ - -import { escape } from '../util' - -import { - isDef, - isUndef, - extend -} from 'shared/util' - -import { - isBooleanAttr, - isEnumeratedAttr, - isFalsyAttrValue -} from 'web/util/attrs' - -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)) { - 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 (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}="${isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'}"` - } else if (!isFalsyAttrValue(value)) { - return ` ${key}="${escape(String(value))}"` - } - return '' -} diff --git a/src/platforms/web/server/modules/class.js b/src/platforms/web/server/modules/class.js deleted file mode 100644 index c19040ed9f4..00000000000 --- a/src/platforms/web/server/modules/class.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -import { escape } from '../util' -import { genClassForVnode } from 'web/util/index' - -export default function renderClass (node: VNodeWithData): ?string { - const classList = genClassForVnode(node) - if (classList !== '') { - return ` class="${escape(classList)}"` - } -} diff --git a/src/platforms/web/server/modules/dom-props.js b/src/platforms/web/server/modules/dom-props.js deleted file mode 100644 index 57c18eefc89..00000000000 --- a/src/platforms/web/server/modules/dom-props.js +++ /dev/null @@ -1,50 +0,0 @@ -/* @flow */ - -import VNode from 'core/vdom/vnode' -import { renderAttr } from './attrs' -import { isDef, isUndef, extend } from 'shared/util' -import { propsToAttrMap, isRenderableAttr } from '../util' - -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, 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/src/platforms/web/server/modules/index.js b/src/platforms/web/server/modules/index.js deleted file mode 100644 index 3d15bc509e7..00000000000 --- a/src/platforms/web/server/modules/index.js +++ /dev/null @@ -1,11 +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/src/platforms/web/server/modules/style.js b/src/platforms/web/server/modules/style.js deleted file mode 100644 index fc043ef3409..00000000000 --- a/src/platforms/web/server/modules/style.js +++ /dev/null @@ -1,28 +0,0 @@ -/* @flow */ - -import { escape } from '../util' -import { hyphenate } from 'shared/util' -import { getStyle } from 'web/util/style' - -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 += `${hyphenatedKey}:${value[i]};` - } - } else { - styleText += `${hyphenatedKey}:${value};` - } - } - return styleText -} - -export default function renderStyle (vnode: VNodeWithData): ?string { - const styleText = genStyle(getStyle(vnode, false)) - if (styleText !== '') { - return ` style=${JSON.stringify(escape(styleText))}` - } -} diff --git a/src/platforms/web/server/util.js b/src/platforms/web/server/util.js deleted file mode 100644 index 774686dae6d..00000000000 --- a/src/platforms/web/server/util.js +++ /dev/null @@ -1,51 +0,0 @@ -/* @flow */ - -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,http-equiv,' + - 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + - 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,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,type,usemap,value,width,wrap' -) - -/* 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 = { - '<': '<', - '>': '>', - '"': '"', - '&': '&' -} - -export function escape (s: string) { - return s.replace(/[<>"&]/g, escapeChar) -} - -function escapeChar (a) { - return ESC[a] || a -} diff --git a/src/platforms/web/util/attrs.js b/src/platforms/web/util/attrs.js deleted file mode 100644 index 59517da9add..00000000000 --- a/src/platforms/web/util/attrs.js +++ /dev/null @@ -1,43 +0,0 @@ -/* @flow */ - -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, 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') - -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,translate,' + - '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/attrs.ts b/src/platforms/web/util/attrs.ts new file mode 100644 index 00000000000..e520c33d09b --- /dev/null +++ b/src/platforms/web/util/attrs.ts @@ -0,0 +1,58 @@ +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.js b/src/platforms/web/util/class.js deleted file mode 100644 index 1da7ef23276..00000000000 --- a/src/platforms/web/util/class.js +++ /dev/null @@ -1,85 +0,0 @@ -/* @flow */ - -import { isDef, isObject } from 'shared/util' - -export function genClassForVnode (vnode: VNode): string { - let data = vnode.data - let parentNode = vnode - let childNode = vnode - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode - if (childNode.data) { - data = mergeClassData(childNode.data, data) - } - } - while (isDef(parentNode = parentNode.parent)) { - if (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, - dynamicClass: any -): string { - if (isDef(staticClass) || isDef(dynamicClass)) { - return concat(staticClass, stringifyClass(dynamicClass)) - } - /* istanbul ignore next */ - return '' -} - -export function concat (a: ?string, b: ?string): 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/class.ts b/src/platforms/web/util/class.ts new file mode 100644 index 00000000000..e2afdd71689 --- /dev/null +++ b/src/platforms/web/util/class.ts @@ -0,0 +1,87 @@ +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.js b/src/platforms/web/util/compat.js deleted file mode 100644 index d95759cce31..00000000000 --- a/src/platforms/web/util/compat.js +++ /dev/null @@ -1,16 +0,0 @@ -/* @flow */ - -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(' ') > 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/compat.ts b/src/platforms/web/util/compat.ts new file mode 100644 index 00000000000..eaa0b87aaa9 --- /dev/null +++ b/src/platforms/web/util/compat.ts @@ -0,0 +1,16 @@ +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(' ') > 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.js b/src/platforms/web/util/element.js deleted file mode 100644 index 65f1aafbbf2..00000000000 --- a/src/platforms/web/util/element.js +++ /dev/null @@ -1,77 +0,0 @@ -/* @flow */ - -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 => { - return isHTMLTag(tag) || isSVG(tag) -} - -export function getTagNamespace (tag: string): ?string { - 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) { - // http://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/element.ts b/src/platforms/web/util/element.ts new file mode 100644 index 00000000000..f1df261a355 --- /dev/null +++ b/src/platforms/web/util/element.ts @@ -0,0 +1,76 @@ +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.js b/src/platforms/web/util/index.js deleted file mode 100644 index da5e46ed2ff..00000000000 --- a/src/platforms/web/util/index.js +++ /dev/null @@ -1,25 +0,0 @@ -/* @flow */ - -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) { - process.env.NODE_ENV !== 'production' && warn( - 'Cannot find element: ' + el - ) - return document.createElement('div') - } - return selected - } else { - return el - } -} diff --git a/src/platforms/web/util/index.ts b/src/platforms/web/util/index.ts new file mode 100644 index 00000000000..e02a0dc955e --- /dev/null +++ b/src/platforms/web/util/index.ts @@ -0,0 +1,21 @@ +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.js b/src/platforms/web/util/style.js deleted file mode 100644 index 7b0663736e5..00000000000 --- a/src/platforms/web/util/style.js +++ /dev/null @@ -1,69 +0,0 @@ -/* @flow */ - -import { cached, extend, toObject } from 'shared/util' - -export const parseStyleText = cached(function (cssText) { - const res = {} - const listDelimiter = /;(?![^(]*\))/g - const propertyDelimiter = /:(.+)/ - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - var 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): ?Object { - 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): ?Object { - 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: VNode, checkChild: boolean): Object { - const res = {} - let styleData - - if (checkChild) { - let childNode = vnode - while (childNode.componentInstance) { - childNode = childNode.componentInstance._vnode - if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { - extend(res, styleData) - } - } - } - - if ((styleData = normalizeStyleData(vnode.data))) { - extend(res, styleData) - } - - let parentNode = vnode - while ((parentNode = parentNode.parent)) { - if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { - extend(res, styleData) - } - } - return res -} - diff --git a/src/platforms/web/util/style.ts b/src/platforms/web/util/style.ts new file mode 100644 index 00000000000..25971ce3f10 --- /dev/null +++ b/src/platforms/web/util/style.ts @@ -0,0 +1,71 @@ +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/platforms/weex/compiler/directives/index.js b/src/platforms/weex/compiler/directives/index.js deleted file mode 100755 index 12ced224ab7..00000000000 --- a/src/platforms/weex/compiler/directives/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import model from './model' - -export default { - model -} diff --git a/src/platforms/weex/compiler/directives/model.js b/src/platforms/weex/compiler/directives/model.js deleted file mode 100644 index 6c9b7685ada..00000000000 --- a/src/platforms/weex/compiler/directives/model.js +++ /dev/null @@ -1,34 +0,0 @@ -/* @flow */ - -import { addHandler, addAttr } from 'compiler/helpers' -import { genComponentModel, genAssignmentCode } from 'compiler/directives/model' - -export default function model ( - el: ASTElement, - dir: ASTDirective, - _warn: Function -): ?boolean { - if (el.tag === 'input' || el.tag === 'textarea') { - genDefaultModel(el, dir.value, dir.modifiers) - } else { - genComponentModel(el, dir.value, dir.modifiers) - } -} - -function genDefaultModel ( - el: ASTElement, - value: string, - modifiers: ?ASTModifiers -): ?boolean { - const { lazy, trim, number } = modifiers || {} - const event = lazy ? 'change' : 'input' - - let valueExpression = `$event.target.attr.value${trim ? '.trim()' : ''}` - if (number) { - valueExpression = `_n(${valueExpression})` - } - - const code = genAssignmentCode(value, valueExpression) - addAttr(el, 'value', `(${value})`) - addHandler(el, event, code, null, true) -} diff --git a/src/platforms/weex/compiler/index.js b/src/platforms/weex/compiler/index.js deleted file mode 100644 index cbbc6381c71..00000000000 --- a/src/platforms/weex/compiler/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* @flow */ - -import { genStaticKeys } from 'shared/util' -import { createCompiler } from 'compiler/index' - -import modules from './modules/index' -import directives from './directives/index' - -import { - isUnaryTag, - mustUseProp, - isReservedTag, - canBeLeftOpenTag, - getTagNamespace -} from '../util/index' - -export const baseOptions: CompilerOptions = { - modules, - directives, - isUnaryTag, - mustUseProp, - canBeLeftOpenTag, - isReservedTag, - getTagNamespace, - preserveWhitespace: false, - staticKeys: genStaticKeys(modules) -} - -const { compile, compileToFunctions } = createCompiler(baseOptions) -export { compile, compileToFunctions } diff --git a/src/platforms/weex/compiler/modules/append.js b/src/platforms/weex/compiler/modules/append.js deleted file mode 100644 index 71a1a816018..00000000000 --- a/src/platforms/weex/compiler/modules/append.js +++ /dev/null @@ -1,21 +0,0 @@ -/* @flow */ - -function preTransformNode (el: ASTElement, options: CompilerOptions) { - if (el.tag === 'cell' && !el.attrsList.some(item => item.name === 'append')) { - el.attrsMap.append = 'tree' - el.attrsList.push({ name: 'append', value: 'tree' }) - } - if (el.attrsMap.append === 'tree') { - el.appendAsTree = true - } -} - -function genData (el: ASTElement): string { - return el.appendAsTree ? `appendAsTree:true,` : '' -} - -export default { - staticKeys: ['appendAsTree'], - preTransformNode, - genData -} diff --git a/src/platforms/weex/compiler/modules/class.js b/src/platforms/weex/compiler/modules/class.js deleted file mode 100644 index fe910fd6573..00000000000 --- a/src/platforms/weex/compiler/modules/class.js +++ /dev/null @@ -1,73 +0,0 @@ -/* @flow */ - -import { parseText } from 'compiler/parser/text-parser' -import { - getAndRemoveAttr, - getBindingAttr, - baseWarn -} from 'compiler/helpers' - -type StaticClassResult = { - dynamic: boolean, - classResult: string -}; - -function transformNode (el: ASTElement, options: CompilerOptions) { - const warn = options.warn || baseWarn - const staticClass = getAndRemoveAttr(el, 'class') - const { dynamic, classResult } = parseStaticClass(staticClass, options) - if (process.env.NODE_ENV !== 'production' && dynamic && staticClass) { - warn( - `class="${staticClass}": ` + - 'Interpolation inside attributes has been deprecated. ' + - 'Use v-bind or the colon shorthand instead.' - ) - } - if (!dynamic && classResult) { - el.staticClass = classResult - } - const classBinding = getBindingAttr(el, 'class', false /* getStatic */) - if (classBinding) { - el.classBinding = classBinding - } else if (dynamic) { - el.classBinding = classResult - } -} - -function genData (el: ASTElement): string { - let data = '' - if (el.staticClass) { - data += `staticClass:${el.staticClass},` - } - if (el.classBinding) { - data += `class:${el.classBinding},` - } - return data -} - -function parseStaticClass (staticClass: ?string, options: CompilerOptions): StaticClassResult { - // "a b c" -> ["a", "b", "c"] => staticClass: ["a", "b", "c"] - // "a {{x}} c" -> ["a", x, "c"] => classBinding: '["a", x, "c"]' - let dynamic = false - let classResult = '' - if (staticClass) { - const classList = staticClass.trim().split(' ').map(name => { - const result = parseText(name, options.delimiters) - if (result) { - dynamic = true - return result - } - return JSON.stringify(name) - }) - if (classList.length) { - classResult = '[' + classList.join(',') + ']' - } - } - return { dynamic, classResult } -} - -export default { - staticKeys: ['staticClass'], - transformNode, - genData -} diff --git a/src/platforms/weex/compiler/modules/index.js b/src/platforms/weex/compiler/modules/index.js deleted file mode 100644 index ad7879165ae..00000000000 --- a/src/platforms/weex/compiler/modules/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import klass from './class' -import style from './style' -import props from './props' -import append from './append' - -export default [ - klass, - style, - props, - append -] diff --git a/src/platforms/weex/compiler/modules/props.js b/src/platforms/weex/compiler/modules/props.js deleted file mode 100644 index 8b0bb5f2e69..00000000000 --- a/src/platforms/weex/compiler/modules/props.js +++ /dev/null @@ -1,32 +0,0 @@ -/* @flow */ - -import { cached, camelize } from 'shared/util' - -const normalize = cached(camelize) - -function normalizeKeyName (str: string): string { - if (str.match(/^v\-/)) { - return str.replace(/(v-[a-z\-]+\:)([a-z\-]+)$/i, ($, directive, prop) => { - return directive + normalize(prop) - }) - } - return normalize(str) -} - -function transformNode (el: ASTElement, options: CompilerOptions) { - if (Array.isArray(el.attrsList)) { - el.attrsList.forEach(attr => { - if (attr.name && attr.name.match(/\-/)) { - const realName = normalizeKeyName(attr.name) - if (el.attrsMap) { - el.attrsMap[realName] = el.attrsMap[attr.name] - delete el.attrsMap[attr.name] - } - attr.name = realName - } - }) - } -} -export default { - transformNode -} diff --git a/src/platforms/weex/compiler/modules/style.js b/src/platforms/weex/compiler/modules/style.js deleted file mode 100644 index 7c428091e8a..00000000000 --- a/src/platforms/weex/compiler/modules/style.js +++ /dev/null @@ -1,82 +0,0 @@ -/* @flow */ - -import { cached, camelize } from 'shared/util' -import { parseText } from 'compiler/parser/text-parser' -import { - getAndRemoveAttr, - getBindingAttr, - baseWarn -} from 'compiler/helpers' - -type StaticStyleResult = { - dynamic: boolean, - styleResult: string -}; - -const normalize = cached(camelize) - -function transformNode (el: ASTElement, options: CompilerOptions) { - const warn = options.warn || baseWarn - const staticStyle = getAndRemoveAttr(el, 'style') - const { dynamic, styleResult } = parseStaticStyle(staticStyle, options) - if (process.env.NODE_ENV !== 'production' && dynamic) { - warn( - `style="${String(staticStyle)}": ` + - 'Interpolation inside attributes has been deprecated. ' + - 'Use v-bind or the colon shorthand instead.' - ) - } - if (!dynamic && styleResult) { - el.staticStyle = styleResult - } - const styleBinding = getBindingAttr(el, 'style', false /* getStatic */) - if (styleBinding) { - el.styleBinding = styleBinding - } else if (dynamic) { - el.styleBinding = styleResult - } -} - -function genData (el: ASTElement): string { - let data = '' - if (el.staticStyle) { - data += `staticStyle:${el.staticStyle},` - } - if (el.styleBinding) { - data += `style:${el.styleBinding},` - } - return data -} - -function parseStaticStyle (staticStyle: ?string, options: CompilerOptions): StaticStyleResult { - // "width: 200px; height: 200px;" -> {width: 200, height: 200} - // "width: 200px; height: {{y}}" -> {width: 200, height: y} - let dynamic = false - let styleResult = '' - if (staticStyle) { - const styleList = staticStyle.trim().split(';').map(style => { - const result = style.trim().split(':') - if (result.length !== 2) { - return - } - const key = normalize(result[0].trim()) - const value = result[1].trim() - const dynamicValue = parseText(value, options.delimiters) - if (dynamicValue) { - dynamic = true - return key + ':' + dynamicValue - } - return key + ':' + JSON.stringify(value) - }).filter(result => result) - if (styleList.length) { - styleResult = '{' + styleList.join(',') + '}' - } - } - return { dynamic, styleResult } -} - -export default { - staticKeys: ['staticStyle'], - transformNode, - genData -} diff --git a/src/platforms/weex/entry-compiler.js b/src/platforms/weex/entry-compiler.js deleted file mode 100644 index 18d79ab742f..00000000000 --- a/src/platforms/weex/entry-compiler.js +++ /dev/null @@ -1 +0,0 @@ -export { compile } from 'weex/compiler/index' diff --git a/src/platforms/weex/entry-framework.js b/src/platforms/weex/entry-framework.js deleted file mode 100644 index 9b7c920cfbd..00000000000 --- a/src/platforms/weex/entry-framework.js +++ /dev/null @@ -1,284 +0,0 @@ -// this will be preserved during build -const VueFactory = require('./factory') - -const instances = {} - -/** - * Prepare framework config. - * Nothing need to do actually, just an interface provided to weex runtime. - */ -export function init () {} - -/** - * Reset framework config and clear all registrations. - */ -export function reset () { - clear(instances) -} - -/** - * Delete all keys of an object. - * @param {object} obj - */ -function clear (obj) { - for (const key in obj) { - delete obj[key] - } -} - -/** - * Create an instance with id, code, config and external data. - * @param {string} instanceId - * @param {string} appCode - * @param {object} config - * @param {object} data - * @param {object} env { info, config, services } - */ -export function createInstance ( - instanceId, - appCode = '', - config = {}, - data, - env = {} -) { - const weex = env.weex - const document = weex.document - const instance = instances[instanceId] = { - instanceId, config, data, - document - } - - const timerAPIs = getInstanceTimer(instanceId, weex.requireModule) - - // Each instance has a independent `Vue` module instance - const Vue = instance.Vue = createVueModuleInstance(instanceId, weex) - - // The function which create a closure the JS Bundle will run in. - // It will declare some instance variables like `Vue`, HTML5 Timer APIs etc. - const instanceVars = Object.assign({ - Vue, - weex - }, timerAPIs, env.services) - - appCode = `(function(global){ \n${appCode}\n })(Object.create(this))` - - callFunction(instanceVars, appCode) - - // Send `createFinish` signal to native. - document.taskCenter.send('dom', { action: 'createFinish' }, []) - - return instance -} - -/** - * Destroy an instance with id. It will make sure all memory of - * this instance released and no more leaks. - * @param {string} instanceId - */ -export function destroyInstance (instanceId) { - const instance = instances[instanceId] - if (instance && instance.app instanceof instance.Vue) { - instance.document.destroy() - instance.app.$destroy() - delete instance.document - delete instance.app - } - delete instances[instanceId] -} - -/** - * Refresh an instance with id and new top-level component data. - * It will use `Vue.set` on all keys of the new data. So it's better - * define all possible meaningful keys when instance created. - * @param {string} instanceId - * @param {object} data - */ -export function refreshInstance (instanceId, data) { - const instance = instances[instanceId] - if (!instance || !(instance.app instanceof instance.Vue)) { - return new Error(`refreshInstance: instance ${instanceId} not found!`) - } - for (const key in data) { - instance.Vue.set(instance.app, key, data[key]) - } - // Finally `refreshFinish` signal needed. - instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, []) -} - -/** - * Get the JSON object of the root element. - * @param {string} instanceId - */ -export function getRoot (instanceId) { - const instance = instances[instanceId] - if (!instance || !(instance.app instanceof instance.Vue)) { - return new Error(`getRoot: instance ${instanceId} not found!`) - } - return instance.app.$el.toJSON() -} - -const jsHandlers = { - fireEvent: (id, ...args) => { - return fireEvent(instances[id], ...args) - }, - callback: (id, ...args) => { - return callback(instances[id], ...args) - } -} - -function fireEvent (instance, nodeId, type, e, domChanges) { - const el = instance.document.getRef(nodeId) - if (el) { - return instance.document.fireEvent(el, type, e, domChanges) - } - return new Error(`invalid element reference "${nodeId}"`) -} - -function callback (instance, callbackId, data, ifKeepAlive) { - const result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive) - instance.document.taskCenter.send('dom', { action: 'updateFinish' }, []) - return result -} - -/** - * Accept calls from native (event or callback). - * - * @param {string} id - * @param {array} tasks list with `method` and `args` - */ -export function receiveTasks (id, tasks) { - const instance = instances[id] - if (instance && Array.isArray(tasks)) { - const results = [] - tasks.forEach((task) => { - const handler = jsHandlers[task.method] - const args = [...task.args] - /* istanbul ignore else */ - if (typeof handler === 'function') { - args.unshift(id) - results.push(handler(...args)) - } - }) - return results - } - return new Error(`invalid instance id "${id}" or tasks`) -} - -/** - * Create a fresh instance of Vue for each Weex instance. - */ -function createVueModuleInstance (instanceId, weex) { - const exports = {} - VueFactory(exports, weex.document) - const Vue = exports.Vue - - const instance = instances[instanceId] - - // patch reserved tag detection to account for dynamically registered - // components - const weexRegex = /^weex:/i - const isReservedTag = Vue.config.isReservedTag || (() => false) - const isRuntimeComponent = Vue.config.isRuntimeComponent || (() => false) - Vue.config.isReservedTag = name => { - return (!isRuntimeComponent(name) && weex.supports(`@component/${name}`)) || - isReservedTag(name) || - weexRegex.test(name) - } - Vue.config.parsePlatformTagName = name => name.replace(weexRegex, '') - - // expose weex-specific info - Vue.prototype.$instanceId = instanceId - Vue.prototype.$document = instance.document - - // expose weex native module getter on subVue prototype so that - // vdom runtime modules can access native modules via vnode.context - Vue.prototype.$requireWeexModule = weex.requireModule - - // Hack `Vue` behavior to handle instance information and data - // before root component created. - Vue.mixin({ - beforeCreate () { - const options = this.$options - // root component (vm) - if (options.el) { - // set external data of instance - const dataOption = options.data - const internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {} - options.data = Object.assign(internalData, instance.data) - // record instance by id - instance.app = this - } - } - }) - - /** - * @deprecated Just instance variable `weex.config` - * Get instance config. - * @return {object} - */ - Vue.prototype.$getConfig = function () { - if (instance.app instanceof Vue) { - return instance.config - } - } - - return Vue -} - -/** - * Generate HTML5 Timer APIs. An important point is that the callback - * will be converted into callback id when sent to native. So the - * framework can make sure no side effect of the callback happened after - * an instance destroyed. - * @param {[type]} instanceId [description] - * @param {[type]} moduleGetter [description] - * @return {[type]} [description] - */ -function getInstanceTimer (instanceId, moduleGetter) { - const instance = instances[instanceId] - const timer = moduleGetter('timer') - const timerAPIs = { - setTimeout: (...args) => { - const handler = function () { - args[0](...args.slice(2)) - } - - timer.setTimeout(handler, args[1]) - return instance.document.taskCenter.callbackManager.lastCallbackId.toString() - }, - setInterval: (...args) => { - const handler = function () { - args[0](...args.slice(2)) - } - - timer.setInterval(handler, args[1]) - return instance.document.taskCenter.callbackManager.lastCallbackId.toString() - }, - clearTimeout: (n) => { - timer.clearTimeout(n) - }, - clearInterval: (n) => { - timer.clearInterval(n) - } - } - return timerAPIs -} - -/** - * Call a new function body with some global objects. - * @param {object} globalObjects - * @param {string} code - * @return {any} - */ -function callFunction (globalObjects, body) { - const globalKeys = [] - const globalValues = [] - for (const key in globalObjects) { - globalKeys.push(key) - globalValues.push(globalObjects[key]) - } - globalKeys.push(body) - - const result = new Function(...globalKeys) - return result(...globalValues) -} diff --git a/src/platforms/weex/entry-runtime-factory.js b/src/platforms/weex/entry-runtime-factory.js deleted file mode 100644 index 166ddf2dae8..00000000000 --- a/src/platforms/weex/entry-runtime-factory.js +++ /dev/null @@ -1,6 +0,0 @@ -// this entry is built and wrapped with a factory function -// used to generate a fresh copy of Vue for every Weex instance. - -import Vue from './runtime/index' - -exports.Vue = Vue diff --git a/src/platforms/weex/runtime/components/index.js b/src/platforms/weex/runtime/components/index.js deleted file mode 100644 index 449db1ad78a..00000000000 --- a/src/platforms/weex/runtime/components/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import Richtext from './richtext' -import Transition from './transition' -import TransitionGroup from './transition-group' - -export default { - Richtext, - Transition, - TransitionGroup -} diff --git a/src/platforms/weex/runtime/components/richtext.js b/src/platforms/weex/runtime/components/richtext.js deleted file mode 100644 index a9164c7a698..00000000000 --- a/src/platforms/weex/runtime/components/richtext.js +++ /dev/null @@ -1,82 +0,0 @@ -/* @flow */ - -function getVNodeType (vnode: VNode): string { - if (!vnode.tag) { - return '' - } - return vnode.tag.replace(/vue\-component\-(\d+\-)?/, '') -} - -function isSimpleSpan (vnode: VNode): boolean { - return vnode.children && - vnode.children.length === 1 && - !vnode.children[0].tag -} - -function parseStyle (vnode: VNode): Object | void { - if (!vnode || !vnode.data) { - return - } - const { staticStyle, staticClass } = vnode.data - if (vnode.data.style || vnode.data.class || staticStyle || staticClass) { - const styles = Object.assign({}, staticStyle, vnode.data.style) - const cssMap = vnode.context.$options.style || {} - const classList = [].concat(staticClass, vnode.data.class) - classList.forEach(name => { - if (name && cssMap[name]) { - Object.assign(styles, cssMap[name]) - } - }) - return styles - } -} - -function convertVNodeChildren (children: Array<VNode>): Array<VNode> | void { - if (!children.length) { - return - } - - return children.map(vnode => { - const type: string = getVNodeType(vnode) - const props: Object = { type } - - // convert raw text node - if (!type) { - props.type = 'span' - props.attr = { - value: (vnode.text || '').trim() - } - } else { - props.style = parseStyle(vnode) - if (vnode.data) { - props.attr = vnode.data.attrs - if (vnode.data.on) { - props.events = vnode.data.on - } - } - if (type === 'span' && isSimpleSpan(vnode)) { - props.attr = props.attr || {} - props.attr.value = vnode.children[0].text.trim() - return props - } - } - - if (vnode.children && vnode.children.length) { - props.children = convertVNodeChildren(vnode.children) - } - - return props - }) -} - -export default { - name: 'richtext', - render (h: Function) { - return h('weex:richtext', { - on: this._events, - attrs: { - value: convertVNodeChildren(this.$options._renderChildren || []) - } - }) - } -} diff --git a/src/platforms/weex/runtime/components/transition-group.js b/src/platforms/weex/runtime/components/transition-group.js deleted file mode 100644 index 4a65965ff55..00000000000 --- a/src/platforms/weex/runtime/components/transition-group.js +++ /dev/null @@ -1,148 +0,0 @@ -import { warn, extend } from 'core/util/index' -import { transitionProps, extractTransitionData } from './transition' - -const props = extend({ - tag: String, - moveClass: String -}, transitionProps) - -delete props.mode - -export default { - props, - - created () { - const dom = this.$requireWeexModule('dom') - this.getPosition = el => new Promise((resolve, reject) => { - dom.getComponentRect(el.ref, res => { - if (!res.result) { - reject(new Error(`failed to get rect for element: ${el.tag}`)) - } else { - resolve(res.size) - } - }) - }) - - const animation = this.$requireWeexModule('animation') - this.animate = (el, options) => new Promise(resolve => { - animation.transition(el.ref, options, resolve) - }) - }, - - render (h) { - const tag = this.tag || this.$vnode.data.tag || 'span' - const map = Object.create(null) - const prevChildren = this.prevChildren = this.children - const rawChildren = this.$slots.default || [] - const children = this.children = [] - const transitionData = extractTransitionData(this) - - for (let i = 0; i < rawChildren.length; i++) { - const c = 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 (process.env.NODE_ENV !== 'production') { - const opts = c.componentOptions - const name = opts - ? (opts.Ctor.options.name || opts.tag) - : c.tag - warn(`<transition-group> children must be keyed: <${name}>`) - } - } - } - - if (prevChildren) { - const kept = [] - const removed = [] - prevChildren.forEach(c => { - c.data.transition = transitionData - - // TODO: record before patch positions - - 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) - }, - - beforeUpdate () { - // force removing pass - this.__patch__( - this._vnode, - this.kept, - false, // hydrating - true // removeOnly (!important, avoids unnecessary moves) - ) - this._vnode = this.kept - }, - - updated () { - const children = this.prevChildren - const moveClass = this.moveClass || ((this.name || 'v') + '-move') - const moveData = children.length && this.getMoveData(children[0].context, moveClass) - if (!moveData) { - return - } - - // TODO: finish implementing move animations once - // we have access to sync getComponentRect() - - // children.forEach(callPendingCbs) - - // Promise.all(children.map(c => { - // 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 - // return this.animate(c.elm, { - // styles: { - // transform: `translate(${dx}px,${dy}px)` - // } - // }) - // } - // })).then(() => { - // children.forEach(c => { - // if (c.data.moved) { - // this.animate(c.elm, { - // styles: { - // transform: '' - // }, - // duration: moveData.duration || 0, - // delay: moveData.delay || 0, - // timingFunction: moveData.timingFunction || 'linear' - // }) - // } - // }) - // }) - }, - - methods: { - getMoveData (context, moveClass) { - const stylesheet = context.$options.style || {} - return stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][moveClass] - } - } -} - -// function callPendingCbs (c) { -// /* istanbul ignore if */ -// if (c.elm._moveCb) { -// c.elm._moveCb() -// } -// /* istanbul ignore if */ -// if (c.elm._enterCb) { -// c.elm._enterCb() -// } -// } diff --git a/src/platforms/weex/runtime/components/transition.js b/src/platforms/weex/runtime/components/transition.js deleted file mode 100644 index 20ba632d036..00000000000 --- a/src/platforms/weex/runtime/components/transition.js +++ /dev/null @@ -1,9 +0,0 @@ -// reuse same transition component logic from web -export { - transitionProps, - extractTransitionData -} from 'web/runtime/components/transition' - -import Transition from 'web/runtime/components/transition' - -export default Transition diff --git a/src/platforms/weex/runtime/directives/index.js b/src/platforms/weex/runtime/directives/index.js deleted file mode 100755 index efba7fa6977..00000000000 --- a/src/platforms/weex/runtime/directives/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export default { -} diff --git a/src/platforms/weex/runtime/index.js b/src/platforms/weex/runtime/index.js deleted file mode 100755 index 812dde9ee22..00000000000 --- a/src/platforms/weex/runtime/index.js +++ /dev/null @@ -1,42 +0,0 @@ -/* @flow */ - -import Vue from 'core/index' -import { patch } from 'weex/runtime/patch' -import { mountComponent } from 'core/instance/lifecycle' -import platformDirectives from 'weex/runtime/directives/index' -import platformComponents from 'weex/runtime/components/index' - -import { - query, - mustUseProp, - isReservedTag, - isRuntimeComponent, - isUnknownElement -} from 'weex/util/index' - -// install platform specific utils -Vue.config.mustUseProp = mustUseProp -Vue.config.isReservedTag = isReservedTag -Vue.config.isRuntimeComponent = isRuntimeComponent -Vue.config.isUnknownElement = isUnknownElement - -// install platform runtime directives and components -Vue.options.directives = platformDirectives -Vue.options.components = platformComponents - -// install platform patch function -Vue.prototype.__patch__ = patch - -// wrap mount -Vue.prototype.$mount = function ( - el?: any, - hydrating?: boolean -): Component { - return mountComponent( - this, - el && query(el, this.$document), - hydrating - ) -} - -export default Vue diff --git a/src/platforms/weex/runtime/modules/attrs.js b/src/platforms/weex/runtime/modules/attrs.js deleted file mode 100755 index 39d532d84d7..00000000000 --- a/src/platforms/weex/runtime/modules/attrs.js +++ /dev/null @@ -1,35 +0,0 @@ -/* @flow */ - -import { extend } from 'shared/util' - -function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) { - if (!oldVnode.data.attrs && !vnode.data.attrs) { - return - } - let key, cur, old - const elm = vnode.elm - const oldAttrs = oldVnode.data.attrs || {} - let attrs = vnode.data.attrs || {} - // clone observed objects, as the user probably wants to mutate it - if (attrs.__ob__) { - attrs = vnode.data.attrs = extend({}, attrs) - } - - for (key in attrs) { - cur = attrs[key] - old = oldAttrs[key] - if (old !== cur) { - elm.setAttr(key, cur) - } - } - for (key in oldAttrs) { - if (attrs[key] == null) { - elm.setAttr(key) - } - } -} - -export default { - create: updateAttrs, - update: updateAttrs -} diff --git a/src/platforms/weex/runtime/modules/class.js b/src/platforms/weex/runtime/modules/class.js deleted file mode 100755 index b1e3b188561..00000000000 --- a/src/platforms/weex/runtime/modules/class.js +++ /dev/null @@ -1,67 +0,0 @@ -/* @flow */ - -import { extend } from 'shared/util' - -function updateClass (oldVnode: VNodeWithData, vnode: VNodeWithData) { - const el = vnode.elm - const ctx = vnode.context - - const data: VNodeData = vnode.data - const oldData: VNodeData = oldVnode.data - if (!data.staticClass && - !data.class && - (!oldData || (!oldData.staticClass && !oldData.class)) - ) { - return - } - - const oldClassList = [] - // unlike web, weex vnode staticClass is an Array - const oldStaticClass: any = oldData.staticClass - if (oldStaticClass) { - oldClassList.push.apply(oldClassList, oldStaticClass) - } - if (oldData.class) { - oldClassList.push.apply(oldClassList, oldData.class) - } - - const classList = [] - // unlike web, weex vnode staticClass is an Array - const staticClass: any = data.staticClass - if (staticClass) { - classList.push.apply(classList, staticClass) - } - if (data.class) { - classList.push.apply(classList, data.class) - } - - const style = getStyle(oldClassList, classList, ctx) - for (const key in style) { - el.setStyle(key, style[key]) - } -} - -function getStyle (oldClassList: Array<string>, classList: Array<string>, ctx: Component): Object { - // style is a weex-only injected object - // compiled from <style> tags in weex files - const stylesheet: any = ctx.$options.style || {} - const result = {} - classList.forEach(name => { - const style = stylesheet[name] - extend(result, style) - }) - oldClassList.forEach(name => { - const style = stylesheet[name] - for (const key in style) { - if (!result.hasOwnProperty(key)) { - result[key] = '' - } - } - }) - return result -} - -export default { - create: updateClass, - update: updateClass -} diff --git a/src/platforms/weex/runtime/modules/events.js b/src/platforms/weex/runtime/modules/events.js deleted file mode 100755 index 11b7edcfec8..00000000000 --- a/src/platforms/weex/runtime/modules/events.js +++ /dev/null @@ -1,55 +0,0 @@ -/* @flow */ - -import { updateListeners } from 'core/vdom/helpers/update-listeners' - -let target: any - -function add ( - event: string, - handler: Function, - once: boolean, - capture: boolean -) { - if (capture) { - console.log('Weex do not support event in bubble phase.') - return - } - if (once) { - const oldHandler = handler - const _target = target // save current target element in closure - handler = function (ev) { - const res = arguments.length === 1 - ? oldHandler(ev) - : oldHandler.apply(null, arguments) - if (res !== null) { - remove(event, null, null, _target) - } - } - } - target.addEvent(event, handler) -} - -function remove ( - event: string, - handler: any, - capture: any, - _target?: any -) { - (_target || target).removeEvent(event) -} - -function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { - if (!oldVnode.data.on && !vnode.data.on) { - return - } - const on = vnode.data.on || {} - const oldOn = oldVnode.data.on || {} - target = vnode.elm - updateListeners(on, oldOn, add, remove, vnode.context) - target = undefined -} - -export default { - create: updateDOMListeners, - update: updateDOMListeners -} diff --git a/src/platforms/weex/runtime/modules/index.js b/src/platforms/weex/runtime/modules/index.js deleted file mode 100755 index dd38448459a..00000000000 --- a/src/platforms/weex/runtime/modules/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import attrs from './attrs' -import klass from './class' -import events from './events' -import style from './style' -import transition from './transition' - -export default [ - attrs, - klass, - events, - style, - transition -] diff --git a/src/platforms/weex/runtime/modules/style.js b/src/platforms/weex/runtime/modules/style.js deleted file mode 100755 index 9c60fd1aaa4..00000000000 --- a/src/platforms/weex/runtime/modules/style.js +++ /dev/null @@ -1,68 +0,0 @@ -/* @flow */ - -import { extend, cached, camelize } from 'shared/util' - -const normalize = cached(camelize) - -function createStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) { - if (!vnode.data.staticStyle) { - updateStyle(oldVnode, vnode) - return - } - const elm = vnode.elm - const staticStyle = vnode.data.staticStyle - for (const name in staticStyle) { - if (staticStyle[name]) { - elm.setStyle(normalize(name), staticStyle[name]) - } - } - updateStyle(oldVnode, vnode) -} - -function updateStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) { - if (!oldVnode.data.style && !vnode.data.style) { - return - } - let cur, name - const elm = vnode.elm - const oldStyle: any = oldVnode.data.style || {} - let style: any = vnode.data.style || {} - - const needClone = style.__ob__ - - // handle array syntax - if (Array.isArray(style)) { - style = vnode.data.style = toObject(style) - } - - // clone the style for future updates, - // in case the user mutates the style object in-place. - if (needClone) { - style = vnode.data.style = extend({}, style) - } - - for (name in oldStyle) { - if (!style[name]) { - elm.setStyle(normalize(name), '') - } - } - for (name in style) { - cur = style[name] - elm.setStyle(normalize(name), cur) - } -} - -function toObject (arr) { - const res = {} - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]) - } - } - return res -} - -export default { - create: createStyle, - update: updateStyle -} diff --git a/src/platforms/weex/runtime/modules/transition.js b/src/platforms/weex/runtime/modules/transition.js deleted file mode 100644 index 86e3f6d9941..00000000000 --- a/src/platforms/weex/runtime/modules/transition.js +++ /dev/null @@ -1,266 +0,0 @@ -import { warn } from 'core/util/debug' -import { extend, once, noop } from 'shared/util' -import { activeInstance } from 'core/instance/lifecycle' -import { resolveTransition } from 'web/runtime/transition-util' - -export default { - create: enter, - activate: enter, - remove: leave -} - -function enter (_, vnode) { - const el = vnode.elm - - // call leave callback now - if (el._leaveCb) { - el._leaveCb.cancelled = true - el._leaveCb() - } - - const data = resolveTransition(vnode.data.transition) - if (!data) { - return - } - - /* istanbul ignore if */ - if (el._enterCb) { - return - } - - const { - enterClass, - enterToClass, - enterActiveClass, - appearClass, - appearToClass, - appearActiveClass, - beforeEnter, - enter, - afterEnter, - enterCancelled, - beforeAppear, - appear, - afterAppear, - appearCancelled - } = data - - let context = activeInstance - let transitionNode = activeInstance.$vnode - while (transitionNode && transitionNode.parent) { - transitionNode = transitionNode.parent - context = transitionNode.context - } - - const isAppear = !context._isMounted || !vnode.isRootInsert - - if (isAppear && !appear && appear !== '') { - return - } - - const startClass = isAppear ? appearClass : enterClass - const toClass = isAppear ? appearToClass : enterToClass - const activeClass = isAppear ? appearActiveClass : enterActiveClass - const beforeEnterHook = isAppear ? (beforeAppear || beforeEnter) : beforeEnter - const enterHook = isAppear ? (typeof appear === 'function' ? appear : enter) : enter - const afterEnterHook = isAppear ? (afterAppear || afterEnter) : afterEnter - const enterCancelledHook = isAppear ? (appearCancelled || enterCancelled) : enterCancelled - - const userWantsControl = - enterHook && - // enterHook may be a bound method which exposes - // the length of original fn as _length - (enterHook._length || enterHook.length) > 1 - - const stylesheet = vnode.context.$options.style || {} - const startState = stylesheet[startClass] - const transitionProperties = (stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][activeClass]) || {} - const endState = getEnterTargetState(el, stylesheet, startClass, toClass, activeClass, vnode.context) - const needAnimation = Object.keys(endState).length > 0 - - const cb = el._enterCb = once(() => { - if (cb.cancelled) { - enterCancelledHook && enterCancelledHook(el) - } else { - afterEnterHook && afterEnterHook(el) - } - el._enterCb = null - }) - - // We need to wait until the native element has been inserted, but currently - // there's no API to do that. So we have to wait "one frame" - not entirely - // sure if this is guaranteed to be enough (e.g. on slow devices?) - setTimeout(() => { - const parent = el.parentNode - const pendingNode = parent && parent._pending && parent._pending[vnode.key] - if (pendingNode && - pendingNode.context === vnode.context && - pendingNode.tag === vnode.tag && - pendingNode.elm._leaveCb - ) { - pendingNode.elm._leaveCb() - } - enterHook && enterHook(el, cb) - - if (needAnimation) { - const animation = vnode.context.$requireWeexModule('animation') - animation.transition(el.ref, { - styles: endState, - duration: transitionProperties.duration || 0, - delay: transitionProperties.delay || 0, - timingFunction: transitionProperties.timingFunction || 'linear' - }, userWantsControl ? noop : cb) - } else if (!userWantsControl) { - cb() - } - }, 16) - - // start enter transition - beforeEnterHook && beforeEnterHook(el) - - if (startState) { - for (const key in startState) { - el.setStyle(key, startState[key]) - } - } - - if (!needAnimation && !userWantsControl) { - cb() - } -} - -function leave (vnode, rm) { - const el = vnode.elm - - // call enter callback now - if (el._enterCb) { - el._enterCb.cancelled = true - el._enterCb() - } - - const data = resolveTransition(vnode.data.transition) - if (!data) { - return rm() - } - - if (el._leaveCb) { - return - } - - const { - leaveClass, - leaveToClass, - leaveActiveClass, - beforeLeave, - leave, - afterLeave, - leaveCancelled, - delayLeave - } = data - - const userWantsControl = - leave && - // leave hook may be a bound method which exposes - // the length of original fn as _length - (leave._length || leave.length) > 1 - - const stylesheet = vnode.context.$options.style || {} - const startState = stylesheet[leaveClass] - const endState = stylesheet[leaveToClass] || stylesheet[leaveActiveClass] - const transitionProperties = (stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][leaveActiveClass]) || {} - - const cb = el._leaveCb = once(() => { - if (el.parentNode && el.parentNode._pending) { - el.parentNode._pending[vnode.key] = null - } - if (cb.cancelled) { - leaveCancelled && leaveCancelled(el) - } else { - rm() - afterLeave && afterLeave(el) - } - el._leaveCb = null - }) - - if (delayLeave) { - delayLeave(performLeave) - } else { - performLeave() - } - - function performLeave () { - const animation = vnode.context.$requireWeexModule('animation') - // the delayed leave may have already been cancelled - if (cb.cancelled) { - return - } - // record leaving element - if (!vnode.data.show) { - (el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key] = vnode - } - beforeLeave && beforeLeave(el) - - if (startState) { - animation.transition(el.ref, { - styles: startState - }, next) - } else { - next() - } - - function next () { - animation.transition(el.ref, { - styles: endState, - duration: transitionProperties.duration || 0, - delay: transitionProperties.delay || 0, - timingFunction: transitionProperties.timingFunction || 'linear' - }, userWantsControl ? noop : cb) - } - - leave && leave(el, cb) - if (!endState && !userWantsControl) { - cb() - } - } -} - -// determine the target animation style for an entering transition. -function getEnterTargetState (el, stylesheet, startClass, endClass, activeClass, vm) { - const targetState = {} - const startState = stylesheet[startClass] - const endState = stylesheet[endClass] - const activeState = stylesheet[activeClass] - // 1. fallback to element's default styling - if (startState) { - for (const key in startState) { - targetState[key] = el.style[key] - if ( - process.env.NODE_ENV !== 'production' && - targetState[key] == null && - (!activeState || activeState[key] == null) && - (!endState || endState[key] == null) - ) { - warn( - `transition property "${key}" is declared in enter starting class (.${startClass}), ` + - `but not declared anywhere in enter ending class (.${endClass}), ` + - `enter active cass (.${activeClass}) or the element's default styling. ` + - `Note in Weex, CSS properties need explicit values to be transitionable.` - ) - } - } - } - // 2. if state is mixed in active state, extract them while excluding - // transition properties - if (activeState) { - for (const key in activeState) { - if (key.indexOf('transition') !== 0) { - targetState[key] = activeState[key] - } - } - } - // 3. explicit endState has highest priority - if (endState) { - extend(targetState, endState) - } - return targetState -} diff --git a/src/platforms/weex/runtime/node-ops.js b/src/platforms/weex/runtime/node-ops.js deleted file mode 100755 index 303c81945ee..00000000000 --- a/src/platforms/weex/runtime/node-ops.js +++ /dev/null @@ -1,81 +0,0 @@ -/* globals document */ -// document is injected by weex factory wrapper - -import TextNode from 'weex/runtime/text-node' - -export const namespaceMap = {} - -export function createElement (tagName) { - return document.createElement(tagName) -} - -export function createElementNS (namespace, tagName) { - return document.createElement(namespace + ':' + tagName) -} - -export function createTextNode (text) { - return new TextNode(text) -} - -export function createComment (text) { - return document.createComment(text) -} - -export function insertBefore (node, target, before) { - if (target.nodeType === 3) { - if (node.type === 'text') { - node.setAttr('value', target.text) - target.parentNode = node - } else { - const text = createElement('text') - text.setAttr('value', target.text) - node.insertBefore(text, before) - } - return - } - node.insertBefore(target, before) -} - -export function removeChild (node, child) { - if (child.nodeType === 3) { - node.setAttr('value', '') - return - } - node.removeChild(child) -} - -export function appendChild (node, child) { - if (child.nodeType === 3) { - if (node.type === 'text') { - node.setAttr('value', child.text) - child.parentNode = node - } else { - const text = createElement('text') - text.setAttr('value', child.text) - node.appendChild(text) - } - return - } - - node.appendChild(child) -} - -export function parentNode (node) { - return node.parentNode -} - -export function nextSibling (node) { - return node.nextSibling -} - -export function tagName (node) { - return node.type -} - -export function setTextContent (node, text) { - node.parentNode.setAttr('value', text) -} - -export function setAttribute (node, key, val) { - node.setAttr(key, val) -} diff --git a/src/platforms/weex/runtime/patch.js b/src/platforms/weex/runtime/patch.js deleted file mode 100644 index 5e70d1436fd..00000000000 --- a/src/platforms/weex/runtime/patch.js +++ /dev/null @@ -1,16 +0,0 @@ -/* @flow */ - -import * as nodeOps from 'weex/runtime/node-ops' -import { createPatchFunction } from 'core/vdom/patch' -import baseModules from 'core/vdom/modules/index' -import platformModules from 'weex/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, - LONG_LIST_THRESHOLD: 10 -}) diff --git a/src/platforms/weex/runtime/text-node.js b/src/platforms/weex/runtime/text-node.js deleted file mode 100644 index 0720a05a5a6..00000000000 --- a/src/platforms/weex/runtime/text-node.js +++ /dev/null @@ -1,9 +0,0 @@ -let latestNodeId = 1 - -export default function TextNode (text) { - this.instanceId = '' - this.nodeId = latestNodeId++ - this.parentNode = null - this.nodeType = 3 - this.text = text -} diff --git a/src/platforms/weex/util/index.js b/src/platforms/weex/util/index.js deleted file mode 100755 index 05da30e985e..00000000000 --- a/src/platforms/weex/util/index.js +++ /dev/null @@ -1,42 +0,0 @@ -/* globals document */ - -import { makeMap } from 'shared/util' - -export const isReservedTag = makeMap( - 'template,script,style,element,content,slot,link,meta,svg,view,' + - 'a,div,img,image,text,span,input,switch,textarea,spinner,select,' + - 'slider,slider-neighbor,indicator,canvas,' + - 'list,cell,header,loading,loading-indicator,refresh,scrollable,scroller,' + - 'video,web,embed,tabbar,tabheader,datepicker,timepicker,marquee,countdown', - true -) - -// Elements that you can, intentionally, leave open (and which close themselves) -// more flexible than web -export const canBeLeftOpenTag = makeMap( - 'web,spinner,switch,video,textarea,canvas,' + - 'indicator,marquee,countdown', - true -) - -export const isRuntimeComponent = makeMap( - 'richtext,trisition,trisition-group', - true -) - -export const isUnaryTag = makeMap( - 'embed,img,image,input,link,meta', - true -) - -export function mustUseProp () { /* console.log('mustUseProp') */ } -export function getTagNamespace () { /* console.log('getTagNamespace') */ } -export function isUnknownElement () { /* console.log('isUnknownElement') */ } - -export function query (el, document) { - // document is injected by weex factory wrapper - const placeholder = document.createComment('root') - placeholder.hasAttribute = placeholder.removeAttribute = function () {} // hack for patch - document.documentElement.appendChild(placeholder) - return placeholder -} diff --git a/src/server/bundle-renderer/create-bundle-renderer.js b/src/server/bundle-renderer/create-bundle-renderer.js deleted file mode 100644 index 05c60c35740..00000000000 --- a/src/server/bundle-renderer/create-bundle-renderer.js +++ /dev/null @@ -1,151 +0,0 @@ -/* @flow */ - -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 { - bundle = JSON.parse(bundle) - } catch (e) { - 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, 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) { - 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) { - 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/src/server/bundle-renderer/create-bundle-runner.js b/src/server/bundle-renderer/create-bundle-runner.js deleted file mode 100644 index a0e1d283aa1..00000000000 --- a/src/server/bundle-renderer/create-bundle-runner.js +++ /dev/null @@ -1,150 +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) { - const sandbox = { - Buffer, - console, - process, - setTimeout, - setInterval, - setImmediate, - clearTimeout, - clearInterval, - clearImmediate, - __VUE_SSR_CONTEXT__: context - } - 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.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') - ? 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 => { - 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. - initialContext = sandbox.__VUE_SSR_CONTEXT__ = {} - runner = evaluate(entry, sandbox) - // On subsequent renders, __VUE_SSR_CONTEXT__ will not be available - // to prevent cross-request pollution. - delete sandbox.__VUE_SSR_CONTEXT__ - if (typeof runner !== 'function') { - throw new Error( - 'bundle export should be a function when using ' + - '{ runInNewContext: false }.' - ) - } - } - userContext._registeredComponents = new Set() - - // vue-style-loader styles imported outside of component lifecycle hooks - if (initialContext._styles) { - 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 () { - return renderStyles(userContext._styles) - } - }) - } - } - - resolve(runner(userContext)) - }) - } -} diff --git a/src/server/bundle-renderer/source-map-support.js b/src/server/bundle-renderer/source-map-support.js deleted file mode 100644 index 1010532e49c..00000000000 --- a/src/server/bundle-renderer/source-map-support.js +++ /dev/null @@ -1,45 +0,0 @@ -/* @flow */ - -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]: 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]: 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/src/server/create-basic-renderer.js b/src/server/create-basic-renderer.js deleted file mode 100644 index 2d53976d8d5..00000000000 --- a/src/server/create-basic-renderer.js +++ /dev/null @@ -1,37 +0,0 @@ -/* @flow */ - -import { createWriteFunction } from './write' -import { createRenderFunction } from './render' -import type { RenderOptions } from './create-renderer' - -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 { - render(component, write, context, () => { - done(null, result) - }) - } catch (e) { - done(e) - } - } -} diff --git a/src/server/create-renderer.js b/src/server/create-renderer.js deleted file mode 100644 index 8be2c449681..00000000000 --- a/src/server/create-renderer.js +++ /dev/null @@ -1,116 +0,0 @@ -/* @flow */ - -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' - -export type Renderer = { - renderToString: (component: Component, context: any, cb: any) => ?Promise<string>; - renderToStream: (component: Component, context?: Object) => stream$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>; - directives?: Object; - isUnaryTag?: Function; - cache?: RenderCache; - template?: string; - inject?: boolean; - basedir?: string; - shouldPreload?: Function; - shouldPrefetch?: Function; - clientManifest?: ClientManifest; - runInNewContext?: boolean | 'once'; -}; - -export function createRenderer ({ - modules = [], - directives = {}, - isUnaryTag = (() => false), - template, - inject, - cache, - shouldPreload, - shouldPrefetch, - clientManifest -}: RenderOptions = {}): Renderer { - const render = createRenderFunction(modules, directives, isUnaryTag, cache) - const templateRenderer = new TemplateRenderer({ - template, - inject, - shouldPreload, - shouldPrefetch, - clientManifest - }) - - 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 { - render(component, write, context, () => { - if (template) { - result = templateRenderer.renderSync(result, context) - } - cb(null, result) - }) - } catch (e) { - cb(e) - } - - return promise - }, - - renderToStream ( - component: Component, - context?: Object - ): stream$Readable { - if (context) { - templateRenderer.bindRenderFns(context) - } - const renderStream = new RenderStream((write, done) => { - render(component, write, context, done) - }) - if (!template) { - return renderStream - } else { - const templateStream = templateRenderer.createStream(context) - renderStream.on('error', err => { - templateStream.emit('error', err) - }) - renderStream.pipe(templateStream) - return templateStream - } - } - } -} diff --git a/src/server/optimizing-compiler/codegen.js b/src/server/optimizing-compiler/codegen.js deleted file mode 100644 index 26097c07b48..00000000000 --- a/src/server/optimizing-compiler/codegen.js +++ /dev/null @@ -1,260 +0,0 @@ -/* @flow */ - -// 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 'web/server/util' -import { optimizability } from './optimizer' -import type { CodegenResult } from 'compiler/codegen/index' - -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) { - 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 = [] - 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) { - segments.push({ type: RAW, value: escape(c.text) }) - } - } - return segments -} - -function flattenSegments (segments: Array<StringSegment>): string { - const mergedSegments = [] - 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/src/server/optimizing-compiler/index.js b/src/server/optimizing-compiler/index.js deleted file mode 100644 index 893630d670c..00000000000 --- a/src/server/optimizing-compiler/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* @flow */ - -import { parse } from 'compiler/parser/index' -import { generate } from './codegen' -import { optimize } from './optimizer' -import { createCompilerCreator } from 'compiler/create-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/src/server/optimizing-compiler/modules.js b/src/server/optimizing-compiler/modules.js deleted file mode 100644 index 41b1af1d9d2..00000000000 --- a/src/server/optimizing-compiler/modules.js +++ /dev/null @@ -1,126 +0,0 @@ -/* @flow */ - -import { - RAW, - // INTERPOLATION, - EXPRESSION -} from './codegen' - -import { - propsToAttrMap, - isRenderableAttr -} from 'web/server/util' - -import { - isBooleanAttr, - isEnumeratedAttr -} from 'web/util/attrs' - -import type { StringSegment } from './codegen' -import type { CodegenState } from 'compiler/codegen/index' - -type Attr = { name: string; value: string }; - -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<Attr> -): Array<StringSegment> { - return attrs.map(({ name, value }) => genAttrSegment(name, value)) -} - -export function genDOMPropSegments ( - props: Array<Attr>, - attrs: ?Array<Attr> -): Array<StringSegment> { - const segments = [] - 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}=${value}` - } - } else { - return { - type: EXPRESSION, - value: `_ssrAttr(${JSON.stringify(name)},${value})` - } - } -} - -export function genClassSegments ( - staticClass: ?string, - classBinding: ?string -): Array<StringSegment> { - if (staticClass && !classBinding) { - return [{ type: RAW, value: ` class=${staticClass}` }] - } else { - return [{ - type: EXPRESSION, - value: `_ssrClass(${staticClass || 'null'},${classBinding || 'null'})` - }] - } -} - -export function genStyleSegments ( - staticStyle: ?string, - parsedStaticStyle: ?string, - styleBinding: ?string, - vShowExpression: ?string -): 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/src/server/optimizing-compiler/optimizer.js b/src/server/optimizing-compiler/optimizer.js deleted file mode 100644 index d24acc5526a..00000000000 --- a/src/server/optimizing-compiler/optimizer.js +++ /dev/null @@ -1,140 +0,0 @@ -/* @flow */ - -/** - * 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' - -// 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, 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 = [] - - let currentOptimizableGroup = [] - const pushGroup = () => { - if (currentOptimizableGroup.length) { - optimizedChildren.push({ - type: 1, - parent: el, - tag: 'template', - attrsList: [], - attrsMap: {}, - 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)) - ) -} - -// <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/src/server/optimizing-compiler/runtime-helpers.js b/src/server/optimizing-compiler/runtime-helpers.js deleted file mode 100644 index b4f7c1f145d..00000000000 --- a/src/server/optimizing-compiler/runtime-helpers.js +++ /dev/null @@ -1,140 +0,0 @@ -/* @flow */ - -import { escape } from 'web/server/util' -import { isObject, extend } from 'shared/util' -import { renderAttr } from 'web/server/modules/attrs' -import { renderClass } from 'web/util/class' -import { genStyle } from 'web/server/modules/style' -import { normalizeStyleBinding } from 'web/util/style' - -import { - normalizeChildren, - simpleNormalizeChildren -} from 'core/vdom/helpers/normalize-children' - -import { - propsToAttrMap, - isRenderableAttr -} from 'web/server/util' - -export function installSSRHelpers (vm: Component) { - if (vm._ssrNode) return - let Ctor = vm.constructor - while (Ctor.super) { - Ctor = Ctor.super - } - extend(Ctor.prototype, { - _ssrEscape: escape, - _ssrNode: renderStringNode, - _ssrList: renderStringList, - _ssrAttr: renderAttr, - _ssrAttrs: renderAttrs, - _ssrDOMProps: renderDOMProps, - _ssrClass: renderSSRClass, - _ssrStyle: renderSSRStyle - }) -} - -class StringNode { - isString: boolean; - open: string; - close: ?string; - children: ?Array<any>; - - 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) { - 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, - dynamic: any -): string { - const res = renderClass(staticClass, dynamic) - return res === '' ? res : ` class="${escape(res)}"` -} - -function renderSSRStyle ( - staticStyle: ?Object, - dynamic: any, - extra: ?Object -): 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/src/server/render-context.js b/src/server/render-context.js deleted file mode 100644 index d75fdc8cf76..00000000000 --- a/src/server/render-context.js +++ /dev/null @@ -1,120 +0,0 @@ -/* @flow */ - -import { isUndef } from 'shared/util' - -type RenderState = { - type: 'Element'; - rendered: number; - total: number; - endTag: string; - children: Array<VNode>; -} | { - type: 'Component'; - prevActive: Component; -} | { - type: 'ComponentWithCache'; - buffer: Array<string>; - bufferIndex: number; - componentBuffer: Array<Set<Class<Component>>>; - key: string; -}; - -export class RenderContext { - userContext: ?Object; - activeInstance: Component; - renderStates: Array<RenderState>; - write: (text: string, next: Function) => void; - renderNode: (node: VNode, isRoot: boolean, context: RenderContext) => void; - next: () => void; - done: () => void; - - modules: Array<(node: VNode) => ?string>; - directives: Object; - isUnaryTag: (tag: string) => boolean; - - cache: any; - get: ?(key: string, cb: Function) => void; - has: ?(key: string, cb: Function) => void; - - constructor (options: Object) { - 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 () { - const lastState = this.renderStates[this.renderStates.length - 1] - if (isUndef(lastState)) { - return this.done() - } - switch (lastState.type) { - case 'Element': - const { children, total } = lastState - const rendered = lastState.rendered++ - if (rendered < total) { - this.renderNode(children[rendered], false, this) - } else { - this.renderStates.pop() - this.write(lastState.endTag, this.next) - } - break - case 'Component': - this.renderStates.pop() - this.activeInstance = lastState.prevActive - this.next() - 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. - 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 - this.next() - 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/src/server/render-stream.js b/src/server/render-stream.js deleted file mode 100644 index d76012afb5b..00000000000 --- a/src/server/render-stream.js +++ /dev/null @@ -1,94 +0,0 @@ -/* @flow */ - -/** - * 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 { isTrue, isUndef } from 'shared/util' -import { createWriteFunction } from './write' - -export default class RenderStream extends stream.Readable { - buffer: string; - render: (write: Function, done: Function) => void; - expectedSize: number; - write: Function; - next: Function; - end: Function; - done: boolean; - - constructor (render: Function) { - super() - this.buffer = '' - 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 = () => { - // 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/src/server/render.js b/src/server/render.js deleted file mode 100644 index 0ca49f7c2ba..00000000000 --- a/src/server/render.js +++ /dev/null @@ -1,382 +0,0 @@ -/* @flow */ - -import { escape } from 'web/server/util' -import { SSR_ATTR } from 'shared/constants' -import { RenderContext } from './render-context' -import { generateComponentTrace } from 'core/util/debug' -import { ssrCompileToFunctions } from 'web/server/compiler' -import { installSSRHelpers } from './optimizing-compiler/runtime-helpers' - -import { isDef, isUndef, isTrue } from 'shared/util' - -import { - createComponent, - createComponentInstanceForVnode -} from 'core/vdom/create-component' - -let warned = Object.create(null) -const warnOnce = msg => { - if (!warned[msg]) { - warned[msg] = true - 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 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 key = name + '::' + getKey(node.componentOptions.propsData) - 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 childNode = child._render() - childNode.parent = node - context.renderStates.push({ - type: 'Component', - prevActive - }) - renderNode(childNode, isRoot, context) -} - -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) { - renderComponent(resolvedNode, isRoot, context) - } else { - reject() - } - } - - const reject = err => { - console.error(`[vue-server-renderer] error when rendering async component:\n`) - if (err) console.error(err.stack) - context.write(`<!--${node.text}-->`, context.next) - } - - if (factory.resolved) { - resolve(factory.resolved) - return - } - - let res - try { - res = factory(resolve, reject) - } catch (e) { - 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', - rendered: 0, - total: children.length, - endTag: el.close, children - }) - 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.functionalOptions) { - registerComponentForCache(el.functionalOptions, 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', - rendered: 0, - total: children.length, - endTag, children - }) - write(startTag, next) - } -} - -function hasAncestorData (node: VNode) { - const parentNode = node.parent - return isDef(parentNode) && (isDef(parentNode.data) || hasAncestorData(parentNode)) -} - -function getVShowDirectiveInfo (node: VNode): ?VNodeDirective { - 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 - } - 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 - const dirRenderer = directives[name] - if (dirRenderer && name !== 'show') { - // directives mutate the node's data - // which then gets rendered by modules - dirRenderer(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: any)}` - } - if (isDef(node.fnScopeId)) { - markup += ` ${node.fnScopeId}` - } else { - while (isDef(node)) { - if (isDef(scopeId = node.context.$options._scopeId)) { - markup += ` ${scopeId}` - } - node = node.parent - } - } - return markup + '>' -} - -export function createRenderFunction ( - modules: Array<(node: VNode) => ?string>, - directives: Object, - isUnaryTag: Function, - cache: any -) { - return function render ( - component: Component, - write: (text: string, next: Function) => void, - userContext: ?Object, - done: Function - ) { - warned = Object.create(null) - const context = new RenderContext({ - activeInstance: component, - userContext, - write, done, renderNode, - isUnaryTag, modules, directives, - cache - }) - installSSRHelpers(component) - normalizeRender(component) - renderNode(component._render(), true, context) - } -} diff --git a/src/server/template-renderer/create-async-file-mapper.js b/src/server/template-renderer/create-async-file-mapper.js deleted file mode 100644 index 64c0647a3ed..00000000000 --- a/src/server/template-renderer/create-async-file-mapper.js +++ /dev/null @@ -1,53 +0,0 @@ -/* @flow */ - -/** - * 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() - 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 = [] - const fileIndices = clientManifest.modules[id] - if (fileIndices) { - fileIndices.forEach(index => { - const file = clientManifest.all[index] - // only include async files or non-js assets - if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) { - files.push(file) - } - }) - } - return files -} diff --git a/src/server/template-renderer/index.js b/src/server/template-renderer/index.js deleted file mode 100644 index a1078243a8a..00000000000 --- a/src/server/template-renderer/index.js +++ /dev/null @@ -1,258 +0,0 @@ -/* @flow */ - -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; - inject?: boolean; - clientManifest?: ClientManifest; - shouldPreload?: (file: string, type: string) => boolean; - shouldPrefetch?: (file: string, type: string) => boolean; -}; - -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 | null; - publicPath: string; - clientManifest: ClientManifest; - preloadFiles: Array<Resource>; - prefetchFiles: Array<Resource>; - mapFiles: AsyncFileMapper; - - 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. - this.parsedTemplate = options.template - ? parseTemplate(options.template) - : null - - // extra functionality with client manifest - if (options.clientManifest) { - const clientManifest = this.clientManifest = options.clientManifest - this.publicPath = clientManifest.publicPath.replace(/\/$/, '') - // 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: Object) { - 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 - renderSync (content: string, context: ?Object) { - const template = this.parsedTemplate - if (!template) { - throw new Error('renderSync cannot be called without a template.') - } - context = 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: Object): string { - const cssFiles = this.clientManifest - ? this.clientManifest.all.filter(isCSS) - : [] - 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: Object, options?: Object): string { - const { - contextKey = 'state', - windowKey = '__INITIAL_STATE__' - } = options || {} - const autoRemove = process.env.NODE_ENV === 'production' - ? ';(function(){var s;(s=document.currentScript||document.scripts[document.scripts.length-1]).parentNode.removeChild(s);}());' - : '' - return context[contextKey] - ? `<script>window.${windowKey}=${ - serialize(context[contextKey], { isJSON: true }) - }${autoRemove}</script>` - : '' - } - - renderScripts (context: Object): string { - if (this.clientManifest) { - const initial = this.preloadFiles - const async = this.getUsedAsyncFiles(context) - const needed = [initial[0]].concat(async || [], initial.slice(1)) - return needed.filter(({ file }) => isJS(file)).map(({ file }) => { - return `<script src="${this.publicPath}/${file}" defer></script>` - }).join('') - } else { - return '' - } - } - - getUsedAsyncFiles (context: Object): ?Array<Resource> { - if (!context._mappedFiles && context._registeredComponents && this.mapFiles) { - const registered = Array.from(context._registeredComponents) - context._mappedFiles = this.mapFiles(registered).map(normalizeFile) - } - return context._mappedFiles - } - - // create a transform stream - createStream (context: ?Object): TemplateStream { - if (!this.parsedTemplate) { - throw new Error('createStream cannot be called without a template.') - } - 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/src/server/template-renderer/parse-template.js b/src/server/template-renderer/parse-template.js deleted file mode 100644 index 1ccfe899620..00000000000 --- a/src/server/template-renderer/parse-template.js +++ /dev/null @@ -1,42 +0,0 @@ -/* @flow */ - -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/src/server/template-renderer/template-stream.js b/src/server/template-renderer/template-stream.js deleted file mode 100644 index ed4db78f65a..00000000000 --- a/src/server/template-renderer/template-stream.js +++ /dev/null @@ -1,82 +0,0 @@ -/* @flow */ - -const Transform = require('stream').Transform -import type TemplateRenderer from './index' -import type { ParsedTemplate } from './parse-template' - -export default class TemplateStream extends Transform { - started: boolean; - renderer: TemplateRenderer; - template: ParsedTemplate; - context: Object; - inject: boolean; - - constructor ( - renderer: TemplateRenderer, - template: ParsedTemplate, - context: Object - ) { - 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/src/server/util.js b/src/server/util.js deleted file mode 100644 index 908f8c9df06..00000000000 --- a/src/server/util.js +++ /dev/null @@ -1,18 +0,0 @@ -/* @flow */ - -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/src/server/webpack-plugin/client.js b/src/server/webpack-plugin/client.js deleted file mode 100644 index 5f2cd4bd6c6..00000000000 --- a/src/server/webpack-plugin/client.js +++ /dev/null @@ -1,70 +0,0 @@ -const hash = require('hash-sum') -const uniq = require('lodash.uniq') -import { isJS } from './util' - -export default class VueSSRClientPlugin { - constructor (options = {}) { - this.options = Object.assign({ - filename: 'vue-ssr-client-manifest.json' - }, options) - } - - apply (compiler) { - compiler.plugin('emit', (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), []) - .filter(isJS)) - - const asyncFiles = allFiles - .filter(isJS) - .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 = file => manifest.all.indexOf(file) - 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 files = manifest.modules[hash(m.identifier)] = 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 debug = (file, obj) => { - // require('fs').writeFileSync(__dirname + '/' + file, JSON.stringify(obj, null, 2)) - // } - // debug('stats.json', stats) - // debug('client-manifest.json', manifest) - - const json = JSON.stringify(manifest, null, 2) - compilation.assets[this.options.filename] = { - source: () => json, - size: () => json.length - } - cb() - }) - } -} diff --git a/src/server/webpack-plugin/server.js b/src/server/webpack-plugin/server.js deleted file mode 100644 index 8ffa58b03ee..00000000000 --- a/src/server/webpack-plugin/server.js +++ /dev/null @@ -1,66 +0,0 @@ -import { validate, isJS } from './util' - -export default class VueSSRServerPlugin { - constructor (options = {}) { - this.options = Object.assign({ - filename: 'vue-ssr-server-bundle.json' - }, options) - } - - apply (compiler) { - validate(compiler) - - compiler.plugin('emit', (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.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: {} - } - - stats.assets.forEach(asset => { - if (asset.name.match(/\.js$/)) { - bundle.files[asset.name] = compilation.assets[asset.name].source() - } else if (asset.name.match(/\.js\.map$/)) { - bundle.maps[asset.name.replace(/\.map$/, '')] = JSON.parse(compilation.assets[asset.name].source()) - } - // do not emit anything else for server - delete compilation.assets[asset.name] - }) - - const json = JSON.stringify(bundle, null, 2) - const filename = this.options.filename - - compilation.assets[filename] = { - source: () => json, - size: () => json.length - } - - cb() - }) - } -} diff --git a/src/server/webpack-plugin/util.js b/src/server/webpack-plugin/util.js deleted file mode 100644 index d22e4b2c497..00000000000 --- a/src/server/webpack-plugin/util.js +++ /dev/null @@ -1,24 +0,0 @@ -const { red, yellow } = require('chalk') - -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`)) - -export const validate = compiler => { - if (compiler.options.target !== 'node') { - warn('webpack config `target` should be "node".') - } - - if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') { - 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 { isJS, isCSS } from '../util' diff --git a/src/server/write.js b/src/server/write.js deleted file mode 100644 index e642081852c..00000000000 --- a/src/server/write.js +++ /dev/null @@ -1,50 +0,0 @@ -/* @flow */ - -const MAX_STACK_DEPTH = 1000 -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) { - onError(e) - } - }) - } else { - stackDepth++ - next() - stackDepth-- - } - } - } - cachedWrite.caching = false - cachedWrite.cacheBuffer = [] - cachedWrite.componentBuffer = [] - return cachedWrite -} diff --git a/src/sfc/parser.js b/src/sfc/parser.js deleted file mode 100644 index 643e55ca2ff..00000000000 --- a/src/sfc/parser.js +++ /dev/null @@ -1,116 +0,0 @@ -/* @flow */ - -import deindent from 'de-indent' -import { parseHTML } from 'compiler/parser/html-parser' -import { makeMap } from 'shared/util' - -const splitRE = /\r?\n/g -const replaceRE = /./g -const isSpecialTag = makeMap('script,style,template', true) - -type Attribute = { - name: string, - value: string -}; - -/** - * Parse a single-file component (*.vue) file into an SFC Descriptor Object. - */ -export function parseComponent ( - content: string, - options?: Object = {} - ): SFCDescriptor { - const sfc: SFCDescriptor = { - template: null, - script: null, - styles: [], - customBlocks: [] - } - let depth = 0 - let currentBlock: ?(SFCBlock | SFCCustomBlock) = null - - function start ( - tag: string, - attrs: Array<Attribute>, - unary: boolean, - start: number, - end: number - ) { - if (depth === 0) { - currentBlock = { - type: tag, - content: '', - start: end, - attrs: attrs.reduce((cumulated, { name, value }) => { - cumulated[name] = value || true - return cumulated - }, Object.create(null)) - } - if (isSpecialTag(tag)) { - checkAttrs(currentBlock, attrs) - 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: Array<Attribute>) { - 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 - } - if (attr.name === 'src') { - block.src = attr.value - } - } - } - - function end (tag: string, start: number, end: number) { - if (depth === 1 && currentBlock) { - currentBlock.end = start - let text = deindent(content.slice(currentBlock.start, currentBlock.end)) - // 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 | SFCCustomBlock, pad: true | "line" | "space") { - if (pad === 'space') { - return content.slice(0, block.start).replace(replaceRE, ' ') - } else { - const offset = content.slice(0, block.start).split(splitRE).length - const padChar = block.type === 'script' && !block.lang - ? '//\n' - : '\n' - return Array(offset).join(padChar) - } - } - - parseHTML(content, { - start, - end - }) - - return sfc -} diff --git a/src/shared/constants.js b/src/shared/constants.js deleted file mode 100644 index 84d019fb4ca..00000000000 --- a/src/shared/constants.js +++ /dev/null @@ -1,21 +0,0 @@ -export const SSR_ATTR = 'data-server-rendered' - -export const ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -] - -export const LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -] diff --git a/src/shared/constants.ts b/src/shared/constants.ts new file mode 100644 index 00000000000..660c4d90519 --- /dev/null +++ b/src/shared/constants.ts @@ -0,0 +1,20 @@ +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.js b/src/shared/util.js deleted file mode 100644 index 353b9829980..00000000000 --- a/src/shared/util.js +++ /dev/null @@ -1,306 +0,0 @@ -/* @flow */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -export function isUndef (v: any): boolean %checks { - return v === undefined || v === null -} - -export function isDef (v: any): boolean %checks { - return v !== undefined && v !== null -} - -export function isTrue (v: any): boolean %checks { - return v === true -} - -export function isFalse (v: any): boolean %checks { - return v === false -} - -/** - * Check if value is primitive - */ -export function isPrimitive (value: any): boolean %checks { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * 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: mixed): boolean %checks { - 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): boolean { - 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) -} - -/** - * Convert a value to a string that is actually rendered. - */ -export function toString (val: any): string { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a 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 | void { - 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 a 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 { - if (arr.length) { - const index = arr.indexOf(item) - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -const hasOwnProperty = Object.prototype.hasOwnProperty -export function hasOwn (obj: Object | Array<*>, key: string): boolean { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -export function cached<F: Function> (fn: F): F { - const cache = Object.create(null) - return (function cachedFn (str: string) { - const hit = cache[str] - return hit || (cache[str] = fn(str)) - }: any) -} - -/** - * 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, faster than native - */ -export function bind (fn: Function, ctx: Object): Function { - function boundFn (a) { - const l: number = arguments.length - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length - return boundFn -} - -/** - * 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: Object, _from: ?Object): Object { - 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 -} - -/** - * 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 - -/** - * Return same value - */ -export const identity = (_: any) => _ - -/** - * Generate a static keys string from compiler modules. - */ -export function genStaticKeys (modules: Array<ModuleOptions>): string { - return modules.reduce((keys, m) => { - return 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, i) => { - return looseEqual(e, b[i]) - }) - } 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) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -export function looseIndexOf (arr: Array<mixed>, val: mixed): 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 (fn: Function): Function { - let called = false - return function () { - if (!called) { - called = true - fn.apply(this, arguments) - } - } -} diff --git a/src/shared/util.ts b/src/shared/util.ts new file mode 100644 index 00000000000..6d84877b74d --- /dev/null +++ b/src/shared/util.ts @@ -0,0 +1,378 @@ +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/types/compiler.ts b/src/types/compiler.ts new file mode 100644 index 00000000000..5c5059019f4 --- /dev/null +++ b/src/types/compiler.ts @@ -0,0 +1,207 @@ +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 new file mode 100644 index 00000000000..5d16fa15f7a --- /dev/null +++ b/src/types/component.ts @@ -0,0 +1,212 @@ +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 new file mode 100644 index 00000000000..c2ecfed5a6b --- /dev/null +++ b/src/types/global-api.ts @@ -0,0 +1,37 @@ +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 new file mode 100644 index 00000000000..814033a1945 --- /dev/null +++ b/src/types/modules.d.ts @@ -0,0 +1,15 @@ +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 new file mode 100644 index 00000000000..27b3d7afc31 --- /dev/null +++ b/src/types/options.ts @@ -0,0 +1,114 @@ +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 new file mode 100644 index 00000000000..d667f283245 --- /dev/null +++ b/src/types/ssr.ts @@ -0,0 +1,27 @@ +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 new file mode 100644 index 00000000000..6f7eeeeaba6 --- /dev/null +++ b/src/types/utils.ts @@ -0,0 +1,3 @@ +// 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 new file mode 100644 index 00000000000..cbe8f0aa98f --- /dev/null +++ b/src/types/vnode.ts @@ -0,0 +1,127 @@ +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/v3/apiAsyncComponent.ts b/src/v3/apiAsyncComponent.ts new file mode 100644 index 00000000000..888a0681cb6 --- /dev/null +++ b/src/v3/apiAsyncComponent.ts @@ -0,0 +1,117 @@ +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 new file mode 100644 index 00000000000..76928268b13 --- /dev/null +++ b/src/v3/apiInject.ts @@ -0,0 +1,71 @@ +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 new file mode 100644 index 00000000000..31e0542920c --- /dev/null +++ b/src/v3/apiLifecycle.ts @@ -0,0 +1,68 @@ +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 new file mode 100644 index 00000000000..064e15dc75f --- /dev/null +++ b/src/v3/apiSetup.ts @@ -0,0 +1,246 @@ +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 new file mode 100644 index 00000000000..7160009dabb --- /dev/null +++ b/src/v3/apiWatch.ts @@ -0,0 +1,353 @@ +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 new file mode 100644 index 00000000000..197f9392945 --- /dev/null +++ b/src/v3/currentInstance.ts @@ -0,0 +1,23 @@ +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 new file mode 100644 index 00000000000..ff6890b9c0c --- /dev/null +++ b/src/v3/debug.ts @@ -0,0 +1,21 @@ +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 new file mode 100644 index 00000000000..292446cad3d --- /dev/null +++ b/src/v3/h.ts @@ -0,0 +1,18 @@ +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 new file mode 100644 index 00000000000..90dfacc17ed --- /dev/null +++ b/src/v3/index.ts @@ -0,0 +1,96 @@ +/** + * 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 new file mode 100644 index 00000000000..7a38c3cfc0a --- /dev/null +++ b/src/v3/reactivity/computed.ts @@ -0,0 +1,100 @@ +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 new file mode 100644 index 00000000000..11f9534a31b --- /dev/null +++ b/src/v3/reactivity/effect.ts @@ -0,0 +1,20 @@ +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 new file mode 100644 index 00000000000..f60e5fccf23 --- /dev/null +++ b/src/v3/reactivity/effectScope.ts @@ -0,0 +1,137 @@ +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 new file mode 100644 index 00000000000..9e6bec51878 --- /dev/null +++ b/src/v3/reactivity/operations.ts @@ -0,0 +1,14 @@ +// 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 new file mode 100644 index 00000000000..da9a1bf0c1c --- /dev/null +++ b/src/v3/reactivity/reactive.ts @@ -0,0 +1,137 @@ +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 new file mode 100644 index 00000000000..e3a83930301 --- /dev/null +++ b/src/v3/reactivity/readonly.ts @@ -0,0 +1,127 @@ +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 new file mode 100644 index 00000000000..33495806da1 --- /dev/null +++ b/src/v3/reactivity/ref.ts @@ -0,0 +1,293 @@ +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 new file mode 100644 index 00000000000..df49931af12 --- /dev/null +++ b/src/v3/sfc-helpers/useCssModule.ts @@ -0,0 +1,24 @@ +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 new file mode 100644 index 00000000000..cba7050a0e7 --- /dev/null +++ b/src/v3/sfc-helpers/useCssVars.ts @@ -0,0 +1,34 @@ +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/test/e2e/.eslintrc b/test/e2e/.eslintrc deleted file mode 100644 index e866525fec5..00000000000 --- a/test/e2e/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rules": { - "indent": 0 - } -} diff --git a/test/e2e/specs/async-edge-cases.html b/test/e2e/async-edge-cases.html similarity index 95% rename from test/e2e/specs/async-edge-cases.html rename to test/e2e/async-edge-cases.html index 4c7411b2e5d..0fc9848efdb 100644 --- a/test/e2e/specs/async-edge-cases.html +++ b/test/e2e/async-edge-cases.html @@ -3,10 +3,9 @@ <head> <meta charset="utf-8"> <title></title> - <script src="../../../dist/vue.min.js"></script> + <script src="../../dist/vue.min.js"></script> </head> <body> - <!-- #4510 click and change event on checkbox --> <div id="case-1"> <div @click="num++"> diff --git a/test/e2e/async-edge-cases.spec.ts b/test/e2e/async-edge-cases.spec.ts new file mode 100644 index 00000000000..08b1a751f35 --- /dev/null +++ b/test/e2e/async-edge-cases.spec.ts @@ -0,0 +1,44 @@ +// @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/specs/basic-ssr.html b/test/e2e/basic-ssr.html similarity index 77% rename from test/e2e/specs/basic-ssr.html rename to test/e2e/basic-ssr.html index 67f0dac6d6a..df6f57deaad 100644 --- a/test/e2e/specs/basic-ssr.html +++ b/test/e2e/basic-ssr.html @@ -5,8 +5,8 @@ <title></title> </head> <body> - <script src="../../../dist/vue.min.js"></script> - <script src="../../../packages/vue-server-renderer/basic.js"></script> + <script src="../../dist/vue.min.js"></script> + <script src="../../packages/server-renderer/basic.js"></script> <div id="result">wtf</div> diff --git a/test/e2e/basic-ssr.spec.ts b/test/e2e/basic-ssr.spec.ts new file mode 100644 index 00000000000..384761644fe --- /dev/null +++ b/test/e2e/basic-ssr.spec.ts @@ -0,0 +1,18 @@ +// @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.mock.ts b/test/e2e/commits.mock.ts new file mode 100644 index 00000000000..c13e127d736 --- /dev/null +++ b/test/e2e/commits.mock.ts @@ -0,0 +1,551 @@ +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 new file mode 100644 index 00000000000..28796fe7c71 --- /dev/null +++ b/test/e2e/commits.spec.ts @@ -0,0 +1,62 @@ +// @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 new file mode 100644 index 00000000000..b991cbd299a --- /dev/null +++ b/test/e2e/e2eUtils.ts @@ -0,0 +1,187 @@ +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.spec.ts b/test/e2e/grid.spec.ts new file mode 100644 index 00000000000..937a5bbb2dd --- /dev/null +++ b/test/e2e/grid.spec.ts @@ -0,0 +1,115 @@ +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.spec.ts b/test/e2e/markdown.spec.ts new file mode 100644 index 00000000000..67b7244afae --- /dev/null +++ b/test/e2e/markdown.spec.ts @@ -0,0 +1,46 @@ +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/nightwatch.config.js b/test/e2e/nightwatch.config.js deleted file mode 100644 index 2004f92b45b..00000000000 --- a/test/e2e/nightwatch.config.js +++ /dev/null @@ -1,57 +0,0 @@ -// http://nightwatchjs.org/guide#settings-file -module.exports = { - 'src_folders': ['test/e2e/specs'], - 'output_folder': 'test/e2e/reports', - 'custom_commands_path': ['node_modules/nightwatch-helpers/commands'], - 'custom_assertions_path': ['node_modules/nightwatch-helpers/assertions'], - - 'selenium': { - 'start_process': true, - 'server_path': require('selenium-server').path, - 'host': '127.0.0.1', - 'port': 4444, - 'cli_args': { - 'webdriver.chrome.driver': require('chromedriver').path - // , 'webdriver.gecko.driver': require('geckodriver').path - } - }, - - 'test_settings': { - 'default': { - 'selenium_port': 4444, - 'selenium_host': 'localhost', - 'silent': true, - 'screenshots': { - 'enabled': true, - 'on_failure': true, - 'on_error': false, - 'path': 'test/e2e/screenshots' - } - }, - - 'chrome': { - 'desiredCapabilities': { - 'browserName': 'chrome', - 'javascriptEnabled': true, - 'acceptSslCerts': true - } - }, - - 'firefox': { - 'desiredCapabilities': { - 'browserName': 'firefox', - 'javascriptEnabled': true, - 'acceptSslCerts': true, - 'marionette': true - } - }, - - 'phantomjs': { - 'desiredCapabilities': { - 'browserName': 'phantomjs', - 'javascriptEnabled': true, - 'acceptSslCerts': true - } - } - } -} diff --git a/test/e2e/runner.js b/test/e2e/runner.js deleted file mode 100644 index d59506d9f73..00000000000 --- a/test/e2e/runner.js +++ /dev/null @@ -1,34 +0,0 @@ -var path = require('path') -var spawn = require('cross-spawn') -var httpServer = require('http-server') -var server = httpServer.createServer({ - root: path.resolve(__dirname, '../../') -}) - -server.listen(8080) - -var args = process.argv.slice(2) -if (args.indexOf('--config') === -1) { - args = args.concat(['--config', 'test/e2e/nightwatch.config.js']) -} -if (args.indexOf('--env') === -1) { - args = args.concat(['--env', 'chrome,phantomjs']) -} -var i = args.indexOf('--test') -if (i > -1) { - args[i + 1] = 'test/e2e/specs/' + args[i + 1] + '.js' -} - -var runner = spawn('./node_modules/.bin/nightwatch', args, { - stdio: 'inherit' -}) - -runner.on('exit', function (code) { - server.close() - process.exit(code) -}) - -runner.on('error', function (err) { - server.close() - throw err -}) diff --git a/test/e2e/specs/async-edge-cases.js b/test/e2e/specs/async-edge-cases.js deleted file mode 100644 index 2873409400b..00000000000 --- a/test/e2e/specs/async-edge-cases.js +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = { - 'async edge cases': function (browser) { - browser - .url('http://localhost:8080/test/e2e/specs/async-edge-cases.html') - // #4510 - .assert.containsText('#case-1', '1') - .assert.checked('#case-1 input', false) - - .click('#case-1 input') - .assert.containsText('#case-1', '2') - .assert.checked('#case-1 input', true) - - .click('#case-1 input') - .assert.containsText('#case-1', '3') - .assert.checked('#case-1 input', false) - - // #6566 - .assert.containsText('#case-2 button', 'Expand is True') - .assert.containsText('.count-a', 'countA: 0') - .assert.containsText('.count-b', 'countB: 0') - - .click('#case-2 button') - .assert.containsText('#case-2 button', 'Expand is False') - .assert.containsText('.count-a', 'countA: 1') - .assert.containsText('.count-b', 'countB: 0') - - .click('#case-2 button') - .assert.containsText('#case-2 button', 'Expand is True') - .assert.containsText('.count-a', 'countA: 1') - .assert.containsText('.count-b', 'countB: 1') - - .end() - } -} diff --git a/test/e2e/specs/basic-ssr.js b/test/e2e/specs/basic-ssr.js deleted file mode 100644 index b1ea2402d2e..00000000000 --- a/test/e2e/specs/basic-ssr.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - 'basic SSR': function (browser) { - browser - .url('http://localhost:8080/test/e2e/specs/basic-ssr.html') - .assert.containsText('#result', '<div data-server-rendered="true">foo</div>') - .end() - } -} diff --git a/test/e2e/specs/commits.js b/test/e2e/specs/commits.js deleted file mode 100644 index cf8152724e2..00000000000 --- a/test/e2e/specs/commits.js +++ /dev/null @@ -1,23 +0,0 @@ -module.exports = { - 'commits': function (browser) { - browser - .url('http://localhost:8080/examples/commits/') - .waitForElementVisible('li', 5000) - .assert.count('input', 2) - .assert.count('label', 2) - .assert.containsText('label[for="master"]', 'master') - .assert.containsText('label[for="dev"]', 'dev') - .assert.checked('#master') - .assert.checked('#dev', false) - .assert.containsText('p', 'vuejs/vue@master') - .assert.count('li', 3) - .assert.count('li .commit', 3) - .assert.count('li .message', 3) - .click('#dev') - .assert.containsText('p', 'vuejs/vue@dev') - .assert.count('li', 3) - .assert.count('li .commit', 3) - .assert.count('li .message', 3) - .end() - } -} diff --git a/test/e2e/specs/grid.js b/test/e2e/specs/grid.js deleted file mode 100644 index 7c273359e1b..00000000000 --- a/test/e2e/specs/grid.js +++ /dev/null @@ -1,105 +0,0 @@ -module.exports = { - 'grid': function (browser) { - var columns = ['name', 'power'] - - browser - .url('http://localhost:8080/examples/grid/') - .waitForElementVisible('table', 1000) - .assert.count('th', 2) - .assert.count('th.active', 0) - .assert.containsText('th:nth-child(1)', 'Name') - .assert.containsText('th:nth-child(2)', 'Power') - assertTable([ - { name: 'Chuck Norris', power: Infinity }, - { name: 'Bruce Lee', power: 9000 }, - { name: 'Jackie Chan', power: 7000 }, - { name: 'Jet Li', power: 8000 } - ]) - - browser - .click('th:nth-child(1)') - .assert.count('th.active:nth-child(1)', 1) - .assert.count('th.active:nth-child(2)', 0) - .assert.count('th:nth-child(1) .arrow.dsc', 1) - .assert.count('th:nth-child(2) .arrow.dsc', 0) - assertTable([ - { name: 'Jet Li', power: 8000 }, - { name: 'Jackie Chan', power: 7000 }, - { name: 'Chuck Norris', power: Infinity }, - { name: 'Bruce Lee', power: 9000 } - ]) - - browser - .click('th:nth-child(2)') - .assert.count('th.active:nth-child(1)', 0) - .assert.count('th.active:nth-child(2)', 1) - .assert.count('th:nth-child(1) .arrow.dsc', 1) - .assert.count('th:nth-child(2) .arrow.dsc', 1) - assertTable([ - { name: 'Chuck Norris', power: Infinity }, - { name: 'Bruce Lee', power: 9000 }, - { name: 'Jet Li', power: 8000 }, - { name: 'Jackie Chan', power: 7000 } - ]) - - browser - .click('th:nth-child(2)') - .assert.count('th.active:nth-child(1)', 0) - .assert.count('th.active:nth-child(2)', 1) - .assert.count('th:nth-child(1) .arrow.dsc', 1) - .assert.count('th:nth-child(2) .arrow.asc', 1) - assertTable([ - { name: 'Jackie Chan', power: 7000 }, - { name: 'Jet Li', power: 8000 }, - { name: 'Bruce Lee', power: 9000 }, - { name: 'Chuck Norris', power: Infinity } - ]) - - browser - .click('th:nth-child(1)') - .assert.count('th.active:nth-child(1)', 1) - .assert.count('th.active:nth-child(2)', 0) - .assert.count('th:nth-child(1) .arrow.asc', 1) - .assert.count('th:nth-child(2) .arrow.asc', 1) - assertTable([ - { name: 'Bruce Lee', power: 9000 }, - { name: 'Chuck Norris', power: Infinity }, - { name: 'Jackie Chan', power: 7000 }, - { name: 'Jet Li', power: 8000 } - ]) - - browser - .setValue('input[name="query"]', 'j') - assertTable([ - { name: 'Jackie Chan', power: 7000 }, - { name: 'Jet Li', power: 8000 } - ]) - - browser - .clearValue('input[name="query"]') - .setValue('input[name="query"]', 'infinity') - assertTable([ - { name: 'Chuck Norris', power: Infinity } - ]) - - browser - .clearValue('input[name="query"]') - .assert.count('p', 0) - .setValue('input[name="query"]', 'stringthatdoesnotexistanywhere') - .assert.count('p', 1) - - browser.end() - - function assertTable (data) { - browser.assert.count('td', data.length * columns.length) - for (var i = 0; i < data.length; i++) { - for (var j = 0; j < columns.length; j++) { - browser.assert.containsText( - 'tr:nth-child(' + (i + 1) + ') td:nth-child(' + (j + 1) + ')', - data[i][columns[j]] - ) - } - } - } - } -} diff --git a/test/e2e/specs/markdown.js b/test/e2e/specs/markdown.js deleted file mode 100644 index 84be2b43b70..00000000000 --- a/test/e2e/specs/markdown.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - 'markdown': function (browser) { - browser - .url('http://localhost:8080/examples/markdown/') - .waitForElementVisible('#editor', 1000) - .assert.value('textarea', '# hello') - .assert.hasHTML('#editor div', '<h1 id="hello">hello</h1>') - .setValue('textarea', '\n## foo\n\n- bar\n- baz') - // assert the output is not updated yet because of debounce - .assert.hasHTML('#editor div', '<h1 id="hello">hello</h1>') - .waitFor(500) - .assert.hasHTML('#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>' - ) - .end() - } -} diff --git a/test/e2e/specs/modal.js b/test/e2e/specs/modal.js deleted file mode 100644 index 947bab1768f..00000000000 --- a/test/e2e/specs/modal.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = { - 'modal': function (browser) { - browser - .url('http://localhost:8080/examples/modal/') - .waitForElementVisible('#app', 1000) - .assert.elementNotPresent('.modal-mask') - .click('#show-modal') - .assert.elementPresent('.modal-mask') - .assert.elementPresent('.modal-wrapper') - .assert.elementPresent('.modal-container') - .waitFor(50) - .assert.cssClassPresent('.modal-mask', 'modal-enter-active') - .waitFor(300) - .assert.cssClassNotPresent('.modal-mask', 'modal-enter-active') - .assert.containsText('.modal-header h3', 'custom header') - .assert.containsText('.modal-body', 'default body') - .assert.containsText('.modal-footer', 'default footer') - .click('.modal-default-button') - // should have transition - .assert.elementPresent('.modal-mask') - .waitFor(50) - .assert.cssClassPresent('.modal-mask', 'modal-leave-active') - .waitFor(300) - .assert.elementNotPresent('.modal-mask') - .end() - } -} diff --git a/test/e2e/specs/select2.js b/test/e2e/specs/select2.js deleted file mode 100644 index ada225d80bf..00000000000 --- a/test/e2e/specs/select2.js +++ /dev/null @@ -1,46 +0,0 @@ -/* globals vm */ -module.exports = { - 'select2': function (browser) { - browser - .url('http://localhost:8080/examples/select2/') - .waitForElementVisible('.select2', 1000) - .assert.elementPresent('select') - .assert.containsText('p', 'Selected: 0') - .assert.containsText('span.select2', 'Select one') - - .click('.select2-selection__rendered') - .assert.count('.select2-results__option', 3) - .assert.containsText('.select2-results__option:nth-child(1)', 'Select one') - .assert.containsText('.select2-results__option:nth-child(2)', 'Hello') - .assert.containsText('.select2-results__option:nth-child(3)', 'World') - .assert.attributePresent('.select2-results__option:nth-child(1)', 'aria-disabled') - - .click('.select2-results__option:nth-child(2)') - .assert.count('.select2-results__option', 0) - .assert.containsText('p', 'Selected: 1') - .assert.containsText('span.select2', 'Hello') - - // test dynamic options - .execute(function () { - vm.options.push({ id: 3, text: 'Vue' }) - }) - .click('.select2-selection__rendered') - .assert.count('.select2-results__option', 4) - .assert.containsText('.select2-results__option:nth-child(1)', 'Select one') - .assert.containsText('.select2-results__option:nth-child(2)', 'Hello') - .assert.containsText('.select2-results__option:nth-child(3)', 'World') - .assert.containsText('.select2-results__option:nth-child(4)', 'Vue') - - .click('.select2-results__option:nth-child(4)') - .assert.count('.select2-results__option', 0) - .assert.containsText('p', 'Selected: 3') - .assert.containsText('span.select2', 'Vue') - - .execute(function () { - vm.selected = 2 - }) - .assert.containsText('p', 'Selected: 2') - .assert.containsText('span.select2', 'World') - .end() - } -} diff --git a/test/e2e/specs/svg.js b/test/e2e/specs/svg.js deleted file mode 100644 index 7726bff5690..00000000000 --- a/test/e2e/specs/svg.js +++ /dev/null @@ -1,48 +0,0 @@ -/* globals stats, valueToPoint */ -module.exports = { - 'svg': function (browser) { - browser - .url('http://localhost:8080/examples/svg/') - .waitForElementVisible('svg', 1000) - .assert.count('g', 1) - .assert.count('polygon', 1) - .assert.count('circle', 1) - .assert.count('text', 6) - .assert.count('label', 6) - .assert.count('button', 7) - .assert.count('input[type="range"]', 6) - .assert.evaluate(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 - }) - .click('button.remove') - .assert.count('text', 5) - .assert.count('label', 5) - .assert.count('button', 6) - .assert.count('input[type="range"]', 5) - .assert.evaluate(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 - }) - .setValue('input[name="newlabel"]', 'foo') - .click('#add > button') - .assert.count('text', 6) - .assert.count('label', 6) - .assert.count('button', 7) - .assert.count('input[type="range"]', 6) - .assert.evaluate(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 - }) - .end() - } -} diff --git a/test/e2e/specs/todomvc.js b/test/e2e/specs/todomvc.js deleted file mode 100644 index 7f3c42fe7cc..00000000000 --- a/test/e2e/specs/todomvc.js +++ /dev/null @@ -1,166 +0,0 @@ -module.exports = { - 'todomvc': function (browser) { - browser - .url('http://localhost:8080/examples/todomvc/#test') - .waitForElementVisible('.todoapp', 1000) - .assert.notVisible('.main') - .assert.notVisible('.footer') - .assert.count('.filters .selected', 1) - .assert.evaluate(function () { - return document.querySelector('.filters .selected').textContent === 'All' - }) - - createNewItem('test') - .assert.count('.todo', 1) - .assert.notVisible('.todo .edit') - .assert.containsText('.todo label', 'test') - .assert.containsText('.todo-count strong', '1') - .assert.checked('.todo .toggle', false) - .assert.visible('.main') - .assert.visible('.footer') - .assert.notVisible('.clear-completed') - .assert.value('.new-todo', '') - - createNewItem('test2') - .assert.count('.todo', 2) - .assert.containsText('.todo:nth-child(2) label', 'test2') - .assert.containsText('.todo-count strong', '2') - - // toggle - browser - .click('.todo .toggle') - .assert.count('.todo.completed', 1) - .assert.cssClassPresent('.todo:nth-child(1)', 'completed') - .assert.containsText('.todo-count strong', '1') - .assert.visible('.clear-completed') - - createNewItem('test3') - .assert.count('.todo', 3) - .assert.containsText('.todo:nth-child(3) label', 'test3') - .assert.containsText('.todo-count strong', '2') - - createNewItem('test4') - createNewItem('test5') - .assert.count('.todo', 5) - .assert.containsText('.todo-count strong', '4') - - // toggle more - browser - .click('.todo:nth-child(4) .toggle') - .click('.todo:nth-child(5) .toggle') - .assert.count('.todo.completed', 3) - .assert.containsText('.todo-count strong', '2') - - // remove - removeItemAt(1) - .assert.count('.todo', 4) - .assert.count('.todo.completed', 2) - .assert.containsText('.todo-count strong', '2') - removeItemAt(2) - .assert.count('.todo', 3) - .assert.count('.todo.completed', 2) - .assert.containsText('.todo-count strong', '1') - - // remove all - browser - .click('.clear-completed') - .assert.count('.todo', 1) - .assert.containsText('.todo label', 'test2') - .assert.count('.todo.completed', 0) - .assert.containsText('.todo-count strong', '1') - .assert.notVisible('.clear-completed') - - // prepare to test filters - createNewItem('test') - createNewItem('test') - .click('.todo:nth-child(2) .toggle') - .click('.todo:nth-child(3) .toggle') - - // active filter - browser - .click('.filters li:nth-child(2) a') - .assert.count('.todo', 1) - .assert.count('.todo.completed', 0) - // add item with filter active - createNewItem('test') - .assert.count('.todo', 2) - - // completed filter - browser.click('.filters li:nth-child(3) a') - .assert.count('.todo', 2) - .assert.count('.todo.completed', 2) - - // filter on page load - browser.url('http://localhost:8080/examples/todomvc/#active') - .assert.count('.todo', 2) - .assert.count('.todo.completed', 0) - .assert.containsText('.todo-count strong', '2') - - // completed on page load - browser.url('http://localhost:8080/examples/todomvc/#completed') - .assert.count('.todo', 2) - .assert.count('.todo.completed', 2) - .assert.containsText('.todo-count strong', '2') - - // toggling with filter active - browser - .click('.todo .toggle') - .assert.count('.todo', 1) - .click('.filters li:nth-child(2) a') - .assert.count('.todo', 3) - .click('.todo .toggle') - .assert.count('.todo', 2) - - // editing triggered by blur - browser - .click('.filters li:nth-child(1) a') - .dblClick('.todo:nth-child(1) label') - .assert.count('.todo.editing', 1) - .assert.focused('.todo:nth-child(1) .edit') - .clearValue('.todo:nth-child(1) .edit') - .setValue('.todo:nth-child(1) .edit', 'edited!') - .click('footer') // blur - .assert.count('.todo.editing', 0) - .assert.containsText('.todo:nth-child(1) label', 'edited!') - - // editing triggered by enter - browser - .dblClick('.todo label') - .enterValue('.todo:nth-child(1) .edit', 'edited again!') - .assert.count('.todo.editing', 0) - .assert.containsText('.todo:nth-child(1) label', 'edited again!') - - // cancel - browser - .dblClick('.todo label') - .clearValue('.todo:nth-child(1) .edit') - .setValue('.todo:nth-child(1) .edit', 'edited!') - .trigger('.todo:nth-child(1) .edit', 'keyup', 27) - .assert.count('.todo.editing', 0) - .assert.containsText('.todo:nth-child(1) label', 'edited again!') - - // empty value should remove - browser - .dblClick('.todo label') - .enterValue('.todo:nth-child(1) .edit', ' ') - .assert.count('.todo', 3) - - // toggle all - browser - .click('.toggle-all') - .assert.count('.todo.completed', 3) - .click('.toggle-all') - .assert.count('.todo:not(.completed)', 3) - .end() - - function createNewItem (text) { - return browser.enterValue('.new-todo', text) - } - - function removeItemAt (n) { - return browser - .moveToElement('.todo:nth-child(' + n + ')', 10, 10) - .click('.todo:nth-child(' + n + ') .destroy') - } - } -} diff --git a/test/e2e/specs/tree.js b/test/e2e/specs/tree.js deleted file mode 100644 index 92ae25d778f..00000000000 --- a/test/e2e/specs/tree.js +++ /dev/null @@ -1,72 +0,0 @@ -module.exports = { - 'tree': function (browser) { - browser - .url('http://localhost:8080/examples/tree/') - .waitForElementVisible('li', 1000) - .assert.count('.item', 12) - .assert.count('.add', 4) - .assert.count('.item > ul', 4) - .assert.notVisible('#demo li ul') - .assert.containsText('#demo li div span', '[+]') - - // expand root - .click('.bold') - .assert.visible('#demo ul') - .assert.evaluate(function () { - return document.querySelector('#demo li ul').children.length === 4 - }) - .assert.containsText('#demo li div span', '[-]') - .assert.containsText('#demo > .item > ul > .item:nth-child(1)', 'hello') - .assert.containsText('#demo > .item > ul > .item:nth-child(2)', 'wat') - .assert.containsText('#demo > .item > ul > .item:nth-child(3)', 'child folder') - .assert.containsText('#demo > .item > ul > .item:nth-child(3)', '[+]') - - // add items to root - .click('#demo > .item > ul > .add') - .assert.evaluate(function () { - return document.querySelector('#demo li ul').children.length === 5 - }) - .assert.containsText('#demo > .item > ul > .item:nth-child(1)', 'hello') - .assert.containsText('#demo > .item > ul > .item:nth-child(2)', 'wat') - .assert.containsText('#demo > .item > ul > .item:nth-child(3)', 'child folder') - .assert.containsText('#demo > .item > ul > .item:nth-child(3)', '[+]') - .assert.containsText('#demo > .item > ul > .item:nth-child(4)', 'new stuff') - - // add another item - .click('#demo > .item > ul > .add') - .assert.evaluate(function () { - return document.querySelector('#demo li ul').children.length === 6 - }) - .assert.containsText('#demo > .item > ul > .item:nth-child(1)', 'hello') - .assert.containsText('#demo > .item > ul > .item:nth-child(2)', 'wat') - .assert.containsText('#demo > .item > ul > .item:nth-child(3)', 'child folder') - .assert.containsText('#demo > .item > ul > .item:nth-child(3)', '[+]') - .assert.containsText('#demo > .item > ul > .item:nth-child(4)', 'new stuff') - .assert.containsText('#demo > .item > ul > .item:nth-child(5)', 'new stuff') - - .click('#demo ul .bold') - .assert.visible('#demo ul ul') - .assert.containsText('#demo ul > .item:nth-child(3)', '[-]') - .assert.evaluate(function () { - return document.querySelector('#demo ul ul').children.length === 5 - }) - - .click('.bold') - .assert.notVisible('#demo ul') - .assert.containsText('#demo li div span', '[+]') - .click('.bold') - .assert.visible('#demo ul') - .assert.containsText('#demo li div span', '[-]') - - .dblClick('#demo ul > .item div') - .assert.count('.item', 15) - .assert.count('.item > ul', 5) - .assert.containsText('#demo ul > .item:nth-child(1)', '[-]') - .assert.evaluate(function () { - var firstItem = document.querySelector('#demo ul > .item:nth-child(1)') - var ul = firstItem.querySelector('ul') - return ul.children.length === 2 - }) - .end() - } -} diff --git a/test/e2e/svg.spec.ts b/test/e2e/svg.spec.ts new file mode 100644 index 00000000000..5032ca4c664 --- /dev/null +++ b/test/e2e/svg.spec.ts @@ -0,0 +1,151 @@ +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.spec.ts b/test/e2e/todomvc.spec.ts new file mode 100644 index 00000000000..f1de97e6354 --- /dev/null +++ b/test/e2e/todomvc.spec.ts @@ -0,0 +1,182 @@ +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.spec.ts b/test/e2e/tree.spec.ts new file mode 100644 index 00000000000..970aaa5eaf2 --- /dev/null +++ b/test/e2e/tree.spec.ts @@ -0,0 +1,108 @@ +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/.eslintrc b/test/helpers/.eslintrc deleted file mode 100644 index de3ffc6bd74..00000000000 --- a/test/helpers/.eslintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "env": { - "jasmine": true - }, - "globals": { - "waitForUpdate": true - } -} diff --git a/test/helpers/classlist.js b/test/helpers/classlist.js deleted file mode 100644 index 29e04b84ed0..00000000000 --- a/test/helpers/classlist.js +++ /dev/null @@ -1,18 +0,0 @@ -beforeEach(() => { - jasmine.addMatchers({ - // since classList may not be supported in all browsers - toHaveClass: () => { - return { - compare: (el, cls) => { - 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/classlist.ts b/test/helpers/classlist.ts new file mode 100644 index 00000000000..2e617e6b255 --- /dev/null +++ b/test/helpers/classlist.ts @@ -0,0 +1,12 @@ +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 new file mode 100644 index 00000000000..902d46a31bb --- /dev/null +++ b/test/helpers/shim-done.ts @@ -0,0 +1,36 @@ +// 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.js b/test/helpers/test-object-option.js deleted file mode 100644 index 058929291c8..00000000000 --- a/test/helpers/test-object-option.js +++ /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/test-object-option.ts b/test/helpers/test-object-option.ts new file mode 100644 index 00000000000..39be7ee8b29 --- /dev/null +++ b/test/helpers/test-object-option.ts @@ -0,0 +1,17 @@ +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-equal.js b/test/helpers/to-equal.js deleted file mode 100644 index 33308bdc25f..00000000000 --- a/test/helpers/to-equal.js +++ /dev/null @@ -1,19 +0,0 @@ -import { isEqual } from 'lodash' - -beforeEach(() => { - jasmine.addMatchers({ - // override built-in toEqual because it behaves incorrectly - // on Vue-observed arrays in Safari - toEqual: () => { - return { - compare: (a, b) => { - const pass = isEqual(a, b) - return { - pass, - message: `Expected ${a} to equal ${b}` - } - } - } - } - }) -}) diff --git a/test/helpers/to-have-been-warned.js b/test/helpers/to-have-been-warned.js deleted file mode 100644 index 097b0e6e02b..00000000000 --- a/test/helpers/to-have-been-warned.js +++ /dev/null @@ -1,70 +0,0 @@ -function noop () {} - -if (typeof console === 'undefined') { - window.console = { - warn: noop, - error: noop - } -} - -// avoid info messages during test -console.info = noop - -let asserted - -function createCompareFn (spy) { - const hasWarned = msg => { - var count = spy.calls.count() - var args - while (count--) { - args = spy.calls.argsFor(count) - if (args.some(containsMsg)) { - return true - } - } - - function containsMsg (arg) { - return arg.toString().indexOf(msg) > -1 - } - } - - return { - compare: msg => { - asserted = asserted.concat(msg) - var warned = Array.isArray(msg) - ? msg.some(hasWarned) - : hasWarned(msg) - return { - pass: warned, - message: warned - ? 'Expected message "' + msg + '" not to have been warned' - : 'Expected message "' + msg + '" to have been warned' - } - } - } -} - -// define custom matcher for warnings -beforeEach(() => { - asserted = [] - spyOn(console, 'warn') - spyOn(console, 'error') - jasmine.addMatchers({ - toHaveBeenWarned: () => createCompareFn(console.error), - toHaveBeenTipped: () => createCompareFn(console.warn) - }) -}) - -afterEach(done => { - const warned = msg => asserted.some(assertedMsg => msg.toString().indexOf(assertedMsg) > -1) - let count = console.error.calls.count() - let args - while (count--) { - args = console.error.calls.argsFor(count) - if (!warned(args[0])) { - done.fail(`Unexpected console.error message: ${args[0]}`) - return - } - } - done() -}) diff --git a/test/helpers/to-have-warned.ts b/test/helpers/to-have-warned.ts new file mode 100644 index 00000000000..cba4bc0cafc --- /dev/null +++ b/test/helpers/to-have-warned.ts @@ -0,0 +1,118 @@ +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.js b/test/helpers/trigger-event.js deleted file mode 100644 index 12ea7e2b9ba..00000000000 --- a/test/helpers/trigger-event.js +++ /dev/null @@ -1,6 +0,0 @@ -window.triggerEvent = function triggerEvent (target, event, process) { - var e = document.createEvent('HTMLEvents') - e.initEvent(event, true, true) - if (process) process(e) - target.dispatchEvent(e) -} diff --git a/test/helpers/trigger-event.ts b/test/helpers/trigger-event.ts new file mode 100644 index 00000000000..cbe5e76531e --- /dev/null +++ b/test/helpers/trigger-event.ts @@ -0,0 +1,10 @@ +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.js b/test/helpers/vdom.js deleted file mode 100644 index abfb189054b..00000000000 --- a/test/helpers/vdom.js +++ /dev/null @@ -1,5 +0,0 @@ -import VNode from 'core/vdom/vnode' - -window.createTextVNode = function (text) { - return new VNode(undefined, undefined, undefined, text) -} diff --git a/test/helpers/vdom.ts b/test/helpers/vdom.ts new file mode 100644 index 00000000000..ab4af6e9883 --- /dev/null +++ b/test/helpers/vdom.ts @@ -0,0 +1,5 @@ +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.js b/test/helpers/wait-for-update.js deleted file mode 100644 index 37f87dbd3cd..00000000000 --- a/test/helpers/wait-for-update.js +++ /dev/null @@ -1,73 +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) -window.waitForUpdate = initialCb => { - let end - const queue = 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) -} diff --git a/test/helpers/wait-for-update.ts b/test/helpers/wait-for-update.ts new file mode 100644 index 00000000000..9adf35fcd63 --- /dev/null +++ b/test/helpers/wait-for-update.ts @@ -0,0 +1,81 @@ +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/ssr/.eslintrc b/test/ssr/.eslintrc deleted file mode 100644 index dab5d0ea656..00000000000 --- a/test/ssr/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "env": { - "jasmine": true - }, - "plugins": ["jasmine"], - "rules": { - "jasmine/no-focused-tests": 2 - } -} diff --git a/test/ssr/compile-with-webpack.js b/test/ssr/compile-with-webpack.js deleted file mode 100644 index 765898e966d..00000000000 --- a/test/ssr/compile-with-webpack.js +++ /dev/null @@ -1,38 +0,0 @@ -import path from 'path' -import webpack from 'webpack' -import MemoryFS from 'memory-fs' - -export function compileWithWebpack (file, extraConfig, cb) { - const config = Object.assign({ - entry: path.resolve(__dirname, 'fixtures', file), - module: { - rules: [ - { - test: /\.js$/, - loader: 'babel-loader' - }, - { - test: /async-.*\.js$/, - loader: require.resolve('./async-loader') - }, - { - test: /\.(png|woff2|css)$/, - loader: 'file-loader', - options: { - name: '[name].[ext]' - } - } - ] - } - }, extraConfig) - - const compiler = webpack(config) - const fs = new MemoryFS() - compiler.outputFileSystem = fs - - compiler.run((err, stats) => { - expect(err).toBeFalsy() - expect(stats.errors).toBeFalsy() - cb(fs) - }) -} diff --git a/test/ssr/fixtures/app.js b/test/ssr/fixtures/app.js deleted file mode 100644 index 85bb2888fb6..00000000000 --- a/test/ssr/fixtures/app.js +++ /dev/null @@ -1,12 +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/test/ssr/fixtures/cache.js b/test/ssr/fixtures/cache.js deleted file mode 100644 index 29016771d7c..00000000000 --- a/test/ssr/fixtures/cache.js +++ /dev/null @@ -1,16 +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/test/ssr/fixtures/nested-cache.js b/test/ssr/fixtures/nested-cache.js deleted file mode 100644 index 9f2656858f3..00000000000 --- a/test/ssr/fixtures/nested-cache.js +++ /dev/null @@ -1,49 +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/test/ssr/fixtures/split.js b/test/ssr/fixtures/split.js deleted file mode 100644 index 5cd38508f2b..00000000000 --- a/test/ssr/fixtures/split.js +++ /dev/null @@ -1,26 +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) - resolve(vm) - }) - }) -} diff --git a/test/ssr/jasmine.json b/test/ssr/jasmine.json deleted file mode 100644 index 345ed149d3c..00000000000 --- a/test/ssr/jasmine.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "spec_dir": "test/ssr", - "spec_files": [ - "*.spec.js" - ], - "helpers": [ - "../../node_modules/babel-register/lib/node.js" - ] -} diff --git a/test/ssr/ssr-basic-renderer.spec.js b/test/ssr/ssr-basic-renderer.spec.js deleted file mode 100644 index 7cbacbee726..00000000000 --- a/test/ssr/ssr-basic-renderer.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -import Vue from '../../dist/vue.runtime.common.js' -import renderToString from '../../packages/vue-server-renderer/basic' - -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 peoperly when accessing $ssrContext in root component', done => { - let ssrContext - renderToString(new Vue({ - template: ` - <div></div> - `, - created () { - ssrContext = this.$ssrContext - } - }), (err, result) => { - expect(err).toBeNull() - expect(ssrContext).toBeUndefined() - done() - }) - }) -}) diff --git a/test/ssr/ssr-bundle-render.spec.js b/test/ssr/ssr-bundle-render.spec.js deleted file mode 100644 index 8ea05b32172..00000000000 --- a/test/ssr/ssr-bundle-render.spec.js +++ /dev/null @@ -1,309 +0,0 @@ -import LRU from 'lru-cache' -import { compileWithWebpack } from './compile-with-webpack' -import { createBundleRenderer } from '../../packages/vue-server-renderer' -import VueSSRServerPlugin from '../../packages/vue-server-renderer/server-plugin' - -export function createRenderer (file, options, cb) { - if (typeof options === 'function') { - cb = options - options = undefined - } - const asBundle = !!(options && options.asBundle) - if (options) delete options.asBundle - - 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()] - : [] - }, fs => { - const bundle = asBundle - ? JSON.parse(fs.readFileSync('/vue-ssr-server-bundle.json', 'utf-8')) - : fs.readFileSync('/bundle.js', 'utf-8') - const renderer = createBundleRenderer(bundle, options) - cb(renderer) - }) -} - -describe('SSR: bundle renderer', () => { - createAssertions(true) - createAssertions(false) -}) - -function createAssertions (runInNewContext) { - it('renderToString', done => { - createRenderer('app.js', { runInNewContext }, renderer => { - const context = { url: '/test' } - renderer.renderToString(context, (err, res) => { - expect(err).toBeNull() - expect(res).toBe('<div data-server-rendered="true">/test</div>') - expect(context.msg).toBe('hello') - done() - }) - }) - }) - - it('renderToStream', done => { - createRenderer('app.js', { runInNewContext }, renderer => { - const context = { url: '/test' } - const stream = renderer.renderToStream(context) - let res = '' - stream.on('data', chunk => { - res += chunk.toString() - }) - stream.on('end', () => { - expect(res).toBe('<div data-server-rendered="true">/test</div>') - expect(context.msg).toBe('hello') - done() - }) - }) - }) - - it('renderToString catch error', done => { - createRenderer('error.js', { runInNewContext }, renderer => { - renderer.renderToString(err => { - expect(err.message).toBe('foo') - done() - }) - }) - }) - - it('renderToString catch Promise rejection', done => { - createRenderer('promise-rejection.js', { runInNewContext }, renderer => { - renderer.renderToString(err => { - expect(err.message).toBe('foo') - done() - }) - }) - }) - - it('renderToStream catch error', done => { - createRenderer('error.js', { runInNewContext }, renderer => { - const stream = renderer.renderToStream() - stream.on('error', err => { - expect(err.message).toBe('foo') - done() - }) - }) - }) - - it('renderToStream catch Promise rejection', done => { - createRenderer('promise-rejection.js', { runInNewContext }, renderer => { - const stream = renderer.renderToStream() - stream.on('error', err => { - expect(err.message).toBe('foo') - done() - }) - }) - }) - - it('render with cache (get/set)', done => { - const cache = {} - const get = jasmine.createSpy('get') - const set = jasmine.createSpy('set') - const options = { - runInNewContext, - cache: { - // async - get: (key, cb) => { - setTimeout(() => { - get(key) - cb(cache[key]) - }, 0) - }, - set: (key, val) => { - set(key, val) - cache[key] = val - } - } - } - createRenderer('cache.js', options, renderer => { - const expected = '<div data-server-rendered="true">/test</div>' - const key = 'app::1' - renderer.renderToString((err, res) => { - expect(err).toBeNull() - expect(res).toBe(expected) - expect(get).toHaveBeenCalledWith(key) - const setArgs = set.calls.argsFor(0) - expect(setArgs[0]).toBe(key) - expect(setArgs[1].html).toBe(expected) - expect(cache[key].html).toBe(expected) - renderer.renderToString((err, res) => { - expect(err).toBeNull() - expect(res).toBe(expected) - expect(get.calls.count()).toBe(2) - expect(set.calls.count()).toBe(1) - done() - }) - }) - }) - }) - - it('render with cache (get/set/has)', done => { - const cache = {} - const has = jasmine.createSpy('has') - const get = jasmine.createSpy('get') - const set = jasmine.createSpy('set') - 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 - } - } - } - createRenderer('cache.js', options, renderer => { - const expected = '<div data-server-rendered="true">/test</div>' - const key = 'app::1' - renderer.renderToString((err, res) => { - expect(err).toBeNull() - expect(res).toBe(expected) - expect(has).toHaveBeenCalledWith(key) - expect(get).not.toHaveBeenCalled() - const setArgs = set.calls.argsFor(0) - expect(setArgs[0]).toBe(key) - expect(setArgs[1].html).toBe(expected) - expect(cache[key].html).toBe(expected) - renderer.renderToString((err, res) => { - expect(err).toBeNull() - expect(res).toBe(expected) - expect(has.calls.count()).toBe(2) - expect(get.calls.count()).toBe(1) - expect(set.calls.count()).toBe(1) - done() - }) - }) - }) - }) - - it('render with cache (nested)', done => { - const cache = LRU({ maxAge: Infinity }) - spyOn(cache, 'get').and.callThrough() - spyOn(cache, 'set').and.callThrough() - const options = { - cache, - runInNewContext - } - createRenderer('nested-cache.js', options, renderer => { - const expected = '<div data-server-rendered="true">/test</div>' - const key = 'app::1' - const context1 = { registered: [] } - const context2 = { registered: [] } - renderer.renderToString(context1, (err, res) => { - expect(err).toBeNull() - expect(res).toBe(expected) - expect(cache.set.calls.count()).toBe(3) // 3 nested components cached - const cached = cache.get(key) - expect(cached.html).toBe(expected) - expect(cache.get.calls.count()).toBe(1) - - // assert component usage registration for nested children - expect(context1.registered).toEqual(['app', 'child', 'grandchild']) - - renderer.renderToString(context2, (err, res) => { - expect(err).toBeNull() - expect(res).toBe(expected) - expect(cache.set.calls.count()).toBe(3) // no new cache sets - expect(cache.get.calls.count()).toBe(2) // 1 get for root - - expect(context2.registered).toEqual(['app', 'child', 'grandchild']) - done() - }) - }) - }) - }) - - it('renderToString (bundle format with code split)', done => { - createRenderer('split.js', { runInNewContext, asBundle: true }, renderer => { - const context = { url: '/test' } - renderer.renderToString(context, (err, res) => { - expect(err).toBeNull() - expect(res).toBe('<div data-server-rendered="true">/test<div>async test.woff2 test.png</div></div>') - done() - }) - }) - }) - - it('renderToStream (bundle format with code split)', done => { - createRenderer('split.js', { runInNewContext, asBundle: true }, renderer => { - const context = { url: '/test' } - const stream = renderer.renderToStream(context) - let res = '' - stream.on('data', chunk => { - res += chunk.toString() - }) - stream.on('end', () => { - expect(res).toBe('<div data-server-rendered="true">/test<div>async test.woff2 test.png</div></div>') - done() - }) - }) - }) - - it('renderToString catch error (bundle format with source map)', done => { - createRenderer('error.js', { runInNewContext, asBundle: true }, renderer => { - renderer.renderToString(err => { - expect(err.stack).toContain('test/ssr/fixtures/error.js:1:6') - expect(err.message).toBe('foo') - done() - }) - }) - }) - - it('renderToString catch error (bundle format with source map)', done => { - createRenderer('error.js', { runInNewContext, asBundle: true }, renderer => { - const stream = renderer.renderToStream() - stream.on('error', err => { - expect(err.stack).toContain('test/ssr/fixtures/error.js:1:6') - expect(err.message).toBe('foo') - done() - }) - }) - }) - - it('renderToString return Promise', done => { - createRenderer('app.js', { runInNewContext }, renderer => { - const context = { url: '/test' } - renderer.renderToString(context).then(res => { - expect(res).toBe('<div data-server-rendered="true">/test</div>') - expect(context.msg).toBe('hello') - done() - }) - }) - }) - - it('renderToString return Promise (error)', done => { - createRenderer('error.js', { runInNewContext }, renderer => { - renderer.renderToString().catch(err => { - expect(err.message).toBe('foo') - done() - }) - }) - }) - - it('renderToString return Promise (Promise rejection)', done => { - createRenderer('promise-rejection.js', { runInNewContext }, renderer => { - renderer.renderToString().catch(err => { - expect(err.message).toBe('foo') - done() - }) - }) - }) -} diff --git a/test/ssr/ssr-stream.spec.js b/test/ssr/ssr-stream.spec.js deleted file mode 100644 index b18707abff3..00000000000 --- a/test/ssr/ssr-stream.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -import Vue from '../../dist/vue.runtime.common.js' -import { createRenderer } from '../../packages/vue-server-renderer' -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 => { - Vue.config.silent = true - const stream = renderToStream(new Vue({ - render () { - throw new Error('oops') - } - })) - stream.on('error', err => { - expect(err.toString()).toMatch(/oops/) - Vue.config.silent = false - 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' - }) - var stream1 = renderToStream(component1) - var stream2 = renderToStream(component2) - var 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) - }) -}) diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js deleted file mode 100644 index 3e8470f9250..00000000000 --- a/test/ssr/ssr-string.spec.js +++ /dev/null @@ -1,1122 +0,0 @@ -import Vue from '../../dist/vue.runtime.common.js' -import VM from 'vm' -import { createRenderer } from '../../packages/vue-server-renderer' -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 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 <span>rendering</span></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"><span>foo</span></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><span>foo</span></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('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', 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('_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 => { - Vue.config.silent = true - renderToString(new Vue({ - render () { - throw new Error('oops') - } - }), err => { - expect(err instanceof Error).toBe(true) - Vue.config.silent = false - 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 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><foo></div>` - }, res => { - expect(res).toBe(`<div data-server-rendered="true"><foo></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() - }) - }) - - it('return Promise', done => { - renderToString(new Vue({ - template: `<div>{{ foo }}</div>`, - data: { foo: 'bar' } - })).then(res => { - expect(res).toBe(`<div data-server-rendered="true">bar</div>`) - done() - }) - }) - - it('return Promise (error)', done => { - Vue.config.silent = true - renderToString(new Vue({ - render () { - throw new Error('foobar') - } - })).catch(err => { - expect(err.toString()).toContain(`foobar`) - Vue.config.silent = false - done() - }) - }) - - it('should catch template compilation error', done => { - renderToString(new Vue({ - template: `<div></div><div></div>` - }), (err, res) => { - 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() - }) - }) -}) - -function renderVmWithOptions (options, cb) { - renderToString(new Vue(options), (err, res) => { - expect(err).toBeNull() - cb(res) - }) -} diff --git a/test/ssr/ssr-template.spec.js b/test/ssr/ssr-template.spec.js deleted file mode 100644 index ad7c0413850..00000000000 --- a/test/ssr/ssr-template.spec.js +++ /dev/null @@ -1,390 +0,0 @@ -import webpack from 'webpack' -import Vue from '../../dist/vue.runtime.common.js' -import { compileWithWebpack } from './compile-with-webpack' -import { createRenderer } from '../../packages/vue-server-renderer' -import VueSSRClientPlugin from '../../packages/vue-server-renderer/client-plugin' -import { createRenderer as createBundleRenderer } from './ssr-bundle-render.spec.js' - -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>` - -function generateClientManifest (file, cb) { - compileWithWebpack(file, { - output: { - path: '/', - filename: '[name].js' - }, - plugins: [ - new webpack.optimize.CommonsChunkPlugin({ - name: 'manifest', - minChunks: Infinity - }), - new VueSSRClientPlugin() - ] - }, fs => { - cb(JSON.parse(fs.readFileSync('/vue-ssr-client-manifest.json', 'utf-8'))) - }) -} - -function createRendererWithManifest (file, options, cb) { - if (typeof options === 'function') { - cb = options - options = null - } - generateClientManifest(file, clientManifest => { - createBundleRenderer(file, Object.assign({ - asBundle: true, - template: defaultTemplate, - clientManifest - }, options), cb) - }) -} - -describe('SSR: template option', () => { - it('renderToString', done => { - const renderer = createRenderer({ - template: defaultTemplate - }) - - const context = { - head: '<meta name="viewport" content="width=device-width">', - styles: '<style>h1 { color: red }</style>', - state: { a: 1 } - } - - renderer.renderToString(new Vue({ - template: '<div>hi</div>' - }), context, (err, res) => { - expect(err).toBeNull() - 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>` - ) - done() - }) - }) - - it('renderToString with interpolation', done => { - 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 } - } - - renderer.renderToString(new Vue({ - template: '<div>hi</div>' - }), context, (err, res) => { - expect(err).toBeNull() - expect(res).toContain( - `<html><head>` + - // double mustache should be escaped - `<title><script>hacks</script></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>` - ) - done() - }) - }) - - it('renderToStream', done => { - 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 stream = renderer.renderToStream(new Vue({ - template: '<div>hi</div>' - }), context) - - let res = '' - stream.on('data', chunk => { - res += chunk - }) - stream.on('end', () => { - 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>` - ) - done() - }) - }) - - it('renderToStream with interpolation', done => { - 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 stream = renderer.renderToStream(new Vue({ - template: '<div>hi</div>' - }), context) - - let res = '' - stream.on('data', chunk => { - res += chunk - }) - stream.on('end', () => { - expect(res).toContain( - `<html><head>` + - // double mustache should be escaped - `<title><script>hacks</script></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>` - ) - done() - }) - }) - - it('bundleRenderer + renderToString', done => { - createBundleRenderer('app.js', { - asBundle: true, - template: defaultTemplate - }, renderer => { - const context = { - head: '<meta name="viewport" content="width=device-width">', - styles: '<style>h1 { color: red }</style>', - state: { a: 1 }, - url: '/test' - } - renderer.renderToString(context, (err, res) => { - expect(err).toBeNull() - 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') - done() - }) - }) - }) - - it('bundleRenderer + renderToStream', done => { - createBundleRenderer('app.js', { - asBundle: true, - template: defaultTemplate - }, renderer => { - const context = { - head: '<meta name="viewport" content="width=device-width">', - styles: '<style>h1 { color: red }</style>', - state: { a: 1 }, - url: '/test' - } - const stream = renderer.renderToStream(context) - let res = '' - stream.on('data', chunk => { - res += chunk.toString() - }) - stream.on('end', () => { - 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') - done() - }) - }) - }) - - const expectedHTMLWithManifest = (options = {}) => - `<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 ()', done => { - createRendererWithManifest('split.js', { runInNewContext }, renderer => { - renderer.renderToString({ state: { a: 1 }}, (err, res) => { - expect(err).toBeNull() - expect(res).toContain(expectedHTMLWithManifest()) - done() - }) - }) - }) - - it('bundleRenderer + renderToStream + clientManifest + shouldPreload', done => { - createRendererWithManifest('split.js', { - runInNewContext, - shouldPreload: (file, type) => { - if (type === 'image' || type === 'script' || type === 'font' || type === 'style') { - return true - } - } - }, renderer => { - const stream = renderer.renderToStream({ state: { a: 1 }}) - let res = '' - stream.on('data', chunk => { - res += chunk.toString() - }) - stream.on('end', () => { - expect(res).toContain(expectedHTMLWithManifest({ - preloadOtherAssets: true - })) - done() - }) - }) - }) - - it('bundleRenderer + renderToStream + clientManifest + shouldPrefetch', done => { - createRendererWithManifest('split.js', { - runInNewContext, - shouldPrefetch: (file, type) => { - if (type === 'script') { - return false - } - } - }, renderer => { - const stream = renderer.renderToStream({ state: { a: 1 }}) - let res = '' - stream.on('data', chunk => { - res += chunk.toString() - }) - stream.on('end', () => { - expect(res).toContain(expectedHTMLWithManifest({ - noPrefetch: true - })) - done() - }) - }) - }) - - it('bundleRenderer + renderToString + clientManifest + inject: false', done => { - createRendererWithManifest('split.js', { - runInNewContext, - template: `<html>` + - `<head>{{{ renderResourceHints() }}}{{{ renderStyles() }}}</head>` + - `<body><!--vue-ssr-outlet-->{{{ renderState({ windowKey: '__FOO__', contextKey: 'foo' }) }}}{{{ renderScripts() }}}</body>` + - `</html>`, - inject: false - }, renderer => { - const context = { foo: { a: 1 }} - renderer.renderToString(context, (err, res) => { - expect(err).toBeNull() - expect(res).toContain(expectedHTMLWithManifest({ - stateKey: '__FOO__' - })) - done() - }) - }) - }) - - it('bundleRenderer + renderToString + clientManifest + no template', done => { - createRendererWithManifest('split.js', { - runInNewContext, - template: null - }, renderer => { - const context = { foo: { a: 1 }} - renderer.renderToString(context, (err, res) => { - expect(err).toBeNull() - - 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__' - })) - done() - }) - }) - }) - - it('whitespace insensitive interpolation', done => { - 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 } - } - - renderer.renderToString(new Vue({ - template: '<div>hi</div>' - }), context, (err, res) => { - expect(err).toBeNull() - expect(res).toContain( - `<html><head>` + - // double mustache should be escaped - `<title><script>hacks</script></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>` - ) - done() - }) - }) - } -}) diff --git a/test/test-env.d.ts b/test/test-env.d.ts new file mode 100644 index 00000000000..be1fa6a7d85 --- /dev/null +++ b/test/test-env.d.ts @@ -0,0 +1,36 @@ +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 new file mode 100644 index 00000000000..a47fd01cb55 --- /dev/null +++ b/test/transition/helpers.ts @@ -0,0 +1,144 @@ +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 new file mode 100644 index 00000000000..02a439e3b3a --- /dev/null +++ b/test/transition/karma.conf.js @@ -0,0 +1,28 @@ +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 new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/test/transition/package.json @@ -0,0 +1 @@ +{} diff --git a/test/transition/transition-group.spec.ts b/test/transition/transition-group.spec.ts new file mode 100644 index 00000000000..2c1905dbb7e --- /dev/null +++ b/test/transition/transition-group.spec.ts @@ -0,0 +1,392 @@ +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 new file mode 100644 index 00000000000..779dd33f7e2 --- /dev/null +++ b/test/transition/transition-mode.spec.ts @@ -0,0 +1,685 @@ +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 new file mode 100644 index 00000000000..128255e0981 --- /dev/null +++ b/test/transition/transition-with-keep-alive.spec.ts @@ -0,0 +1,654 @@ +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 new file mode 100644 index 00000000000..bff2e0fefd7 --- /dev/null +++ b/test/transition/transition.spec.ts @@ -0,0 +1,1842 @@ +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 new file mode 100644 index 00000000000..594e420687c --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["node", "vitest/globals"] + }, + "include": ["../src", "."] +} diff --git a/test/unit/.eslintrc b/test/unit/.eslintrc deleted file mode 100644 index 8334c32a015..00000000000 --- a/test/unit/.eslintrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "env": { - "jasmine": true - }, - "globals": { - "waitForUpdate": true, - "triggerEvent": true, - "createTextVNode": true - }, - "plugins": ["jasmine"], - "rules": { - "jasmine/no-focused-tests": 2 - } -} diff --git a/test/unit/features/component/component-async.spec.js b/test/unit/features/component/component-async.spec.js deleted file mode 100644 index a63b8c0e0da..00000000000 --- a/test/unit/features/component/component-async.spec.js +++ /dev/null @@ -1,346 +0,0 @@ -import Vue from 'vue' -import { Promise } from 'es6-promise' - -describe('Component async', () => { - 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 => { - var 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() - var 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(function () { - 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) - }) - }) -}) diff --git a/test/unit/features/component/component-async.spec.ts b/test/unit/features/component/component-async.spec.ts new file mode 100644 index 00000000000..ec1ec2484fe --- /dev/null +++ b/test/unit/features/component/component-async.spec.ts @@ -0,0 +1,455 @@ +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.js b/test/unit/features/component/component-keep-alive.spec.js deleted file mode 100644 index d6dad4e1d6d..00000000000 --- a/test/unit/features/component/component-keep-alive.spec.js +++ /dev/null @@ -1,1016 +0,0 @@ -import Vue from 'vue' -import injectStyles from '../transition/inject-styles' -import { isIE9 } from 'core/util/env' -import { nextFrame } from 'web/runtime/transition-util' - -describe('Component keep-alive', () => { - const { duration, buffer } = injectStyles() - let components, one, two, el - beforeEach(() => { - one = { - template: '<div>one</div>', - created: jasmine.createSpy('one created'), - mounted: jasmine.createSpy('one mounted'), - activated: jasmine.createSpy('one activated'), - deactivated: jasmine.createSpy('one deactivated'), - destroyed: jasmine.createSpy('one destroyed') - } - two = { - template: '<div>two</div>', - created: jasmine.createSpy('two created'), - mounted: jasmine.createSpy('two mounted'), - activated: jasmine.createSpy('two activated'), - deactivated: jasmine.createSpy('two deactivated'), - destroyed: jasmine.createSpy('two destroyed') - } - 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('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() - - var 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('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) - }) - - if (!isIE9) { - 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).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('bar resolved') - 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) - } - }) - - it('max', done => { - const spyA = jasmine.createSpy() - const spyB = jasmine.createSpy() - const spyC = jasmine.createSpy() - const spyAD = jasmine.createSpy() - const spyBD = jasmine.createSpy() - const spyCD = jasmine.createSpy() - - function assertCount (calls) { - expect([ - spyA.calls.count(), - spyAD.calls.count(), - spyB.calls.count(), - spyBD.calls.count(), - spyC.calls.count(), - spyCD.calls.count() - ]).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) - }) - } -}) diff --git a/test/unit/features/component/component-keep-alive.spec.ts b/test/unit/features/component/component-keep-alive.spec.ts new file mode 100644 index 00000000000..28722d0c6a6 --- /dev/null +++ b/test/unit/features/component/component-keep-alive.spec.ts @@ -0,0 +1,833 @@ +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.js b/test/unit/features/component/component-scoped-slot.spec.js deleted file mode 100644 index 4fab0127ea7..00000000000 --- a/test/unit/features/component/component-scoped-slot.spec.js +++ /dev/null @@ -1,596 +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>') - }) - - // #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) - }) -}) diff --git a/test/unit/features/component/component-scoped-slot.spec.ts b/test/unit/features/component/component-scoped-slot.spec.ts new file mode 100644 index 00000000000..96c23f8c66c --- /dev/null +++ b/test/unit/features/component/component-scoped-slot.spec.ts @@ -0,0 +1,1406 @@ +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.js b/test/unit/features/component/component-slot.spec.js deleted file mode 100644 index 4e6deac39cb..00000000000 --- a/test/unit/features/component/component-slot.spec.js +++ /dev/null @@ -1,782 +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('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></div></test>', - components: { - test: { - render () { - return this.$slots.default - } - } - } - }).$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 = [] - 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('warn duplicate slots', () => { - new Vue({ - template: `<div> - <test> - <div>foo</div> - <div slot="a">bar</div> - </test> - </div>`, - components: { - test: { - template: `<div> - <slot></slot><slot></slot> - <div v-for="i in 3"><slot name="a"></slot></div> - </div>` - } - } - }).$mount() - expect('Duplicate presence of slot "default"').toHaveBeenWarned() - expect('Duplicate presence of slot "a"').toHaveBeenWarned() - }) - - it('should not warn valid conditional slots', () => { - new Vue({ - template: `<div> - <test> - <div>foo</div> - </test> - </div>`, - components: { - test: { - template: `<div> - <slot v-if="true"></slot> - <slot v-else></slot> - </div>` - } - } - }).$mount() - expect('Duplicate presence of slot "default"').not.toHaveBeenWarned() - }) - - // #3518 - it('events should not break when slot is toggled by v-if', done => { - const spy = jasmine.createSpy() - 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() - - expect(vm.$el.textContent).toBe('hi') - vm.$children[0].toggle = false - waitForUpdate(() => { - vm.$children[0].toggle = true - }).then(() => { - triggerEvent(vm.$el.querySelector('.click'), 'click') - expect(spy).toHaveBeenCalled() - }).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>') - }) -}) diff --git a/test/unit/features/component/component-slot.spec.ts b/test/unit/features/component/component-slot.spec.ts new file mode 100644 index 00000000000..da57d328aff --- /dev/null +++ b/test/unit/features/component/component-slot.spec.ts @@ -0,0 +1,1076 @@ +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.js b/test/unit/features/component/component.spec.js deleted file mode 100644 index 52744c68bad..00000000000 --- a/test/unit/features/component/component.spec.js +++ /dev/null @@ -1,430 +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 in comps" :key="c.type" :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 = jasmine.createSpy() - 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 = null - }).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) - }) -}) diff --git a/test/unit/features/component/component.spec.ts b/test/unit/features/component/component.spec.ts new file mode 100644 index 00000000000..510ad2473ba --- /dev/null +++ b/test/unit/features/component/component.spec.ts @@ -0,0 +1,459 @@ +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.js b/test/unit/features/debug.spec.js deleted file mode 100644 index 5fb5b48270c..00000000000 --- a/test/unit/features/debug.spec.js +++ /dev/null @@ -1,117 +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', () => { - Vue.config.warnHandler = jasmine.createSpy() - - warn(msg, vm) - - expect(Vue.config.warnHandler).toHaveBeenCalledWith(msg, vm, jasmine.any(String)) - - 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/debug.spec.ts b/test/unit/features/debug.spec.ts new file mode 100644 index 00000000000..49398b4c1f7 --- /dev/null +++ b/test/unit/features/debug.spec.ts @@ -0,0 +1,121 @@ +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.js b/test/unit/features/directives/bind.spec.js deleted file mode 100644 index e9ab1bf7361..00000000000 --- a/test/unit/features/directives/bind.spec.js +++ /dev/null @@ -1,414 +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 :draggable="foo">hello</span></div>', - data: { foo: true } - }).$mount() - expect(vm.$el.firstChild.getAttribute('draggable')).toBe('true') - vm.foo = 'again' - waitForUpdate(() => { - expect(vm.$el.firstChild.getAttribute('draggable')).toBe('true') - vm.foo = null - }).then(() => { - expect(vm.$el.firstChild.getAttribute('draggable')).toBe('false') - vm.foo = '' - }).then(() => { - expect(vm.$el.firstChild.getAttribute('draggable')).toBe('true') - vm.foo = false - }).then(() => { - expect(vm.$el.firstChild.getAttribute('draggable')).toBe('false') - vm.foo = 'false' - }).then(() => { - expect(vm.$el.firstChild.getAttribute('draggable')).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) - }) - - 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() - - expect(vm.$el.textContent).toBe('1') - triggerEvent(vm.$el, 'click') - 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('.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() - 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') - }).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>') - }) - }) -}) diff --git a/test/unit/features/directives/bind.spec.ts b/test/unit/features/directives/bind.spec.ts new file mode 100644 index 00000000000..9adb72ff22a --- /dev/null +++ b/test/unit/features/directives/bind.spec.ts @@ -0,0 +1,631 @@ +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.js b/test/unit/features/directives/class.spec.js deleted file mode 100644 index c1390697ed9..00000000000 --- a/test/unit/features/directives/class.spec.js +++ /dev/null @@ -1,175 +0,0 @@ -import Vue from 'vue' - -function assertClass (assertions, done) { - const vm = new Vue({ - template: '<div class="foo" :class="value"></div>', - data: { value: '' } - }).$mount() - var chain = waitForUpdate() - assertions.forEach(([value, expected], i) => { - chain.then(() => { - if (typeof value === 'function') { - value(vm.value) - } else { - vm.value = value - } - }).then(() => { - expect(vm.$el.className).toBe(expected) - 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) - }) - - // 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/class.spec.ts b/test/unit/features/directives/class.spec.ts new file mode 100644 index 00000000000..f2c00110b6a --- /dev/null +++ b/test/unit/features/directives/class.spec.ts @@ -0,0 +1,237 @@ +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.js b/test/unit/features/directives/cloak.spec.ts similarity index 100% rename from test/unit/features/directives/cloak.spec.js rename to test/unit/features/directives/cloak.spec.ts diff --git a/test/unit/features/directives/for.spec.js b/test/unit/features/directives/for.spec.js deleted file mode 100644 index db92426b2d1..00000000000 --- a/test/unit/features/directives/for.spec.js +++ /dev/null @@ -1,466 +0,0 @@ -import Vue from 'vue' - -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) - }) - - 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 () { - var 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 () { - var 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', () => { - const warn = console.warn - console.warn = jasmine.createSpy() - new Vue({ - template: `<div><test v-for="i in 3"></test></div>`, - components: { - test: { - render () {} - } - } - }).$mount() - expect(console.warn.calls.argsFor(0)[0]).toContain( - `<test v-for="i in 3">: component lists rendered with v-for should have explicit keys` - ) - console.warn = warn - }) - - 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('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) - }) -}) diff --git a/test/unit/features/directives/for.spec.ts b/test/unit/features/directives/for.spec.ts new file mode 100644 index 00000000000..7291bd5d264 --- /dev/null +++ b/test/unit/features/directives/for.spec.ts @@ -0,0 +1,885 @@ +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.js b/test/unit/features/directives/html.spec.js deleted file mode 100644 index c398828ac1d..00000000000 --- a/test/unit/features/directives/html.spec.js +++ /dev/null @@ -1,67 +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><</span>' } - }).$mount() - expect(vm.$el.innerHTML).toBe('<span><</span>') - }) - - it('should work inline', () => { - const vm = new Vue({ - template: `<div v-html="'<span><</span>'"></div>` - }).$mount() - expect(vm.$el.innerHTML).toBe('<span><</span>') - }) - - it('should work inline in DOM', () => { - const el = document.createElement('div') - el.innerHTML = `<div v-html="'<span><</span>'"></div>` - const vm = new Vue({ el }) - expect(vm.$el.children[0].innerHTML).toBe('<span><</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 = 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/html.spec.ts b/test/unit/features/directives/html.spec.ts new file mode 100644 index 00000000000..95b125f1188 --- /dev/null +++ b/test/unit/features/directives/html.spec.ts @@ -0,0 +1,92 @@ +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><</span>' } + }).$mount() + expect(vm.$el.innerHTML).toBe('<span><</span>') + }) + + it('should work inline', () => { + const vm = new Vue({ + template: `<div v-html="'<span><</span>'"></div>` + }).$mount() + expect(vm.$el.innerHTML).toBe('<span><</span>') + }) + + it('should work inline in DOM', () => { + const el = document.createElement('div') + el.innerHTML = `<div v-html="'<span><</span>'"></div>` + const vm = new Vue({ el }) + expect(vm.$el.children[0].innerHTML).toBe('<span><</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.js b/test/unit/features/directives/if.spec.js deleted file mode 100644 index 458f1f92484..00000000000 --- a/test/unit/features/directives/if.spec.js +++ /dev/null @@ -1,276 +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 = jasmine.createSpy() - const destroyed = jasmine.createSpy() - 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.calls.count()).toBe(1) - vm.ok = false - waitForUpdate(() => { - expect(created.calls.count()).toBe(1) - expect(destroyed).not.toHaveBeenCalled() - }).then(done) - }) -}) diff --git a/test/unit/features/directives/if.spec.ts b/test/unit/features/directives/if.spec.ts new file mode 100644 index 00000000000..caaca63f3af --- /dev/null +++ b/test/unit/features/directives/if.spec.ts @@ -0,0 +1,313 @@ +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.js b/test/unit/features/directives/model-checkbox.spec.js deleted file mode 100644 index e102af62609..00000000000 --- a/test/unit/features/directives/model-checkbox.spec.js +++ /dev/null @@ -1,340 +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> - <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() - expect(vm.test.length).toBe(0) - vm.$el.children[1].click() - expect(vm.test).toEqual(['2']) - vm.$el.children[0].click() - expect(vm.test).toEqual(['2', '1']) - vm.test = ['1'] - waitForUpdate(() => { - 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() - expect(vm.test.length).toBe(0) - vm.$el.children[1].click() - expect(vm.test).toEqual(['2']) - vm.$el.children[0].click() - expect(vm.test).toEqual(['2', '1']) - vm.test = ['1'] - waitForUpdate(() => { - 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() - expect(vm.test.length).toBe(0) - vm.$el.children[1].click() - expect(vm.test).toEqual([2]) - vm.$el.children[0].click() - expect(vm.test).toEqual([2, 1]) - vm.test = [1] - waitForUpdate(() => { - 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() - expect(vm.test.length).toBe(0) - vm.$el.children[1].click() - expect(vm.test).toEqual([{ a: 2 }]) - vm.$el.children[0].click() - expect(vm.test).toEqual([{ a: 2 }, { a: 1 }]) - vm.test = [{ a: 1 }] - waitForUpdate(() => { - 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() - expect(vm.test.length).toBe(0) - vm.$el.children[1].click() - expect(vm.test).toEqual([[2]]) - vm.$el.children[0].click() - expect(vm.test).toEqual([[2], { a: 1 }]) - vm.test = [{ a: 1 }] - waitForUpdate(() => { - 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) - }) -}) diff --git a/test/unit/features/directives/model-checkbox.spec.ts b/test/unit/features/directives/model-checkbox.spec.ts new file mode 100644 index 00000000000..212d03990df --- /dev/null +++ b/test/unit/features/directives/model-checkbox.spec.ts @@ -0,0 +1,400 @@ +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.js b/test/unit/features/directives/model-component.spec.js deleted file mode 100644 index 6098e4d6241..00000000000 --- a/test/unit/features/directives/model-component.spec.js +++ /dev/null @@ -1,151 +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 = jasmine.createSpy('update') - 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') - }) -}) diff --git a/test/unit/features/directives/model-component.spec.ts b/test/unit/features/directives/model-component.spec.ts new file mode 100644 index 00000000000..412278bc366 --- /dev/null +++ b/test/unit/features/directives/model-component.spec.ts @@ -0,0 +1,245 @@ +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.js b/test/unit/features/directives/model-dynamic.spec.js deleted file mode 100644 index a5c18fe82b2..00000000000 --- a/test/unit/features/directives/model-dynamic.spec.js +++ /dev/null @@ -1,168 +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) - }) -}) - -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-dynamic.spec.ts b/test/unit/features/directives/model-dynamic.spec.ts new file mode 100644 index 00000000000..b50a519f905 --- /dev/null +++ b/test/unit/features/directives/model-dynamic.spec.ts @@ -0,0 +1,234 @@ +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.js b/test/unit/features/directives/model-file.spec.ts similarity index 100% rename from test/unit/features/directives/model-file.spec.js rename to test/unit/features/directives/model-file.spec.ts diff --git a/test/unit/features/directives/model-parse.spec.js b/test/unit/features/directives/model-parse.spec.ts similarity index 100% rename from test/unit/features/directives/model-parse.spec.js rename to test/unit/features/directives/model-parse.spec.ts diff --git a/test/unit/features/directives/model-radio.spec.js b/test/unit/features/directives/model-radio.spec.js deleted file mode 100644 index 4b3886c2a5d..00000000000 --- a/test/unit/features/directives/model-radio.spec.js +++ /dev/null @@ -1,254 +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 = jasmine.createSpy() - 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) - var 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() - var 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-radio.spec.ts b/test/unit/features/directives/model-radio.spec.ts new file mode 100644 index 00000000000..f36167a7a7f --- /dev/null +++ b/test/unit/features/directives/model-radio.spec.ts @@ -0,0 +1,269 @@ +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.js b/test/unit/features/directives/model-select.spec.js deleted file mode 100644 index c961224972b..00000000000 --- a/test/unit/features/directives/model-select.spec.js +++ /dev/null @@ -1,591 +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 () { - var s = document.createElement('select') - s.setAttribute('multiple', '') - var 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) { - var options = el.options - var 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 = jasmine.createSpy() - 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.calls.count()).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() - var 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() - var 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 = jasmine.createSpy() - 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() - var 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 = jasmine.createSpy() - 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) - var selects = vm.$el.getElementsByTagName('select') - var 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() - var 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 = jasmine.createSpy() - 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) - }) - }) -}) diff --git a/test/unit/features/directives/model-select.spec.ts b/test/unit/features/directives/model-select.spec.ts new file mode 100644 index 00000000000..6b9b682f7c9 --- /dev/null +++ b/test/unit/features/directives/model-select.spec.ts @@ -0,0 +1,623 @@ +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.js b/test/unit/features/directives/model-text.spec.js deleted file mode 100644 index 7e291356c2f..00000000000 --- a/test/unit/features/directives/model-text.spec.js +++ /dev/null @@ -1,374 +0,0 @@ -import Vue from 'vue' -import { isIE9, 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('.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 = jasmine.createSpy() - 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() - var 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) - }) - } - - if (!isAndroid) { - 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 = jasmine.createSpy() - const vm = new Vue({ - data: { - a: 'a' - }, - template: '<input v-model="a" @input="onInput">', - methods: { - onInput (e) { - spy(e.target.value) - } - } - }).$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() - }) - - if (!isAndroid) { - it('does not trigger extra input events with single compositionend', () => { - const spy = jasmine.createSpy() - const vm = new Vue({ - data: { - a: 'a' - }, - template: '<input v-model="a" @input="onInput">', - methods: { - onInput (e) { - spy(e.target.value) - } - } - }).$mount() - expect(spy.calls.count()).toBe(0) - vm.$el.value = 'b' - triggerEvent(vm.$el, 'input') - expect(spy.calls.count()).toBe(1) - triggerEvent(vm.$el, 'compositionend') - expect(spy.calls.count()).toBe(1) - }) - - it('triggers extra input on compositionstart + end', () => { - const spy = jasmine.createSpy() - const vm = new Vue({ - data: { - a: 'a' - }, - template: '<input v-model="a" @input="onInput">', - methods: { - onInput (e) { - spy(e.target.value) - } - } - }).$mount() - expect(spy.calls.count()).toBe(0) - vm.$el.value = 'b' - triggerEvent(vm.$el, 'input') - expect(spy.calls.count()).toBe(1) - triggerEvent(vm.$el, 'compositionstart') - triggerEvent(vm.$el, 'compositionend') - expect(spy.calls.count()).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) - }) - } -}) diff --git a/test/unit/features/directives/model-text.spec.ts b/test/unit/features/directives/model-text.spec.ts new file mode 100644 index 00000000000..467fd3c585c --- /dev/null +++ b/test/unit/features/directives/model-text.spec.ts @@ -0,0 +1,488 @@ +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.js b/test/unit/features/directives/on.spec.js deleted file mode 100644 index a32bcaab2da..00000000000 --- a/test/unit/features/directives/on.spec.js +++ /dev/null @@ -1,855 +0,0 @@ -import Vue from 'vue' -import { supportsPassive } from 'core/util/env' - -describe('Directive v-on', () => { - let vm, spy, el - - beforeEach(() => { - vm = null - spy = jasmine.createSpy() - 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.calls.count()).toBe(1) - - const args = spy.calls.allArgs() - const event = args[0] && args[0][0] || {} - expect(event.type).toBe('click') - }) - - it('should bind event to a 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.calls.count()).toBe(1) - - const args = spy.calls.allArgs() - 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 = jasmine.createSpy() - 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.calls.count()).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 = [] - 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.calls.count()).toBe(1) - triggerEvent(vm.$el, 'click') - expect(spy.calls.count()).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.calls.count()).toBe(1) - triggerEvent(vm.$refs.one, 'click') - expect(spy.calls.count()).toBe(1) - triggerEvent(vm.$refs.two, 'click') - expect(spy.calls.count()).toBe(2) - triggerEvent(vm.$refs.one, 'click') - triggerEvent(vm.$refs.two, 'click') - expect(spy.calls.count()).toBe(2) - }) - - it('should support capture and once', () => { - const callOrder = [] - 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.calls.count()).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 modifers', () => { - 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.calls.count()).toBe(0) - triggerEvent(vm.$refs.ctrl, 'keyup', e => { e.ctrlKey = true }) - expect(spy.calls.count()).toBe(1) - - triggerEvent(vm.$refs.shift, 'keyup') - expect(spy.calls.count()).toBe(1) - triggerEvent(vm.$refs.shift, 'keyup', e => { e.shiftKey = true }) - expect(spy.calls.count()).toBe(2) - - triggerEvent(vm.$refs.alt, 'keyup') - expect(spy.calls.count()).toBe(2) - triggerEvent(vm.$refs.alt, 'keyup', e => { e.altKey = true }) - expect(spy.calls.count()).toBe(3) - - triggerEvent(vm.$refs.meta, 'keyup') - expect(spy.calls.count()).toBe(3) - triggerEvent(vm.$refs.meta, 'keyup', e => { e.metaKey = true }) - expect(spy.calls.count()).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.calls.count()).toBe(1) - - triggerEvent(vm.$refs.ctrl, 'keyup', e => { - e.ctrlKey = true - }) - expect(spy.calls.count()).toBe(1) - - // should not trigger if has other system modifiers - triggerEvent(vm.$refs.ctrl, 'keyup', e => { - e.ctrlKey = true - e.altKey = true - }) - expect(spy.calls.count()).toBe(1) - }) - - it('should support system modifers 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.calls.count()).toBe(0) - - triggerEvent(vm.$refs.ctrl, 'keyup', e => { - e.ctrlKey = true - }) - expect(spy.calls.count()).toBe(1) - - // should not trigger if has other system modifiers - triggerEvent(vm.$refs.ctrl, 'keyup', e => { - e.ctrlKey = true - e.altKey = true - }) - expect(spy.calls.count()).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 = jasmine.createSpy() - const spyMiddle = jasmine.createSpy() - const spyRight = jasmine.createSpy() - - 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 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 build-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 build-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('.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.calls.count()).toBe(1) - vm.$children[0].$emit('custom') - expect(spy.calls.count()).toBe(1) // should not be called again - }) - - it('remove listener', done => { - const spy2 = jasmine.createSpy('remove listener') - 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.calls.count()).toBe(1) - expect(spy2.calls.count()).toBe(0) - vm.ok = false - waitForUpdate(() => { - triggerEvent(vm.$el, 'click') - expect(spy.calls.count()).toBe(1) // should no longer trigger - triggerEvent(vm.$el, 'input') - expect(spy2.calls.count()).toBe(1) - }).then(done) - }) - - it('remove capturing listener', done => { - const spy2 = jasmine.createSpy('remove listener') - 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.calls.count()).toBe(1) - expect(spy2.calls.count()).toBe(0) - vm.ok = false - waitForUpdate(() => { - triggerEvent(vm.$el.firstChild, 'click') - expect(spy.calls.count()).toBe(1) // should no longer trigger - triggerEvent(vm.$el, 'mouseOver') - expect(spy2.calls.count()).toBe(1) - }).then(done) - }) - - it('remove once listener', done => { - const spy2 = jasmine.createSpy('remove listener') - 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.calls.count()).toBe(1) - triggerEvent(vm.$el, 'click') - expect(spy.calls.count()).toBe(1) // should no longer trigger - expect(spy2.calls.count()).toBe(0) - vm.ok = false - waitForUpdate(() => { - triggerEvent(vm.$el, 'click') - expect(spy.calls.count()).toBe(1) // should no longer trigger - triggerEvent(vm.$el, 'input') - expect(spy2.calls.count()).toBe(1) - }).then(done) - }) - - it('remove capturing and once listener', done => { - const spy2 = jasmine.createSpy('remove listener') - 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.calls.count()).toBe(1) - triggerEvent(vm.$el.firstChild, 'click') - expect(spy.calls.count()).toBe(1) // should no longer trigger - expect(spy2.calls.count()).toBe(0) - vm.ok = false - waitForUpdate(() => { - triggerEvent(vm.$el.firstChild, 'click') - expect(spy.calls.count()).toBe(1) // should no longer trigger - triggerEvent(vm.$el, 'mouseOver') - expect(spy2.calls.count()).toBe(1) - }).then(done) - }) - - it('remove listener on child component', done => { - const spy2 = jasmine.createSpy('remove listener') - 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.calls.count()).toBe(1) - expect(spy2.calls.count()).toBe(0) - vm.ok = false - waitForUpdate(() => { - vm.$children[0].$emit('foo') - expect(spy.calls.count()).toBe(1) // should no longer trigger - vm.$children[0].$emit('bar') - expect(spy2.calls.count()).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 = jasmine.createSpy() - const spyRight = jasmine.createSpy() - const spyUp = jasmine.createSpy() - const spyDown = jasmine.createSpy() - 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.calls.count()).toBe(1) - expect(spyRight.calls.count()).toBe(1) - expect(spyUp.calls.count()).toBe(1) - expect(spyDown.calls.count()).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 warn click.right', () => { - new Vue({ - template: `<div @click.right="foo"></div>`, - methods: { foo () {} } - }).$mount() - - expect(`Use "contextmenu" instead`).toHaveBeenWarned() - }) - - it('object syntax (no argument)', () => { - const click = jasmine.createSpy('click') - const mouseup = jasmine.createSpy('mouseup') - vm = new Vue({ - el, - template: `<button v-on="listeners">foo</button>`, - created () { - this.listeners = { - click, - mouseup - } - } - }) - - triggerEvent(vm.$el, 'click') - expect(click.calls.count()).toBe(1) - expect(mouseup.calls.count()).toBe(0) - - triggerEvent(vm.$el, 'mouseup') - expect(click.calls.count()).toBe(1) - expect(mouseup.calls.count()).toBe(1) - }) - - it('object syntax (no argument, mixed with normal listeners)', () => { - const click1 = jasmine.createSpy('click1') - const click2 = jasmine.createSpy('click2') - const mouseup = jasmine.createSpy('mouseup') - 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.calls.count()).toBe(1) - expect(click2.calls.count()).toBe(1) - expect(mouseup.calls.count()).toBe(0) - - triggerEvent(vm.$el, 'mouseup') - expect(click1.calls.count()).toBe(1) - expect(click2.calls.count()).toBe(1) - expect(mouseup.calls.count()).toBe(1) - }) - - it('object syntax (usage in HOC, mixed with native listeners)', () => { - const click = jasmine.createSpy('click') - const mouseup = jasmine.createSpy('mouseup') - const mousedown = jasmine.createSpy('mousedown') - - 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.calls.count()).toBe(1) - expect(mouseup.calls.count()).toBe(0) - expect(mousedown.calls.count()).toBe(0) - - triggerEvent(vm.$el, 'mouseup') - expect(click.calls.count()).toBe(1) - expect(mouseup.calls.count()).toBe(1) - expect(mousedown.calls.count()).toBe(0) - - triggerEvent(vm.$el, 'mousedown') - expect(click.calls.count()).toBe(1) - expect(mouseup.calls.count()).toBe(1) - expect(mousedown.calls.count()).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() - }) -}) diff --git a/test/unit/features/directives/on.spec.ts b/test/unit/features/directives/on.spec.ts new file mode 100644 index 00000000000..e103301471e --- /dev/null +++ b/test/unit/features/directives/on.spec.ts @@ -0,0 +1,1211 @@ +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.js b/test/unit/features/directives/once.spec.js deleted file mode 100644 index eca8be0a3ef..00000000000 --- a/test/unit/features/directives/once.spec.js +++ /dev/null @@ -1,364 +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/once.spec.ts b/test/unit/features/directives/once.spec.ts new file mode 100644 index 00000000000..732a440bf6e --- /dev/null +++ b/test/unit/features/directives/once.spec.ts @@ -0,0 +1,386 @@ +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.js b/test/unit/features/directives/pre.spec.js deleted file mode 100644 index 92d3bc85537..00000000000 --- a/test/unit/features/directives/pre.spec.js +++ /dev/null @@ -1,34 +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></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></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 }}') - }) -}) diff --git a/test/unit/features/directives/pre.spec.ts b/test/unit/features/directives/pre.spec.ts new file mode 100644 index 00000000000..65def9cddb0 --- /dev/null +++ b/test/unit/features/directives/pre.spec.ts @@ -0,0 +1,54 @@ +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.js b/test/unit/features/directives/show.spec.js deleted file mode 100644 index dd509577c27..00000000000 --- a/test/unit/features/directives/show.spec.js +++ /dev/null @@ -1,84 +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/show.spec.ts b/test/unit/features/directives/show.spec.ts new file mode 100644 index 00000000000..86641b4af4f --- /dev/null +++ b/test/unit/features/directives/show.spec.ts @@ -0,0 +1,97 @@ +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.js b/test/unit/features/directives/static-style-parser.spec.js deleted file mode 100644 index 13dd675bda1..00000000000 --- a/test/unit/features/directives/static-style-parser.spec.js +++ /dev/null @@ -1,41 +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/static-style-parser.spec.ts b/test/unit/features/directives/static-style-parser.spec.ts new file mode 100644 index 00000000000..ccf9e4fe9c1 --- /dev/null +++ b/test/unit/features/directives/static-style-parser.spec.ts @@ -0,0 +1,42 @@ +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.js b/test/unit/features/directives/style.spec.js deleted file mode 100644 index 1d8f68b77e0..00000000000 --- a/test/unit/features/directives/style.spec.js +++ /dev/null @@ -1,381 +0,0 @@ -import Vue from 'vue' - -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('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('object with multiple entries', done => { - vm.$el.style.color = 'red' - vm.styles = { - marginLeft: '10px', - marginRight: '15px' - } - waitForUpdate(() => { - expect(vm.$el.style.getPropertyValue('color')).toBe('red') - expect(vm.$el.style.getPropertyValue('margin-left')).toBe('10px') - expect(vm.$el.style.getPropertyValue('margin-right')).toBe('15px') - vm.styles = { - color: 'blue', - padding: null - } - }).then(() => { - expect(vm.$el.style.getPropertyValue('color')).toBe('blue') - expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy() - expect(vm.$el.style.getPropertyValue('margin-left')).toBeFalsy() - expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy() - // handle falsy value - vm.styles = null - }).then(() => { - expect(vm.$el.style.getPropertyValue('color')).toBeFalsy() - expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy() - expect(vm.$el.style.getPropertyValue('margin-left')).toBeFalsy() - expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy() - }).then(done) - }) - - it('array of objects', done => { - vm.$el.style.padding = '10px' - vm.styles = [{ color: 'red' }, { marginRight: '20px' }] - - waitForUpdate(() => { - expect(vm.$el.style.getPropertyValue('color')).toBe('red') - expect(vm.$el.style.getPropertyValue('margin-right')).toBe('20px') - expect(vm.$el.style.getPropertyValue('padding')).toBe('10px') - vm.styles = [{ color: 'blue' }, { padding: null }] - }).then(() => { - expect(vm.$el.style.getPropertyValue('color')).toBe('blue') - expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy() - expect(vm.$el.style.getPropertyValue('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.getPropertyValue('background-image')).toMatch('https://vuejs.org/images/logo.png') - expect(style.getPropertyValue('color')).toBe('red') - expect(style.getPropertyValue('font-size')).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-top: 12px" v-else-if="bar"></section>' + - '<section style="margin-bottom: 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.marginBottom).toBe('24px') - vm.bar = true - }).then(() => { - expect(style.color).toBe('') - expect(style.fontSize).toBe('') - expect(style.marginBottom).toBe('') - expect(style.marginTop).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/style.spec.ts b/test/unit/features/directives/style.spec.ts new file mode 100644 index 00000000000..f1cfb871db7 --- /dev/null +++ b/test/unit/features/directives/style.spec.ts @@ -0,0 +1,414 @@ +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.js b/test/unit/features/directives/text.spec.js deleted file mode 100644 index 477badf9b9b..00000000000 --- a/test/unit/features/directives/text.spec.js +++ /dev/null @@ -1,53 +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('<foo>') - }) - - 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 = 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/text.spec.ts b/test/unit/features/directives/text.spec.ts new file mode 100644 index 00000000000..5affa3c7f43 --- /dev/null +++ b/test/unit/features/directives/text.spec.ts @@ -0,0 +1,78 @@ +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('<foo>') + }) + + 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.js b/test/unit/features/error-handling.spec.js deleted file mode 100644 index 217bb72721a..00000000000 --- a/test/unit/features/error-handling.spec.js +++ /dev/null @@ -1,287 +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) - }) - }) - - // 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) - }) - }) - - ;[ - ['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) - }) - }) - - 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) { - return e.toString() - } - } - const msg = getErrorMsg.call(vm) - expect(msg).toHaveBeenWarned() - }).thenWaitFor(next => { - assertBothInstancesActive(vm).end(next) - }).then(done) - }) - - it('should recover from errors in user watcher callback', done => { - const vm = createTestInstance(components.userWatcherCallback) - vm.n++ - waitForUpdate(() => { - expect(`Error in callback for watcher "n"`).toHaveBeenWarned() - expect(`Error: userWatcherCallback`).toHaveBeenWarned() - }).thenWaitFor(next => { - assertBothInstancesActive(vm).end(next) - }).then(done) - }) - - it('config.errorHandler should capture render errors', done => { - const spy = Vue.config.errorHandler = jasmine.createSpy('errorHandler') - const vm = createTestInstance(components.render) - - const args = spy.calls.argsFor(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 = null - }).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 = jasmine.createSpy('errorHandler') - 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 = null - 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 = null - }) -}) - -function createErrorTestComponents () { - const components = {} - - // 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) - } - - // 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) - } - }) - - // directive hooks errors - ;['bind', 'update', 'unbind'].forEach(hook => { - const key = 'directive ' + hook - const dirComp = 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) - } - } - - // event errors - components.event = { - beforeCreate () { - this.$on('e', () => { throw 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, chain) { - 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/error-handling.spec.ts b/test/unit/features/error-handling.spec.ts new file mode 100644 index 00000000000..b90bb947a96 --- /dev/null +++ b/test/unit/features/error-handling.spec.ts @@ -0,0 +1,487 @@ +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.js b/test/unit/features/filter/filter.spec.js deleted file mode 100644 index 82c57a3d62e..00000000000 --- a/test/unit/features/filter/filter.spec.js +++ /dev/null @@ -1,197 +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`)') - }) -}) diff --git a/test/unit/features/filter/filter.spec.ts b/test/unit/features/filter/filter.spec.ts new file mode 100644 index 00000000000..b7d72c93f00 --- /dev/null +++ b/test/unit/features/filter/filter.spec.ts @@ -0,0 +1,204 @@ +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.js b/test/unit/features/global-api/assets.spec.ts similarity index 100% rename from test/unit/features/global-api/assets.spec.js rename to test/unit/features/global-api/assets.spec.ts diff --git a/test/unit/features/global-api/compile.spec.js b/test/unit/features/global-api/compile.spec.ts similarity index 100% rename from test/unit/features/global-api/compile.spec.js rename to test/unit/features/global-api/compile.spec.ts diff --git a/test/unit/features/global-api/config.spec.js b/test/unit/features/global-api/config.spec.js deleted file mode 100644 index 1edfc3c7829..00000000000 --- a/test/unit/features/global-api/config.spec.js +++ /dev/null @@ -1,58 +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 = jasmine.createSpy('option merging') - Vue.config.optionMergeStrategies.__test__ = (parent, child, vm) => { - spy(parent, child, vm) - return child + 1 - } - const Test = Vue.extend({ - __test__: 1 - }) - expect(spy.calls.count()).toBe(1) - expect(spy).toHaveBeenCalledWith(undefined, 1, undefined) - expect(Test.options.__test__).toBe(2) - const test = new Test({ - __test__: 2 - }) - expect(spy.calls.count()).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 = [] - }) - }) -}) diff --git a/test/unit/features/global-api/config.spec.ts b/test/unit/features/global-api/config.spec.ts new file mode 100644 index 00000000000..c9cb6968deb --- /dev/null +++ b/test/unit/features/global-api/config.spec.ts @@ -0,0 +1,125 @@ +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.js b/test/unit/features/global-api/extend.spec.js deleted file mode 100644 index 4a4a73505b9..00000000000 --- a/test/unit/features/global-api/extend.spec.js +++ /dev/null @@ -1,134 +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 = [] - 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 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/extend.spec.ts b/test/unit/features/global-api/extend.spec.ts new file mode 100644 index 00000000000..b2aca199de4 --- /dev/null +++ b/test/unit/features/global-api/extend.spec.ts @@ -0,0 +1,159 @@ +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.js b/test/unit/features/global-api/mixin.spec.js deleted file mode 100644 index bcf962402c2..00000000000 --- a/test/unit/features/global-api/mixin.spec.js +++ /dev/null @@ -1,165 +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 = jasmine.createSpy('global mixin') - 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 = [] - const Test = 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 = jasmine.createSpy('base') - 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 = jasmine.createSpy('late attached') - Test.options.beforeCreate = Test.options.beforeCreate.concat(spy) - - // Update super constructor's options - const mixinSpy = jasmine.createSpy('mixin') - Vue.mixin({ - beforeCreate: mixinSpy - }) - - // mount the component - const vm = new Test({ - template: '<div>{{ $style }}</div>' - }).$mount() - - expect(spy.calls.count()).toBe(1) - expect(baseSpy.calls.count()).toBe(1) - expect(mixinSpy.calls.count()).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 = jasmine.createSpy('global mixin') - 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 = jasmine.createSpy('base') - - const Base = Vue.extend({ - beforeCreate: base - }) - - const injected = jasmine.createSpy('injected') - - // inject a function - Base.options.beforeCreate = Base.options.beforeCreate.concat(injected) - - Vue.mixin({}) - - new Base({}) - - expect(base).toHaveBeenCalled() - expect(injected).toHaveBeenCalled() - }) -}) diff --git a/test/unit/features/global-api/mixin.spec.ts b/test/unit/features/global-api/mixin.spec.ts new file mode 100644 index 00000000000..622f8bd806a --- /dev/null +++ b/test/unit/features/global-api/mixin.spec.ts @@ -0,0 +1,201 @@ +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 new file mode 100644 index 00000000000..1c0388b889a --- /dev/null +++ b/test/unit/features/global-api/observable.spec.ts @@ -0,0 +1,34 @@ +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.js b/test/unit/features/global-api/set-delete.spec.js deleted file mode 100644 index d1c7f3d8709..00000000000 --- a/test/unit/features/global-api/set-delete.spec.js +++ /dev/null @@ -1,170 +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 a 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 a 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 { - 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)) - }).then(() => { - expect(vm.$el.innerHTML).toBe('') - }).then(done) - }) - }) -}) diff --git a/test/unit/features/global-api/set-delete.spec.ts b/test/unit/features/global-api/set-delete.spec.ts new file mode 100644 index 00000000000..1303119498e --- /dev/null +++ b/test/unit/features/global-api/set-delete.spec.ts @@ -0,0 +1,190 @@ +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.js b/test/unit/features/global-api/use.spec.js deleted file mode 100644 index 00053f808a7..00000000000 --- a/test/unit/features/global-api/use.spec.js +++ /dev/null @@ -1,52 +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) - }) -}) diff --git a/test/unit/features/global-api/use.spec.ts b/test/unit/features/global-api/use.spec.ts new file mode 100644 index 00000000000..d54ff0750f3 --- /dev/null +++ b/test/unit/features/global-api/use.spec.ts @@ -0,0 +1,57 @@ +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.js b/test/unit/features/instance/init.spec.js deleted file mode 100644 index 62cc06dc22d..00000000000 --- a/test/unit/features/instance/init.spec.js +++ /dev/null @@ -1,12 +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/init.spec.ts b/test/unit/features/instance/init.spec.ts new file mode 100644 index 00000000000..2619a785a6d --- /dev/null +++ b/test/unit/features/instance/init.spec.ts @@ -0,0 +1,16 @@ +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.js b/test/unit/features/instance/methods-data.spec.js deleted file mode 100644 index 8684c69eb3a..00000000000 --- a/test/unit/features/instance/methods-data.spec.js +++ /dev/null @@ -1,116 +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 = jasmine.createSpy('watch') - vm = new Vue({ - data: { - a: { - b: 1 - } - }, - methods: { - foo: spy - } - }) - }) - - it('basic usage', done => { - vm.$watch('a.b', spy) - vm.a.b = 2 - waitForUpdate(() => { - expect(spy.calls.count()).toBe(1) - expect(spy).toHaveBeenCalledWith(2, 1) - vm.a = { b: 3 } - }).then(() => { - expect(spy.calls.count()).toBe(2) - expect(spy).toHaveBeenCalledWith(3, 2) - }).then(done) - }) - - it('immediate', () => { - vm.$watch('a.b', spy, { immediate: true }) - expect(spy.calls.count()).toBe(1) - expect(spy).toHaveBeenCalledWith(1) - }) - - it('unwatch', done => { - const unwatch = vm.$watch('a.b', spy) - unwatch() - vm.a.b = 2 - waitForUpdate(() => { - expect(spy.calls.count()).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 => { - var 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 => { - var 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.calls.count()).toBe(1) - expect(spy).toHaveBeenCalledWith(1) - }) - - 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-data.spec.ts b/test/unit/features/instance/methods-data.spec.ts new file mode 100644 index 00000000000..0cdc4789222 --- /dev/null +++ b/test/unit/features/instance/methods-data.spec.ts @@ -0,0 +1,138 @@ +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.js b/test/unit/features/instance/methods-events.spec.js deleted file mode 100644 index 88834577f26..00000000000 --- a/test/unit/features/instance/methods-events.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -import Vue from 'vue' - -describe('Instance methods events', () => { - let vm, spy - beforeEach(() => { - vm = new Vue() - spy = jasmine.createSpy('emitter') - }) - - 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.calls.count()).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.calls.count()).toBe(1) - expect(spy).toHaveBeenCalledWith(1, 2, 3, 4) - vm.$emit('test2', 5, 6, 7, 8) - expect(spy.calls.count()).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.calls.count()).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.calls.count()).toBe(1) - expect(spy).toHaveBeenCalledWith(1, 2, 3) - }) - - 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.calls.count()).toBe(1) - expect(spy).toHaveBeenCalledWith(2) - }) - - it('$off event + fn', () => { - 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) - }) -}) diff --git a/test/unit/features/instance/methods-events.spec.ts b/test/unit/features/instance/methods-events.spec.ts new file mode 100644 index 00000000000..a9dd77b219d --- /dev/null +++ b/test/unit/features/instance/methods-events.spec.ts @@ -0,0 +1,96 @@ +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.js b/test/unit/features/instance/methods-lifecycle.spec.js deleted file mode 100644 index 71525be2f0b..00000000000 --- a/test/unit/features/instance/methods-lifecycle.spec.js +++ /dev/null @@ -1,130 +0,0 @@ -import Vue from 'vue' - -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') - }) - }) - - 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._watchers.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 = jasmine.createSpy('destroy') - const vm = new Vue({ - beforeDestroy: spy - }) - vm.$destroy() - vm.$destroy() - expect(spy.calls.count()).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/methods-lifecycle.spec.ts b/test/unit/features/instance/methods-lifecycle.spec.ts new file mode 100644 index 00000000000..826f1b7a368 --- /dev/null +++ b/test/unit/features/instance/methods-lifecycle.spec.ts @@ -0,0 +1,185 @@ +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.js b/test/unit/features/instance/properties.spec.js deleted file mode 100644 index 02aca12fcfa..00000000000 --- a/test/unit/features/instance/properties.spec.js +++ /dev/null @@ -1,203 +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 = [] - 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/>`, - data: { foo: 'foo' }, - components: { - foo: { - template: `<div>{{ this.foo }}</div>` - } - } - }).$mount() - expect(vm.$attrs).toBeDefined() - }) - - it('warn mutating $attrs', () => { - const vm = new Vue() - vm.$attrs = {} - expect(`$attrs is readonly`).toHaveBeenWarned() - }) - - it('$listeners', done => { - const spyA = jasmine.createSpy('A') - const spyB = jasmine.createSpy('B') - 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.calls.count()).toBe(1) - expect(spyB.calls.count()).toBe(0) - - vm.foo = spyB - waitForUpdate(() => { - triggerEvent(vm.$el, 'click') - expect(spyA.calls.count()).toBe(1) - expect(spyB.calls.count()).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/properties.spec.ts b/test/unit/features/instance/properties.spec.ts new file mode 100644 index 00000000000..366997ee22b --- /dev/null +++ b/test/unit/features/instance/properties.spec.ts @@ -0,0 +1,213 @@ +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.js b/test/unit/features/instance/render-proxy.spec.js deleted file mode 100644 index d4d01ebcf2a..00000000000 --- a/test/unit/features/instance/render-proxy.spec.js +++ /dev/null @@ -1,32 +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() - }) - }) -} diff --git a/test/unit/features/instance/render-proxy.spec.ts b/test/unit/features/instance/render-proxy.spec.ts new file mode 100644 index 00000000000..62b347a1292 --- /dev/null +++ b/test/unit/features/instance/render-proxy.spec.ts @@ -0,0 +1,101 @@ +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.js b/test/unit/features/options/_scopeId.spec.js deleted file mode 100644 index ec45f902c32..00000000000 --- a/test/unit/features/options/_scopeId.spec.js +++ /dev/null @@ -1,96 +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/_scopeId.spec.ts b/test/unit/features/options/_scopeId.spec.ts new file mode 100644 index 00000000000..a800f80f806 --- /dev/null +++ b/test/unit/features/options/_scopeId.spec.ts @@ -0,0 +1,98 @@ +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.js b/test/unit/features/options/comments.spec.js deleted file mode 100644 index b13b63aaabc..00000000000 --- a/test/unit/features/options/comments.spec.js +++ /dev/null @@ -1,16 +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/comments.spec.ts b/test/unit/features/options/comments.spec.ts new file mode 100644 index 00000000000..d1c5fb3de5a --- /dev/null +++ b/test/unit/features/options/comments.spec.ts @@ -0,0 +1,19 @@ +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.js b/test/unit/features/options/components.spec.js deleted file mode 100644 index 1a94380c322..00000000000 --- a/test/unit/features/options/components.spec.js +++ /dev/null @@ -1,92 +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/components.spec.ts b/test/unit/features/options/components.spec.ts new file mode 100644 index 00000000000..bff289536a5 --- /dev/null +++ b/test/unit/features/options/components.spec.ts @@ -0,0 +1,96 @@ +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.js b/test/unit/features/options/computed.spec.js deleted file mode 100644 index edc20bad3bf..00000000000 --- a/test/unit/features/options/computed.spec.js +++ /dev/null @@ -1,219 +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 = jasmine.createSpy('watch computed') - 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 = jasmine.createSpy('cached computed') - const vm = new Vue({ - data: { - a: 1 - }, - computed: { - b () { - spy() - return this.a + 1 - } - } - }) - expect(spy.calls.count()).toBe(0) - vm.b - expect(spy.calls.count()).toBe(1) - vm.b - expect(spy.calls.count()).toBe(1) - }) - - it('cache: false', () => { - const spy = jasmine.createSpy('cached computed') - const vm = new Vue({ - data: { - a: 1 - }, - computed: { - b: { - cache: false, - get () { - spy() - return this.a + 1 - } - } - } - }) - expect(spy.calls.count()).toBe(0) - vm.b - expect(spy.calls.count()).toBe(1) - vm.b - expect(spy.calls.count()).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('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/computed.spec.ts b/test/unit/features/options/computed.spec.ts new file mode 100644 index 00000000000..bcebaea4adf --- /dev/null +++ b/test/unit/features/options/computed.spec.ts @@ -0,0 +1,249 @@ +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.js b/test/unit/features/options/data.spec.js deleted file mode 100644 index 29a48c38b44..00000000000 --- a/test/unit/features/options/data.spec.js +++ /dev/null @@ -1,126 +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('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 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>') - }) -}) diff --git a/test/unit/features/options/data.spec.ts b/test/unit/features/options/data.spec.ts new file mode 100644 index 00000000000..398ea70fd23 --- /dev/null +++ b/test/unit/features/options/data.spec.ts @@ -0,0 +1,188 @@ +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.js b/test/unit/features/options/delimiters.spec.js deleted file mode 100644 index cebcc2b34a0..00000000000 --- a/test/unit/features/options/delimiters.spec.js +++ /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/delimiters.spec.ts b/test/unit/features/options/delimiters.spec.ts new file mode 100644 index 00000000000..d477cab0a37 --- /dev/null +++ b/test/unit/features/options/delimiters.spec.ts @@ -0,0 +1,116 @@ +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.js b/test/unit/features/options/directives.spec.js deleted file mode 100644 index f15dd77f8e6..00000000000 --- a/test/unit/features/options/directives.spec.js +++ /dev/null @@ -1,268 +0,0 @@ -import Vue from 'vue' - -describe('Options directives', () => { - it('basic usage', done => { - const bindSpy = jasmine.createSpy('bind') - const insertedSpy = jasmine.createSpy('inserted') - const updateSpy = jasmine.createSpy('update') - const componentUpdatedSpy = jasmine.createSpy('componentUpdated') - const unbindSpy = jasmine.createSpy('unbind') - - 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.calls.count()).toBe(2) - vm.ok = false - }).then(() => { - expect(unbindSpy).toHaveBeenCalled() - }).then(done) - }) - - it('function shorthand', done => { - const spy = jasmine.createSpy('directive') - 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 = jasmine.createSpy('directive') - 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 = {} - const createSpy = name => (spies[name] = jasmine.createSpy(name)) - 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.calls.count()).toBe(2) - expect(spies.inserted1.calls.count()).toBe(2) - expect(spies.bind2.calls.count()).toBe(0) - expect(spies.inserted2.calls.count()).toBe(0) - - vm.ok = false - waitForUpdate(() => { - // v-test with modifier should be updated - expect(spies.update1.calls.count()).toBe(1) - expect(spies.componentUpdated1.calls.count()).toBe(1) - - // v-test without modifier should be unbound - expect(spies.unbind1.calls.count()).toBe(1) - - // v-test2 should be bound - expect(spies.bind2.calls.count()).toBe(1) - expect(spies.inserted2.calls.count()).toBe(1) - - vm.ok = true - }).then(() => { - // v-test without modifier should be bound again - expect(spies.bind1.calls.count()).toBe(3) - expect(spies.inserted1.calls.count()).toBe(3) - - // v-test2 should be unbound - expect(spies.unbind2.calls.count()).toBe(1) - - // v-test with modifier should be updated again - expect(spies.update1.calls.count()).toBe(2) - expect(spies.componentUpdated1.calls.count()).toBe(2) - - vm.val = 234 - }).then(() => { - expect(spies.update1.calls.count()).toBe(4) - expect(spies.componentUpdated1.calls.count()).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: jasmine.createSpy('bind'), - inserted: jasmine.createSpy('inserted'), - unbind: jasmine.createSpy('unbind') - } - - 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.calls.count()).toBe(1) - expect(dir.bind.calls.argsFor(0)[0]).toBe(oldEl) - expect(dir.inserted.calls.count()).toBe(1) - expect(dir.inserted.calls.argsFor(0)[0]).toBe(oldEl) - expect(dir.unbind).not.toHaveBeenCalled() - - vm.$refs.child.ok = false - waitForUpdate(() => { - expect(vm.$el.tagName).toBe('SPAN') - expect(dir.bind.calls.count()).toBe(2) - expect(dir.bind.calls.argsFor(1)[0]).toBe(vm.$el) - expect(dir.inserted.calls.count()).toBe(2) - expect(dir.inserted.calls.argsFor(1)[0]).toBe(vm.$el) - expect(dir.unbind.calls.count()).toBe(1) - expect(dir.unbind.calls.argsFor(0)[0]).toBe(oldEl) - }).then(done) - }) -}) diff --git a/test/unit/features/options/directives.spec.ts b/test/unit/features/options/directives.spec.ts new file mode 100644 index 00000000000..2d999c8b3ae --- /dev/null +++ b/test/unit/features/options/directives.spec.ts @@ -0,0 +1,380 @@ +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.js b/test/unit/features/options/el.spec.js deleted file mode 100644 index 75beda1c2a1..00000000000 --- a/test/unit/features/options/el.spec.js +++ /dev/null @@ -1,90 +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/el.spec.ts b/test/unit/features/options/el.spec.ts new file mode 100644 index 00000000000..e30382ff02c --- /dev/null +++ b/test/unit/features/options/el.spec.ts @@ -0,0 +1,93 @@ +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.js b/test/unit/features/options/errorCaptured.spec.js deleted file mode 100644 index d16c057da6f..00000000000 --- a/test/unit/features/options/errorCaptured.spec.js +++ /dev/null @@ -1,212 +0,0 @@ -import Vue from 'vue' - -describe('Options errorCaptured', () => { - let globalSpy - - beforeEach(() => { - globalSpy = Vue.config.errorHandler = jasmine.createSpy() - }) - - afterEach(() => { - Vue.config.errorHandler = null - }) - - it('should capture error from child component', () => { - const spy = jasmine.createSpy() - - 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 = jasmine.createSpy() - - 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 = [] - - 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 = [] - - 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]) - }) -}) diff --git a/test/unit/features/options/errorCaptured.spec.ts b/test/unit/features/options/errorCaptured.spec.ts new file mode 100644 index 00000000000..65991544e66 --- /dev/null +++ b/test/unit/features/options/errorCaptured.spec.ts @@ -0,0 +1,426 @@ +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.js b/test/unit/features/options/extends.spec.js deleted file mode 100644 index 1734640202e..00000000000 --- a/test/unit/features/options/extends.spec.js +++ /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 = jasmine.createSpy('watch') - 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/extends.spec.ts b/test/unit/features/options/extends.spec.ts new file mode 100644 index 00000000000..d94247dd540 --- /dev/null +++ b/test/unit/features/options/extends.spec.ts @@ -0,0 +1,75 @@ +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.js b/test/unit/features/options/functional.spec.js deleted file mode 100644 index ecbd0be3c55..00000000000 --- a/test/unit/features/options/functional.spec.js +++ /dev/null @@ -1,263 +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 = jasmine.createSpy('foo') - const bar = jasmine.createSpy('bar') - 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() - - triggerEvent(vm.$el.children[0], 'click') - expect(foo).toHaveBeenCalled() - expect(foo.calls.argsFor(0)[0].type).toBe('click') // should have click event - triggerEvent(vm.$el.children[0], 'mousedown') - expect(bar).toHaveBeenCalledWith('bar') - }) - - 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 = jasmine.createSpy('valid') - 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()) - }) - - it('should work with render fns compiled from template', done => { - // code generated via vue-template-es2015-compiler - var render = function (_h, _vm) { - var _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 - ) - } - var staticRenderFns = [ - function (_h, _vm) { - var _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) - }) -}) diff --git a/test/unit/features/options/functional.spec.ts b/test/unit/features/options/functional.spec.ts new file mode 100644 index 00000000000..c26930b203c --- /dev/null +++ b/test/unit/features/options/functional.spec.ts @@ -0,0 +1,369 @@ +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.js b/test/unit/features/options/inheritAttrs.spec.ts similarity index 100% rename from test/unit/features/options/inheritAttrs.spec.js rename to test/unit/features/options/inheritAttrs.spec.ts diff --git a/test/unit/features/options/inject.spec.js b/test/unit/features/options/inject.spec.js deleted file mode 100644 index cd5c7097f31..00000000000 --- a/test/unit/features/options/inject.spec.js +++ /dev/null @@ -1,638 +0,0 @@ -import Vue from 'vue' -import { Observer } from 'core/observer/index' -import { isNative, isObject, hasOwn } 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') - }) - } - - // 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') - }) -}) diff --git a/test/unit/features/options/inject.spec.ts b/test/unit/features/options/inject.spec.ts new file mode 100644 index 00000000000..b4c295e3f51 --- /dev/null +++ b/test/unit/features/options/inject.spec.ts @@ -0,0 +1,723 @@ +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.js b/test/unit/features/options/lifecycle.spec.js deleted file mode 100644 index 2b649aa98e4..00000000000 --- a/test/unit/features/options/lifecycle.spec.js +++ /dev/null @@ -1,249 +0,0 @@ -import Vue from 'vue' - -describe('Options lifecycle hooks', () => { - let spy - beforeEach(() => { - spy = jasmine.createSpy('hook') - }) - - 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 = [] - 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) - }) - }) - - 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 = [] - 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) - }) - }) - - 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.calls.count()).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.calls.count()).toBe(1) - }) - }) - - it('should emit hook events', () => { - const created = jasmine.createSpy() - const mounted = jasmine.createSpy() - const destroyed = jasmine.createSpy() - 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/lifecycle.spec.ts b/test/unit/features/options/lifecycle.spec.ts new file mode 100644 index 00000000000..39e43881250 --- /dev/null +++ b/test/unit/features/options/lifecycle.spec.ts @@ -0,0 +1,338 @@ +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.js b/test/unit/features/options/methods.spec.js deleted file mode 100644 index 34f9ee88fc6..00000000000 --- a/test/unit/features/options/methods.spec.js +++ /dev/null @@ -1,51 +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 undefined methods', () => { - new Vue({ - methods: { - hello: undefined - } - }) - expect(`Method "hello" has an undefined value 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/methods.spec.ts b/test/unit/features/options/methods.spec.ts new file mode 100644 index 00000000000..93f31f8ef4e --- /dev/null +++ b/test/unit/features/options/methods.spec.ts @@ -0,0 +1,57 @@ +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.js b/test/unit/features/options/mixins.spec.js deleted file mode 100644 index 8385edfe03c..00000000000 --- a/test/unit/features/options/mixins.spec.js +++ /dev/null @@ -1,112 +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() - }) -}) diff --git a/test/unit/features/options/mixins.spec.ts b/test/unit/features/options/mixins.spec.ts new file mode 100644 index 00000000000..760e07429b2 --- /dev/null +++ b/test/unit/features/options/mixins.spec.ts @@ -0,0 +1,150 @@ +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.js b/test/unit/features/options/name.spec.js deleted file mode 100644 index 2f586f85893..00000000000 --- a/test/unit/features/options/name.spec.js +++ /dev/null @@ -1,40 +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". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`) - .toHaveBeenWarned() - /* eslint-enable */ - - Vue.extend({ - name: '2Cool2BValid' - }) - - /* eslint-disable */ - expect(`Invalid component name: "2Cool2BValid". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`) - .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) - }) -}) diff --git a/test/unit/features/options/name.spec.ts b/test/unit/features/options/name.spec.ts new file mode 100644 index 00000000000..753acf26a47 --- /dev/null +++ b/test/unit/features/options/name.spec.ts @@ -0,0 +1,50 @@ +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.js b/test/unit/features/options/parent.spec.js deleted file mode 100644 index ac844713c2f..00000000000 --- a/test/unit/features/options/parent.spec.js +++ /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/parent.spec.ts b/test/unit/features/options/parent.spec.ts new file mode 100644 index 00000000000..6defc783eac --- /dev/null +++ b/test/unit/features/options/parent.spec.ts @@ -0,0 +1,28 @@ +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.js b/test/unit/features/options/props.spec.js deleted file mode 100644 index ae2baefff99..00000000000 --- a/test/unit/features/options/props.spec.js +++ /dev/null @@ -1,515 +0,0 @@ -import Vue from 'vue' -import { hasSymbol } from 'core/util/env' -import testObjectOption from '../../../helpers/test-object-option' - -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.calls.count()).toBe(0) - makeInstance(123, String) - expect('Expected String').toHaveBeenWarned() - }) - - it('number', () => { - makeInstance(123, Number) - expect(console.error.calls.count()).toBe(0) - makeInstance('123', Number) - expect('Expected Number').toHaveBeenWarned() - }) - - it('boolean', () => { - makeInstance(true, Boolean) - expect(console.error.calls.count()).toBe(0) - makeInstance('123', Boolean) - expect('Expected Boolean').toHaveBeenWarned() - }) - - it('function', () => { - makeInstance(() => {}, Function) - expect(console.error.calls.count()).toBe(0) - makeInstance(123, Function) - expect('Expected Function').toHaveBeenWarned() - }) - - it('object', () => { - makeInstance({}, Object) - expect(console.error.calls.count()).toBe(0) - makeInstance([], Object) - expect('Expected Object').toHaveBeenWarned() - }) - - it('array', () => { - makeInstance([], Array) - expect(console.error.calls.count()).toBe(0) - makeInstance({}, Array) - expect('Expected Array').toHaveBeenWarned() - }) - - it('primitive wrapper objects', () => { - /* eslint-disable no-new-wrappers */ - makeInstance(new String('s'), String) - expect(console.error.calls.count()).toBe(0) - makeInstance(new Number(1), Number) - expect(console.error.calls.count()).toBe(0) - makeInstance(new Boolean(true), Boolean) - expect(console.error.calls.count()).toBe(0) - /* eslint-enable no-new-wrappers */ - }) - - if (hasSymbol) { - it('symbol', () => { - makeInstance(Symbol('foo'), Symbol) - expect(console.error.calls.count()).toBe(0) - makeInstance({}, Symbol) - expect('Expected Symbol').toHaveBeenWarned() - }) - } - - it('custom constructor', () => { - function Class () {} - makeInstance(new Class(), Class) - expect(console.error.calls.count()).toBe(0) - makeInstance({}, Class) - expect('type check failed').toHaveBeenWarned() - }) - - it('multiple types', () => { - makeInstance([], [Array, Number, Boolean]) - expect(console.error.calls.count()).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.calls.count()).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.calls.count()).toBe(0) - makeInstance(123, Number, v => v === 234) - expect('custom validator check failed').toHaveBeenWarned() - makeInstance(123, String, v => v === 123) - expect('Expected String').toHaveBeenWarned() - }) - - it('multiple types + custom validator', () => { - makeInstance(123, [Number, String, Boolean], v => v === 123) - expect(console.error.calls.count()).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.calls.count()).toBe(0) - makeInstance(null, String) - expect(console.error.calls.count()).toBe(0) - }) - - it('required with type + null/undefined', () => { - makeInstance(undefined, String, null, true) - expect(console.error.calls.count()).toBe(1) - expect('Expected String').toHaveBeenWarned() - makeInstance(null, Boolean, null, true) - expect(console.error.calls.count()).toBe(2) - expect('Expected Boolean').toHaveBeenWarned() - }) - - it('optional prop of any type (type: true or prop: true)', () => { - makeInstance(1, true) - expect(console.error.calls.count()).toBe(0) - makeInstance('any', true) - expect(console.error.calls.count()).toBe(0) - makeInstance({}, true) - expect(console.error.calls.count()).toBe(0) - makeInstance(undefined, true) - expect(console.error.calls.count()).toBe(0) - makeInstance(null, true) - expect(console.error.calls.count()).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.calls.count()).toBe(0) - }) - - // #3453 - it('should not fire watcher on object/array props when parent re-renders', done => { - const spy = jasmine.createSpy() - 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 = jasmine.createSpy() - 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.calls.count()).toBe(1) - }).then(() => { - vm.b = undefined - }).then(() => { - expect(spy.calls.count()).toBe(2) - vm.a++ - }).then(() => { - expect(spy.calls.count()).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() - }) - }) -}) diff --git a/test/unit/features/options/props.spec.ts b/test/unit/features/options/props.spec.ts new file mode 100644 index 00000000000..23f30df429b --- /dev/null +++ b/test/unit/features/options/props.spec.ts @@ -0,0 +1,614 @@ +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.js b/test/unit/features/options/propsData.spec.js deleted file mode 100644 index 9fa5557ef07..00000000000 --- a/test/unit/features/options/propsData.spec.js +++ /dev/null @@ -1,30 +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/propsData.spec.ts b/test/unit/features/options/propsData.spec.ts new file mode 100644 index 00000000000..bc364581a28 --- /dev/null +++ b/test/unit/features/options/propsData.spec.ts @@ -0,0 +1,32 @@ +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.js b/test/unit/features/options/render.spec.js deleted file mode 100644 index 302e2ee0098..00000000000 --- a/test/unit/features/options/render.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import Vue from 'vue' - -describe('Options render', () => { - it('basic usage', () => { - const vm = new Vue({ - render (h) { - const children = [] - 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/render.spec.ts b/test/unit/features/options/render.spec.ts new file mode 100644 index 00000000000..7bccefdc1f1 --- /dev/null +++ b/test/unit/features/options/render.spec.ts @@ -0,0 +1,44 @@ +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.js b/test/unit/features/options/renderError.spec.js deleted file mode 100644 index 20a0aa749a2..00000000000 --- a/test/unit/features/options/renderError.spec.js +++ /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 = null - }).then(done) - }) - - it('should pass on errors in renderError to global handler', () => { - const spy = Vue.config.errorHandler = jasmine.createSpy() - 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/renderError.spec.ts b/test/unit/features/options/renderError.spec.ts new file mode 100644 index 00000000000..6ca0a590f58 --- /dev/null +++ b/test/unit/features/options/renderError.spec.ts @@ -0,0 +1,42 @@ +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.js b/test/unit/features/options/template.spec.js deleted file mode 100644 index 09aafd3efaa..00000000000 --- a/test/unit/features/options/template.spec.js +++ /dev/null @@ -1,91 +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/template.spec.ts b/test/unit/features/options/template.spec.ts new file mode 100644 index 00000000000..e4ed15a38c1 --- /dev/null +++ b/test/unit/features/options/template.spec.ts @@ -0,0 +1,96 @@ +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.js b/test/unit/features/options/watch.spec.js deleted file mode 100644 index e16b2f8312d..00000000000 --- a/test/unit/features/options/watch.spec.js +++ /dev/null @@ -1,146 +0,0 @@ -import Vue from 'vue' -import testObjectOption from '../../../helpers/test-object-option' - -describe('Options watch', () => { - let spy - beforeEach(() => { - spy = jasmine.createSpy('watch') - }) - - 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 = jasmine.createSpy('watch') - 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 => { - var spy2 = jasmine.createSpy('A') - var spy3 = jasmine.createSpy('B') - var A = Vue.extend({ - data: function () { - return { - a: 0, - b: 0 - } - }, - watch: { - b: spy - } - }) - - var B = Vue.extend({ - extends: A, - watch: { - a: spy2 - } - }) - - var C = Vue.extend({ - extends: B, - watch: { - a: spy3 - } - }) - - var vm = new C() - vm.a = 1 - - waitForUpdate(() => { - expect(spy).not.toHaveBeenCalled() - expect(spy2).toHaveBeenCalledWith(1, 0) - expect(spy3).toHaveBeenCalledWith(1, 0) - }).then(done) - }) -}) diff --git a/test/unit/features/options/watch.spec.ts b/test/unit/features/options/watch.spec.ts new file mode 100644 index 00000000000..d092e21224b --- /dev/null +++ b/test/unit/features/options/watch.spec.ts @@ -0,0 +1,179 @@ +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/ref.spec.js b/test/unit/features/ref.spec.js deleted file mode 100644 index 05efc4d5244..00000000000 --- a/test/unit/features/ref.spec.js +++ /dev/null @@ -1,220 +0,0 @@ -import Vue from 'vue' - -describe('ref', () => { - const components = { - test: { - id: 'test', - template: '<div>test</div>' - }, - test2: { - id: 'test2', - template: '<div>test2</div>' - } - } - - it('should work', () => { - const vm = new Vue({ - data: { - value: 'bar' - }, - template: `<div> - <test ref="foo"></test> - <test2 :ref="value"></test2> - </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') - }) - - 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).toBeUndefined() - 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).toBeUndefined() - }).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/template-ref.spec.ts b/test/unit/features/template-ref.spec.ts new file mode 100644 index 00000000000..ce3ee2b2034 --- /dev/null +++ b/test/unit/features/template-ref.spec.ts @@ -0,0 +1,252 @@ +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/transition/inject-styles.js b/test/unit/features/transition/inject-styles.js deleted file mode 100644 index 2304e3d5924..00000000000 --- a/test/unit/features/transition/inject-styles.js +++ /dev/null @@ -1,64 +0,0 @@ -function insertCSS (text) { - var cssEl = document.createElement('style') - cssEl.textContent = text.trim() - document.head.appendChild(cssEl) -} - -const duration = process.env.TRANSITION_DURATION || 50 -const buffer = process.env.TRANSITION_BUFFER || 10 -let injected = false - -export default 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/unit/features/transition/transition-group.spec.js b/test/unit/features/transition/transition-group.spec.js deleted file mode 100644 index 2988d5bff4a..00000000000 --- a/test/unit/features/transition/transition-group.spec.js +++ /dev/null @@ -1,344 +0,0 @@ -import Vue from 'vue' -import injectStyles from './inject-styles' -import { isIE9 } from 'core/util/env' -import { nextFrame } from 'web/runtime/transition-util' - -if (!isIE9) { - describe('Transition group', () => { - const { duration, buffer } = injectStyles() - - let el - beforeEach(() => { - el = document.createElement('div') - document.body.appendChild(el) - }) - - function createBasicVM (useIs, appear) { - 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/unit/features/transition/transition-mode.spec.js b/test/unit/features/transition/transition-mode.spec.js deleted file mode 100644 index 6e4f5be06ca..00000000000 --- a/test/unit/features/transition/transition-mode.spec.js +++ /dev/null @@ -1,569 +0,0 @@ -import Vue from 'vue' -import injectStyles from './inject-styles' -import { isIE9 } from 'core/util/env' -import { nextFrame } from 'web/runtime/transition-util' - -if (!isIE9) { - 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).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.5) - } - }, - 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).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/unit/features/transition/transition.spec.js b/test/unit/features/transition/transition.spec.js deleted file mode 100644 index 1f2f2140422..00000000000 --- a/test/unit/features/transition/transition.spec.js +++ /dev/null @@ -1,1085 +0,0 @@ -import Vue from 'vue' -import injectStyles from './inject-styles' -import { isIE9 } from 'core/util/env' -import { nextFrame } from 'web/runtime/transition-util' - -if (!isIE9) { - describe('Transition basic', () => { - const { duration, buffer } = injectStyles() - 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('enter') - const leave = jasmine.createSpy('leave') - 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('leave') - const onEnterSpy = jasmine.createSpy('enter') - const beforeLeaveSpy = jasmine.createSpy('beforeLeave') - const beforeEnterSpy = jasmine.createSpy('beforeEnter') - const afterLeaveSpy = jasmine.createSpy('afterLeave') - const afterEnterSpy = jasmine.createSpy('afterEnter') - - 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('leave') - const onEnterSpy = jasmine.createSpy('enter') - const beforeLeaveSpy = jasmine.createSpy('beforeLeave') - const beforeEnterSpy = jasmine.createSpy('beforeEnter') - const afterLeaveSpy = jasmine.createSpy('afterLeave') - const afterEnterSpy = jasmine.createSpy('afterEnter') - - 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('enter') - const leaveSpy = jasmine.createSpy('leave') - 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('enter') - const leaveSpy = jasmine.createSpy('leave') - 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('enterCancelled') - 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('afterLeave') - 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('leaveCancelled') - 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('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', 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 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('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) - }) - }) - }) -} diff --git a/test/unit/features/v3/apiAsyncComponent.spec.ts b/test/unit/features/v3/apiAsyncComponent.spec.ts new file mode 100644 index 00000000000..2970738f9d5 --- /dev/null +++ b/test/unit/features/v3/apiAsyncComponent.spec.ts @@ -0,0 +1,241 @@ +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 new file mode 100644 index 00000000000..c467d085a55 --- /dev/null +++ b/test/unit/features/v3/apiInject.spec.ts @@ -0,0 +1,336 @@ +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 new file mode 100644 index 00000000000..21eb33272e3 --- /dev/null +++ b/test/unit/features/v3/apiLifecycle.spec.ts @@ -0,0 +1,360 @@ +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 new file mode 100644 index 00000000000..11757878e9c --- /dev/null +++ b/test/unit/features/v3/apiSetup.spec.ts @@ -0,0 +1,336 @@ +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 new file mode 100644 index 00000000000..a684d16116e --- /dev/null +++ b/test/unit/features/v3/apiWatch.spec.ts @@ -0,0 +1,1234 @@ +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 new file mode 100644 index 00000000000..b03278877cf --- /dev/null +++ b/test/unit/features/v3/reactivity/computed.spec.ts @@ -0,0 +1,301 @@ +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 new file mode 100644 index 00000000000..78966e42e4d --- /dev/null +++ b/test/unit/features/v3/reactivity/effectScope.spec.ts @@ -0,0 +1,318 @@ +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 new file mode 100644 index 00000000000..aa49f103f6e --- /dev/null +++ b/test/unit/features/v3/reactivity/reactive.spec.ts @@ -0,0 +1,312 @@ +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 new file mode 100644 index 00000000000..252fb9884b7 --- /dev/null +++ b/test/unit/features/v3/reactivity/readonly.spec.ts @@ -0,0 +1,538 @@ +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 new file mode 100644 index 00000000000..96212975359 --- /dev/null +++ b/test/unit/features/v3/reactivity/ref.spec.ts @@ -0,0 +1,421 @@ +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 new file mode 100644 index 00000000000..d1125771b90 --- /dev/null +++ b/test/unit/features/v3/reactivity/shallowReactive.spec.ts @@ -0,0 +1,193 @@ +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 new file mode 100644 index 00000000000..6ac3578c675 --- /dev/null +++ b/test/unit/features/v3/reactivity/shallowReadonly.spec.ts @@ -0,0 +1,206 @@ +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 new file mode 100644 index 00000000000..5ac6b879875 --- /dev/null +++ b/test/unit/features/v3/setupTemplateRef.spec.ts @@ -0,0 +1,501 @@ +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 new file mode 100644 index 00000000000..76bb6cca6e2 --- /dev/null +++ b/test/unit/features/v3/useCssVars.spec.ts @@ -0,0 +1,48 @@ +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.js b/test/unit/index.js deleted file mode 100644 index 35df6c940ea..00000000000 --- a/test/unit/index.js +++ /dev/null @@ -1,9 +0,0 @@ -require('es6-promise/auto') - -// import all helpers -const helpersContext = require.context('../helpers', true) -helpersContext.keys().forEach(helpersContext) - -// require all test files -const testsContext = require.context('./', true, /\.spec$/) -testsContext.keys().forEach(testsContext) diff --git a/test/unit/karma.base.config.js b/test/unit/karma.base.config.js deleted file mode 100644 index 68600302598..00000000000 --- a/test/unit/karma.base.config.js +++ /dev/null @@ -1,49 +0,0 @@ -var alias = require('../../build/alias') -var webpack = require('webpack') - -var webpackConfig = { - resolve: { - alias: alias - }, - module: { - rules: [ - { - test: /\.js$/, - loader: 'babel-loader', - exclude: /node_modules/ - } - ] - }, - plugins: [ - new webpack.DefinePlugin({ - __WEEX__: false, - 'process.env': { - NODE_ENV: '"development"', - TRANSITION_DURATION: 50, - TRANSITION_BUFFER: 10 - } - }) - ], - devtool: '#inline-source-map' -} - -// shared config for all unit tests -module.exports = { - frameworks: ['jasmine'], - files: [ - './index.js' - ], - preprocessors: { - './index.js': ['webpack', 'sourcemap'] - }, - webpack: webpackConfig, - webpackMiddleware: { - noInfo: true - }, - plugins: [ - 'karma-jasmine', - 'karma-mocha-reporter', - 'karma-sourcemap-loader', - 'karma-webpack' - ] -} diff --git a/test/unit/karma.cover.config.js b/test/unit/karma.cover.config.js deleted file mode 100644 index 172225bc7ed..00000000000 --- a/test/unit/karma.cover.config.js +++ /dev/null @@ -1,33 +0,0 @@ -var base = require('./karma.base.config.js') - -module.exports = function (config) { - var options = Object.assign(base, { - browsers: ['PhantomJS'], - reporters: ['mocha', 'coverage'], - coverageReporter: { - reporters: [ - { type: 'lcov', dir: '../../coverage', subdir: '.' }, - { type: 'text-summary', dir: '../../coverage', subdir: '.' } - ] - }, - singleRun: true, - plugins: base.plugins.concat([ - 'karma-coverage', - 'karma-phantomjs-launcher' - ]) - }) - - // add babel-plugin-istanbul for code instrumentation - options.webpack.module.rules[0].options = { - plugins: [['istanbul', { - exclude: [ - 'test/', - 'src/compiler/parser/html-parser.js', - 'src/core/instance/proxy.js', - 'src/sfc/deindent.js' - ] - }]] - } - - config.set(options) -} diff --git a/test/unit/karma.dev.config.js b/test/unit/karma.dev.config.js deleted file mode 100644 index 9285861e27b..00000000000 --- a/test/unit/karma.dev.config.js +++ /dev/null @@ -1,11 +0,0 @@ -var base = require('./karma.base.config.js') - -module.exports = function (config) { - config.set(Object.assign(base, { - browsers: ['PhantomJS'], - reporters: ['progress'], - plugins: base.plugins.concat([ - 'karma-phantomjs-launcher' - ]) - })) -} diff --git a/test/unit/karma.sauce.config.js b/test/unit/karma.sauce.config.js deleted file mode 100644 index 55672b56778..00000000000 --- a/test/unit/karma.sauce.config.js +++ /dev/null @@ -1,106 +0,0 @@ -var webpack = require('webpack') -var base = require('./karma.base.config.js') - -base.webpack.plugins = [ - new webpack.DefinePlugin({ - __WEEX__: false, - 'process.env': { - NODE_ENV: '"development"', - // sauce lab vms are slow! - TRANSITION_DURATION: 500, - TRANSITION_BUFFER: 50 - } - }) -] - -/** - * 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' - }, - sl_edge: { - base: 'SauceLabs', - browserName: 'MicrosoftEdge', - platform: 'Windows 10' - } - }, - // mobile - { - sl_ios_safari_9: { - base: 'SauceLabs', - browserName: 'iphone', - version: '10.3' - }, - sl_android_6_0: { - base: 'SauceLabs', - browserName: 'android', - version: '6.0' - } - } -] - -module.exports = function (config) { - var batch = batches[process.argv[4] || 0] - - config.set(Object.assign(base, { - singleRun: true, - browsers: Object.keys(batch), - customLaunchers: batch, - reporters: process.env.CI - ? ['dots', 'saucelabs'] // avoid spamming CI output - : ['progress', 'saucelabs'], - sauceLabs: { - testName: 'Vue.js unit tests', - recordScreenshots: false, - connectOptions: { - 'no-ssl-bump-domains': 'all' // Ignore SSL error on Android emulator - }, - build: process.env.CIRCLE_BUILD_NUM || process.env.SAUCE_BUILD_ID || Date.now() - }, - // mobile emulators are really slow - captureTimeout: 300000, - browserNoActivityTimeout: 300000, - plugins: base.plugins.concat([ - 'karma-sauce-launcher' - ]) - })) -} diff --git a/test/unit/karma.unit.config.js b/test/unit/karma.unit.config.js deleted file mode 100644 index 7840df74e25..00000000000 --- a/test/unit/karma.unit.config.js +++ /dev/null @@ -1,14 +0,0 @@ -var base = require('./karma.base.config.js') - -module.exports = function (config) { - config.set(Object.assign(base, { - browsers: ['Chrome', 'Firefox', 'Safari'], - reporters: ['progress'], - singleRun: true, - plugins: base.plugins.concat([ - 'karma-chrome-launcher', - 'karma-firefox-launcher', - 'karma-safari-launcher' - ]) - })) -} diff --git a/test/unit/modules/compiler/codeframe.spec.ts b/test/unit/modules/compiler/codeframe.spec.ts new file mode 100644 index 00000000000..4ed904d78bf --- /dev/null +++ b/test/unit/modules/compiler/codeframe.spec.ts @@ -0,0 +1,82 @@ +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.js b/test/unit/modules/compiler/codegen.spec.js deleted file mode 100644 index d8b6c7e6839..00000000000 --- a/test/unit/modules/compiler/codegen.spec.js +++ /dev/null @@ -1,555 +0,0 @@ -import { parse } from 'compiler/parser/index' -import { optimize } from 'compiler/optimizer' -import { generate } from 'compiler/codegen' -import { isObject, extend } from 'shared/util' -import { isReservedTag } from 'web/util/index' -import { baseOptions } from 'web/compiler/options' - -function assertCodegen (template, generatedCode, ...args) { - let staticRenderFnCodes = [] - let generateOptions = baseOptions - let proc = 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 (typeof arg === 'function') { - 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) -} - -/* eslint-disable quotes */ -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 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})}))}` - ) - // 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')}))}` - ) - 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')}))}` - ) - // 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')}))}` - ) - 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')}))}` - ) - // 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})}))}` - ) - }) - - 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 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",[_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 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 keycode', () => { - assertCodegen( - '<input @input.enter="onInput">', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;onInput($event)}}})}` - ) - // multiple keycodes (delete) - assertCodegen( - '<input @input.delete="onInput">', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"delete",[8,46],$event.key))return null;onInput($event)}}})}` - ) - // multiple keycodes (chained) - assertCodegen( - '<input @keydown.enter.delete="onInput">', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key)&&_k($event.keyCode,"delete",[8,46],$event.key))return null;onInput($event)}}})}` - ) - // number keycode - assertCodegen( - '<input @input.13="onInput">', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&$event.keyCode!==13)return null;onInput($event)}}})}` - ) - // custom keycode - assertCodegen( - '<input @input.custom="onInput">', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"custom",undefined,$event.key))return null;onInput($event)}}})}` - ) - }) - - it('generate events with generic modifiers', () => { - assertCodegen( - '<input @input.stop="onInput">', - `with(this){return _c('input',{on:{"input":function($event){$event.stopPropagation();onInput($event)}}})}` - ) - assertCodegen( - '<input @input.prevent="onInput">', - `with(this){return _c('input',{on:{"input":function($event){$event.preventDefault();onInput($event)}}})}` - ) - assertCodegen( - '<input @input.self="onInput">', - `with(this){return _c('input',{on:{"input":function($event){if($event.target !== $event.currentTarget)return null;onInput($event)}}})}` - ) - }) - - // 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(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;$event.preventDefault();onInput($event)}}})}` - ) - - assertCodegen( - '<input @keydown.enter.stop="onInput">', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;$event.stopPropagation();onInput($event)}}})}` - ) - }) - - 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;onClick($event)}}})}` - ) - assertCodegen( - '<input @click.shift="onClick">', - `with(this){return _c('input',{on:{"click":function($event){if(!$event.shiftKey)return null;onClick($event)}}})}` - ) - assertCodegen( - '<input @click.alt="onClick">', - `with(this){return _c('input',{on:{"click":function($event){if(!$event.altKey)return null;onClick($event)}}})}` - ) - assertCodegen( - '<input @click.meta="onClick">', - `with(this){return _c('input',{on:{"click":function($event){if(!$event.metaKey)return null;onClick($event)}}})}` - ) - 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;onClick($event)}}})}` - ) - 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;onClick($event)}}})}` - ) - }) - - 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;onInput($event)}}})}` - ) - }) - - it('generate events with capture modifier', () => { - assertCodegen( - '<input @input.capture="onInput">', - `with(this){return _c('input',{on:{"!input":function($event){onInput($event)}}})}` - ) - }) - - it('generate events with once modifier', () => { - assertCodegen( - '<input @input.once="onInput">', - `with(this){return _c('input',{on:{"~input":function($event){onInput($event)}}})}` - ) - }) - - it('generate events with capture and once modifier', () => { - assertCodegen( - '<input @input.capture.once="onInput">', - `with(this){return _c('input',{on:{"~!input":function($event){onInput($event)}}})}` - ) - }) - - it('generate events with once and capture modifier', () => { - assertCodegen( - '<input @input.once.capture="onInput">', - `with(this){return _c('input',{on:{"~!input":function($event){onInput($event)}}})}` - ) - }) - - 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++ }}})}` - ) - // 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(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;(e=>current++)($event)}}})}` - ) - }) - - // #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();onInput($event)}]}})}` - ) - }) - - 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"})}` - ) - }) - - 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:[]}})}` - ) - try { - assertCodegen( - '<my-component inline-template></my-component>', - '' - ) - } catch (e) {} - expect('Inline-template components must have exactly one child element.').toHaveBeenWarned() - expect(console.error.calls.count()).toBe(2) - }) - - 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)])}))}`, - [`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) - }) - - it('not specified ast type', () => { - const res = generate(null, 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 } - ) - }) -}) -/* eslint-enable quotes */ diff --git a/test/unit/modules/compiler/codegen.spec.ts b/test/unit/modules/compiler/codegen.spec.ts new file mode 100644 index 00000000000..aab2505f254 --- /dev/null +++ b/test/unit/modules/compiler/codegen.spec.ts @@ -0,0 +1,743 @@ +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.js b/test/unit/modules/compiler/compiler-options.spec.js deleted file mode 100644 index 78fc8e92363..00000000000 --- a/test/unit/modules/compiler/compiler-options.spec.js +++ /dev/null @@ -1,128 +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++++ }}') - }) -}) diff --git a/test/unit/modules/compiler/compiler-options.spec.ts b/test/unit/modules/compiler/compiler-options.spec.ts new file mode 100644 index 00000000000..865ad0b30c9 --- /dev/null +++ b/test/unit/modules/compiler/compiler-options.spec.ts @@ -0,0 +1,170 @@ +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.js b/test/unit/modules/compiler/optimizer.spec.js deleted file mode 100644 index c807908044b..00000000000 --- a/test/unit/modules/compiler/optimizer.spec.js +++ /dev/null @@ -1,257 +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/optimizer.spec.ts b/test/unit/modules/compiler/optimizer.spec.ts new file mode 100644 index 00000000000..a2b9e351ae3 --- /dev/null +++ b/test/unit/modules/compiler/optimizer.spec.ts @@ -0,0 +1,323 @@ +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.js b/test/unit/modules/compiler/parser.spec.js deleted file mode 100644 index c06ac5ba2f3..00000000000 --- a/test/unit/modules/compiler/parser.spec.js +++ /dev/null @@ -1,605 +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('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-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 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') - }) - - // #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 modifiered 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 = jasmine.createSpy('preTransform') - const spy2 = jasmine.createSpy('postTransform') - 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('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">><foo><</script>`, options) - expect(ast.children[0].text).toBe(`><foo><`) - }) - - 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') - }) -}) diff --git a/test/unit/modules/compiler/parser.spec.ts b/test/unit/modules/compiler/parser.spec.ts new file mode 100644 index 00000000000..1efba124146 --- /dev/null +++ b/test/unit/modules/compiler/parser.spec.ts @@ -0,0 +1,1149 @@ +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">><foo><</script>`, + options + ) + expect(ast.children[0].text).toBe(`><foo><`) + }) + + 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 with whitespace: 'condense'`, () => { + const options = extend({}, condenseOptions) + const ast = parse('<span> </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 new file mode 100644 index 00000000000..de226812caf --- /dev/null +++ b/test/unit/modules/observer/dep.spec.ts @@ -0,0 +1,68 @@ +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.js b/test/unit/modules/observer/observer.spec.js deleted file mode 100644 index 0cda76960b1..00000000000 --- a/test/unit/modules/observer/observer.spec.js +++ /dev/null @@ -1,358 +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 = { - 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 = 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 = {} - 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 = {} - 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 = {} - 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 = {} - Object.defineProperty(obj, 'a', { - configurable: false, - enumerable: true, - val: 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 = [{}, {}] - 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 = { a: { b: 2 }, c: NaN } - observe(obj) - // mock a watcher! - const watcher = { - deps: [], - addDep (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 + a.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 - obj.c - Dep.target = null - expect(watcher.deps.length).toBe(4) - // set on the swapped object - obj.a.b = 5 - expect(watcher.update.calls.count()).toBe(3) - // should not trigger on NaN -> NaN set - obj.c = NaN - expect(watcher.update.calls.count()).toBe(3) - }) - - it('observing object prop change on defined property', () => { - const obj = { val: 2 } - Object.defineProperty(obj, 'a', { - configurable: true, - enumerable: true, - get () { return this.val }, - set (v) { - this.val = v - return this.val - } - }) - - observe(obj) - // mock a watcher! - const 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', () => { - const obj1 = { a: 1 } - const ob1 = observe(obj1) - const dep1 = ob1.dep - spyOn(dep1, 'notify') - setProp(obj1, 'b', 2) - expect(obj1.b).toBe(2) - expect(dep1.notify.calls.count()).toBe(1) - delProp(obj1, 'a') - expect(hasOwn(obj1, 'a')).toBe(false) - expect(dep1.notify.calls.count()).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.calls.count()).toBe(2) - // set non-existing key - setProp(obj1, 'c', 1) - expect(obj1.c).toBe(1) - expect(dep1.notify.calls.count()).toBe(3) - // should ignore deleting non-existing key - delProp(obj1, 'a') - expect(dep1.notify.calls.count()).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 = Object.create(null) - obj3.a = 1 - const ob3 = observe(obj3) - const dep3 = ob3.dep - spyOn(dep3, 'notify') - setProp(obj3, 'b', 2) - expect(obj3.b).toBe(2) - expect(dep3.notify.calls.count()).toBe(1) - delProp(obj3, 'a') - expect(hasOwn(obj3, 'a')).toBe(false) - expect(dep3.notify.calls.count()).toBe(2) - // set and delete non-numeric key on array - const arr2 = ['a'] - const ob2 = observe(arr2) - const dep2 = ob2.dep - spyOn(dep2, 'notify') - setProp(arr2, 'b', 2) - expect(arr2.b).toBe(2) - expect(dep2.notify.calls.count()).toBe(1) - delProp(arr2, 'b') - expect(hasOwn(arr2, 'b')).toBe(false) - expect(dep2.notify.calls.count()).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 = [] - const ob = observe(arr) - const dep = ob.dep - 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.calls.count()).toBe(7) - // inserted elements should be observed - objs.forEach(obj => { - expect(obj.__ob__ instanceof Observer).toBe(true) - }) - }) -}) diff --git a/test/unit/modules/observer/observer.spec.ts b/test/unit/modules/observer/observer.spec.ts new file mode 100644 index 00000000000..72fc03e2e7e --- /dev/null +++ b/test/unit/modules/observer/observer.spec.ts @@ -0,0 +1,400 @@ +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.js b/test/unit/modules/observer/scheduler.spec.js deleted file mode 100644 index 1443375356e..00000000000 --- a/test/unit/modules/observer/scheduler.spec.js +++ /dev/null @@ -1,180 +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 = jasmine.createSpy('scheduler') - }) - - it('queueWatcher', done => { - queueWatcher({ - run: spy - }) - waitForUpdate(() => { - expect(spy.calls.count()).toBe(1) - }).then(done) - }) - - it('dedup', done => { - queueWatcher({ - id: 1, - run: spy - }) - queueWatcher({ - id: 1, - run: spy - }) - waitForUpdate(() => { - expect(spy.calls.count()).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.calls.count()).toBe(2) - }).then(done) - }) - - it('call user watchers before component re-render', done => { - const calls = [] - 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 = [] - 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 = [] - 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/scheduler.spec.ts b/test/unit/modules/observer/scheduler.spec.ts new file mode 100644 index 00000000000..001ee5a429a --- /dev/null +++ b/test/unit/modules/observer/scheduler.spec.ts @@ -0,0 +1,184 @@ +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.js b/test/unit/modules/observer/watcher.spec.js deleted file mode 100644 index 724a3cc8637..00000000000 --- a/test/unit/modules/observer/watcher.spec.js +++ /dev/null @@ -1,181 +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 = jasmine.createSpy('watcher') - }) - - 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.calls.count()).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.calls.count()).toBe(2) - vm.b.c[0].a = 2 - }).then(() => { - expect(spy).toHaveBeenCalledWith(vm.b, vm.b) - expect(spy.calls.count()).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.calls.count()).toBe(1) - vm.b._.c = 1 - }).then(() => { - expect(spy).toHaveBeenCalledWith(vm.b, vm.b) - expect(spy.calls.count()).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.calls.count()).toBe(1) - Vue.delete(vm.b, 'e') - }).then(() => { - expect(spy.calls.count()).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/observer/watcher.spec.ts b/test/unit/modules/observer/watcher.spec.ts new file mode 100644 index 00000000000..1525d30b397 --- /dev/null +++ b/test/unit/modules/observer/watcher.spec.ts @@ -0,0 +1,203 @@ +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 new file mode 100644 index 00000000000..d6bc36393ff --- /dev/null +++ b/test/unit/modules/server-compiler/compiler-options.spec.ts @@ -0,0 +1,16 @@ +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/server-compiler/optimizer.spec.js b/test/unit/modules/server-compiler/optimizer.spec.js deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/unit/modules/sfc/sfc-parser.spec.js b/test/unit/modules/sfc/sfc-parser.spec.js deleted file mode 100644 index 4cf6bad574a..00000000000 --- a/test/unit/modules/sfc/sfc-parser.spec.js +++ /dev/null @@ -1,178 +0,0 @@ -import { parseComponent } from 'sfc/parser' - -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('pad content', () => { - const content = ` - <template> - <div></div> - </template> - <script> - export default {} - </script> - <style> - h1 { color: red } - </style> -` - const padDefault = parseComponent(content.trim(), { pad: true }) - const padLine = parseComponent(content.trim(), { pad: 'line' }) - const padSpace = parseComponent(content.trim(), { pad: 'space' }) - - 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> - `) - 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> - `) - expect(res.customBlocks.length).toBe(3) - - 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') - }) - - // 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') - }) -}) diff --git a/test/unit/modules/util/error.spec.ts b/test/unit/modules/util/error.spec.ts new file mode 100644 index 00000000000..1ed8b72107c --- /dev/null +++ b/test/unit/modules/util/error.spec.ts @@ -0,0 +1,26 @@ +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.js b/test/unit/modules/util/next-tick.spec.js deleted file mode 100644 index 53f6124c1bb..00000000000 --- a/test/unit/modules/util/next-tick.spec.js +++ /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 = jasmine.createSpy() - nextTick(spy) - nextTick().then(() => { - expect(spy).toHaveBeenCalled() - done() - }) - }) - } -}) diff --git a/test/unit/modules/util/next-tick.spec.ts b/test/unit/modules/util/next-tick.spec.ts new file mode 100644 index 00000000000..353848b488f --- /dev/null +++ b/test/unit/modules/util/next-tick.spec.ts @@ -0,0 +1,34 @@ +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 new file mode 100644 index 00000000000..1d7e2a919ab --- /dev/null +++ b/test/unit/modules/util/toString.spec.ts @@ -0,0 +1,11 @@ +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.js b/test/unit/modules/vdom/create-component.spec.js deleted file mode 100644 index 6b88ac7d66f..00000000000 --- a/test/unit/modules/vdom/create-component.spec.js +++ /dev/null @@ -1,119 +0,0 @@ -import Vue from 'vue' -import { createComponent } from 'core/vdom/create-component' - -describe('create-component', () => { - let vm - beforeEach(done => { - vm = new Vue({ - template: '<p>{{msg}}</p>', - data () { - return { msg: 'hello, my children' } - } - }).$mount() - Vue.nextTick(done) - }) - - it('create a component basically', () => { - const child = { - name: 'child', - props: ['msg'], - render () {} - } - const init = jasmine.createSpy() - const data = { - props: { msg: 'hello world' }, - attrs: { id: 1 }, - staticAttrs: { class: 'foo' }, - hook: { init }, - 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) - - vnode.data.hook.init(vnode) - expect(init.calls.argsFor(0)[0]).toBe(vnode) - }) - - it('create a component when resolved with async loading', done => { - let vnode = null - const data = { - props: {}, - staticAttrs: { class: 'foo' } - } - spyOn(vm, '$forceUpdate') - function async (resolve, reject) { - setTimeout(() => { - resolve({ - name: 'child', - props: ['msg'] - }) - Vue.nextTick(loaded) - }, 0) - } - function go () { - vnode = createComponent(async, data, vm, vm) - expect(vnode.isComment).toBe(true) // not to be loaded yet. - expect(vnode.asyncFactory).toBe(async) - } - function loaded () { - vnode = createComponent(async, data, vm, vm) - 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(vm.$forceUpdate).toHaveBeenCalled() - done() - } - go() - }) - - 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 () { - vnode = createComponent(async, data, vm, vm) - expect(vnode.isComment).toBe(true) // not to be loaded yet. - } - function failed () { - vnode = createComponent(async, data, vm, vm) - 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-component.spec.ts b/test/unit/modules/vdom/create-component.spec.ts new file mode 100644 index 00000000000..55a9e085a46 --- /dev/null +++ b/test/unit/modules/vdom/create-component.spec.ts @@ -0,0 +1,155 @@ +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.js b/test/unit/modules/vdom/create-element.spec.js deleted file mode 100644 index 3bec065f312..00000000000 --- a/test/unit/modules/vdom/create-element.spec.js +++ /dev/null @@ -1,245 +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')])]) - expect(vnode.ns).toBe('svg') - expect(vnode.children[0].ns).toBe('svg') - expect(vnode.children[0].children[0].ns).toBeUndefined() - }) - - // #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('nested child elements should be updated correctly', done => { - const vm = new Vue({ - data: { n: 1 }, - render (h) { - const list = [] - 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) - }) -}) diff --git a/test/unit/modules/vdom/create-element.spec.ts b/test/unit/modules/vdom/create-element.spec.ts new file mode 100644 index 00000000000..f97b4bc07cf --- /dev/null +++ b/test/unit/modules/vdom/create-element.spec.ts @@ -0,0 +1,284 @@ +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.js b/test/unit/modules/vdom/modules/attrs.spec.js deleted file mode 100644 index 0ec1cc43694..00000000000 --- a/test/unit/modules/vdom/modules/attrs.spec.js +++ /dev/null @@ -1,102 +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/attrs.spec.ts b/test/unit/modules/vdom/modules/attrs.spec.ts new file mode 100644 index 00000000000..fa654a65292 --- /dev/null +++ b/test/unit/modules/vdom/modules/attrs.spec.ts @@ -0,0 +1,104 @@ +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.js b/test/unit/modules/vdom/modules/class.spec.js deleted file mode 100644 index 174e934d4ac..00000000000 --- a/test/unit/modules/vdom/modules/class.spec.js +++ /dev/null @@ -1,107 +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/class.spec.ts b/test/unit/modules/vdom/modules/class.spec.ts new file mode 100644 index 00000000000..1d0dee4d232 --- /dev/null +++ b/test/unit/modules/vdom/modules/class.spec.ts @@ -0,0 +1,111 @@ +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.js b/test/unit/modules/vdom/modules/directive.spec.js deleted file mode 100644 index 38c53186891..00000000000 --- a/test/unit/modules/vdom/modules/directive.spec.js +++ /dev/null @@ -1,38 +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: jasmine.createSpy('bind'), - update: jasmine.createSpy('update'), - unbind: jasmine.createSpy('unbind') - } - 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/directive.spec.ts b/test/unit/modules/vdom/modules/directive.spec.ts new file mode 100644 index 00000000000..d17d66ba6db --- /dev/null +++ b/test/unit/modules/vdom/modules/directive.spec.ts @@ -0,0 +1,62 @@ +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.js b/test/unit/modules/vdom/modules/dom-props.spec.js deleted file mode 100644 index 90176759ab3..00000000000 --- a/test/unit/modules/vdom/modules/dom-props.spec.js +++ /dev/null @@ -1,90 +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/dom-props.spec.ts b/test/unit/modules/vdom/modules/dom-props.spec.ts new file mode 100644 index 00000000000..0f5d3aa0313 --- /dev/null +++ b/test/unit/modules/vdom/modules/dom-props.spec.ts @@ -0,0 +1,94 @@ +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.js b/test/unit/modules/vdom/modules/events.spec.js deleted file mode 100644 index 9dcd72c4774..00000000000 --- a/test/unit/modules/vdom/modules/events.spec.js +++ /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 = jasmine.createSpy() - const vnode = new VNode('a', { on: { click }}) - - const elm = patch(null, vnode) - document.body.appendChild(elm) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - }) - - it('should not duplicate the same listener', () => { - const click = jasmine.createSpy() - 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) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - }) - - it('should update different listener', () => { - const click = jasmine.createSpy() - const click2 = jasmine.createSpy() - const vnode1 = new VNode('a', { on: { click }}) - const vnode2 = new VNode('a', { on: { click: click2 }}) - - const elm = patch(null, vnode1) - document.body.appendChild(elm) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - expect(click2.calls.count()).toBe(0) - - patch(vnode1, vnode2) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - expect(click2.calls.count()).toBe(1) - }) - - it('should attach Array of multiple handlers', () => { - const click = jasmine.createSpy() - const vnode = new VNode('a', { on: { click: [click, click] }}) - - const elm = patch(null, vnode) - document.body.appendChild(elm) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(2) - }) - - it('should update Array of multiple handlers', () => { - const click = jasmine.createSpy() - const click2 = jasmine.createSpy() - 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) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - expect(click2.calls.count()).toBe(1) - - patch(vnode1, vnode2) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(2) - expect(click2.calls.count()).toBe(1) - }) - - it('should remove handlers that are no longer present', () => { - const click = jasmine.createSpy() - const vnode1 = new VNode('a', { on: { click }}) - const vnode2 = new VNode('a', {}) - - const elm = patch(null, vnode1) - document.body.appendChild(elm) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - - patch(vnode1, vnode2) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - }) - - it('should remove Array handlers that are no longer present', () => { - const click = jasmine.createSpy() - const vnode1 = new VNode('a', { on: { click: [click, click] }}) - const vnode2 = new VNode('a', {}) - - const elm = patch(null, vnode1) - document.body.appendChild(elm) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(2) - - patch(vnode1, vnode2) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(2) - }) - - // #4650 - it('should handle single -> array or array -> single handler changes', () => { - const click = jasmine.createSpy() - const click2 = jasmine.createSpy() - const click3 = jasmine.createSpy() - 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) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(1) - expect(click2.calls.count()).toBe(0) - - patch(vnode0, vnode1) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(2) - expect(click2.calls.count()).toBe(1) - - patch(vnode1, vnode2) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(3) - expect(click2.calls.count()).toBe(1) - - patch(vnode2, vnode3) - triggerEvent(elm, 'click') - expect(click.calls.count()).toBe(3) - expect(click2.calls.count()).toBe(2) - expect(click3.calls.count()).toBe(1) - }) -}) diff --git a/test/unit/modules/vdom/modules/events.spec.ts b/test/unit/modules/vdom/modules/events.spec.ts new file mode 100644 index 00000000000..514054783aa --- /dev/null +++ b/test/unit/modules/vdom/modules/events.spec.ts @@ -0,0 +1,135 @@ +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.js b/test/unit/modules/vdom/modules/style.spec.js deleted file mode 100644 index 13ee5fce5c6..00000000000 --- a/test/unit/modules/vdom/modules/style.spec.js +++ /dev/null @@ -1,35 +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') - }) -}) diff --git a/test/unit/modules/vdom/modules/style.spec.ts b/test/unit/modules/vdom/modules/style.spec.ts new file mode 100644 index 00000000000..19bbb9c8453 --- /dev/null +++ b/test/unit/modules/vdom/modules/style.spec.ts @@ -0,0 +1,54 @@ +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.js b/test/unit/modules/vdom/patch/children.spec.js deleted file mode 100644 index 1bba91f11d2..00000000000 --- a/test/unit/modules/vdom/patch/children.spec.js +++ /dev/null @@ -1,510 +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 = [] - 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 = [] - const opacities = [] - 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) - }) -}) diff --git a/test/unit/modules/vdom/patch/children.spec.ts b/test/unit/modules/vdom/patch/children.spec.ts new file mode 100644 index 00000000000..2cdc142218e --- /dev/null +++ b/test/unit/modules/vdom/patch/children.spec.ts @@ -0,0 +1,577 @@ +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.js b/test/unit/modules/vdom/patch/edge-cases.spec.js deleted file mode 100644 index 7e140534173..00000000000 --- a/test/unit/modules/vdom/patch/edge-cases.spec.js +++ /dev/null @@ -1,249 +0,0 @@ -import Vue from 'vue' - -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) - }) - - // #3533 - // a static node (<br>) 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"> - <br><p>{{ 1 }}</p> - </div> - <div class="d"> - <label>{{ 2 }}</label> - </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 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`) - - 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`) - }).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 = jasmine.createSpy() - 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() - }) -}) diff --git a/test/unit/modules/vdom/patch/edge-cases.spec.ts b/test/unit/modules/vdom/patch/edge-cases.spec.ts new file mode 100644 index 00000000000..1b27f2b630c --- /dev/null +++ b/test/unit/modules/vdom/patch/edge-cases.spec.ts @@ -0,0 +1,508 @@ +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.js b/test/unit/modules/vdom/patch/element.spec.js deleted file mode 100644 index 09bf3fa6ef8..00000000000 --- a/test/unit/modules/vdom/patch/element.spec.js +++ /dev/null @@ -1,60 +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 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/element.spec.ts b/test/unit/modules/vdom/patch/element.spec.ts new file mode 100644 index 00000000000..2838b87da94 --- /dev/null +++ b/test/unit/modules/vdom/patch/element.spec.ts @@ -0,0 +1,62 @@ +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.js b/test/unit/modules/vdom/patch/hooks.spec.js deleted file mode 100644 index dbb7ee3bd1b..00000000000 --- a/test/unit/modules/vdom/patch/hooks.spec.js +++ /dev/null @@ -1,321 +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) - -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 = [] - 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 = [] - 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 = [] - const post = [] - 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 = [] - const result2 = [] - 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 = [] - 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, - 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 = [] - 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 = [] - 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 = [] - 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/hooks.spec.ts b/test/unit/modules/vdom/patch/hooks.spec.ts new file mode 100644 index 00000000000..ba80c700739 --- /dev/null +++ b/test/unit/modules/vdom/patch/hooks.spec.ts @@ -0,0 +1,378 @@ +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.js b/test/unit/modules/vdom/patch/hydration.spec.js deleted file mode 100644 index 42fb337d55a..00000000000 --- a/test/unit/modules/vdom/patch/hydration.spec.js +++ /dev/null @@ -1,345 +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 = [] - 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 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() - }) -}) diff --git a/test/unit/modules/vdom/patch/hydration.spec.ts b/test/unit/modules/vdom/patch/hydration.spec.ts new file mode 100644 index 00000000000..a757226f83d --- /dev/null +++ b/test/unit/modules/vdom/patch/hydration.spec.ts @@ -0,0 +1,413 @@ +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/vitest.setup.ts b/test/vitest.setup.ts new file mode 100644 index 00000000000..38397765f06 --- /dev/null +++ b/test/vitest.setup.ts @@ -0,0 +1,13 @@ +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/test/weex/.eslintrc b/test/weex/.eslintrc deleted file mode 100644 index dab5d0ea656..00000000000 --- a/test/weex/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "env": { - "jasmine": true - }, - "plugins": ["jasmine"], - "rules": { - "jasmine/no-focused-tests": 2 - } -} diff --git a/test/weex/cases/cases.spec.js b/test/weex/cases/cases.spec.js deleted file mode 100644 index 291600dfb24..00000000000 --- a/test/weex/cases/cases.spec.js +++ /dev/null @@ -1,72 +0,0 @@ -import fs from 'fs' -import path from 'path' -import { - compileVue, - createInstance, - getRoot, - getEvents, - fireEvent -} from '../helpers' - -function readFile (filename) { - return fs.readFileSync(path.resolve(__dirname, filename), 'utf8') -} - -function readObject (filename) { - return (new Function(`return ${readFile(filename)}`))() -} - -// Create one-off render test case -function createRenderTestCase (name) { - const source = readFile(`${name}.vue`) - const target = readObject(`${name}.vdom.js`) - return done => { - compileVue(source).then(code => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, code) - setTimeout(() => { - expect(getRoot(instance)).toEqual(target) - done() - }, 50) - }).catch(err => { - expect(err).toBe(null) - done() - }) - } -} - -// Create event test case, will trigger the first bind event -function createEventTestCase (name) { - const source = readFile(`${name}.vue`) - const before = readObject(`${name}.before.vdom.js`) - const after = readObject(`${name}.after.vdom.js`) - return done => { - compileVue(source).then(code => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, code) - setTimeout(() => { - expect(getRoot(instance)).toEqual(before) - const event = getEvents(instance)[0] - fireEvent(instance, event.ref, event.type, {}) - setTimeout(() => { - expect(getRoot(instance)).toEqual(after) - done() - }, 50) - }, 50) - }).catch(err => { - expect(err).toBe(null) - done() - }) - } -} - -describe('Usage', () => { - describe('render', () => { - it('sample', createRenderTestCase('render/sample')) - }) - - describe('event', () => { - it('click', createEventTestCase('event/click')) - }) -}) - diff --git a/test/weex/cases/event/click.after.vdom.js b/test/weex/cases/event/click.after.vdom.js deleted file mode 100644 index eab1a36e7a3..00000000000 --- a/test/weex/cases/event/click.after.vdom.js +++ /dev/null @@ -1,10 +0,0 @@ -({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - attr: { - value: '43' - } - }] -}) diff --git a/test/weex/cases/event/click.before.vdom.js b/test/weex/cases/event/click.before.vdom.js deleted file mode 100644 index de278b3a100..00000000000 --- a/test/weex/cases/event/click.before.vdom.js +++ /dev/null @@ -1,10 +0,0 @@ -({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - attr: { - value: '42' - } - }] -}) diff --git a/test/weex/cases/event/click.vue b/test/weex/cases/event/click.vue deleted file mode 100644 index 508782c59e7..00000000000 --- a/test/weex/cases/event/click.vue +++ /dev/null @@ -1,20 +0,0 @@ -<template> - <div @click="inc"> - <text>{{count}}</text> - </div> -</template> - -<script> - module.exports = { - data () { - return { - count: 42 - } - }, - methods: { - inc () { - this.count++ - } - } - } -</script> diff --git a/test/weex/cases/render/sample.vdom.js b/test/weex/cases/render/sample.vdom.js deleted file mode 100644 index 6e213a506d9..00000000000 --- a/test/weex/cases/render/sample.vdom.js +++ /dev/null @@ -1,17 +0,0 @@ -({ - type: 'div', - style: { - justifyContent: 'center' - }, - children: [{ - type: 'text', - attr: { - value: 'Yo' - }, - style: { - color: '#41B883', - fontSize: '233px', - textAlign: 'center' - } - }] -}) diff --git a/test/weex/cases/render/sample.vue b/test/weex/cases/render/sample.vue deleted file mode 100644 index 0251b3d6fba..00000000000 --- a/test/weex/cases/render/sample.vue +++ /dev/null @@ -1,23 +0,0 @@ -<template> - <div style="justify-content:center"> - <text class="freestyle">{{string}}</text> - </div> -</template> - -<style scoped> - .freestyle { - color: #41B883; - font-size: 233px; - text-align: center; - } -</style> - -<script> - module.exports = { - data () { - return { - string: 'Yo' - } - } - } -</script> diff --git a/test/weex/compiler/append.spec.js b/test/weex/compiler/append.spec.js deleted file mode 100644 index 341a8fb23e6..00000000000 --- a/test/weex/compiler/append.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import { compile } from '../../../packages/weex-template-compiler' -import { strToRegExp } from '../helpers/index' - -describe('append props', () => { - it('append="tree"', () => { - const { render, staticRenderFns, errors } = compile(`<list><cell></cell></list>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(1) - expect(staticRenderFns).toMatch(strToRegExp(`appendAsTree:true`)) - expect(staticRenderFns).toMatch(strToRegExp(`attrs:{"append":"tree"}`)) - expect(errors).toEqual([]) - }) -}) diff --git a/test/weex/compiler/class.spec.js b/test/weex/compiler/class.spec.js deleted file mode 100644 index a7e88ab83a8..00000000000 --- a/test/weex/compiler/class.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -import { compile } from '../../../packages/weex-template-compiler' -import { strToRegExp } from '../helpers/index' - -describe('compile class', () => { - it('should be compiled', () => { - const { render, staticRenderFns, errors } = compile(`<div class="a b c"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(0) - expect(render).toMatch(strToRegExp(`staticClass:["a","b","c"]`)) - expect(errors).toEqual([]) - }) - - it('should compile dynamic class', () => { - const { render, staticRenderFns, errors } = compile(`<div class="a {{b}} c"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`class:["a",_s(b),"c"]`)) - expect(errors).not.toBeUndefined() - expect(errors.length).toEqual(1) - expect(errors[0]).toMatch(strToRegExp(`a {{b}} c`)) - expect(errors[0]).toMatch(strToRegExp(`v-bind`)) - }) - - it('should compile class binding of array', () => { - const { render, staticRenderFns, errors } = compile(`<div v-bind:class="['a', 'b', c]"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`class:['a', 'b', c]`)) - expect(errors).toEqual([]) - }) - - it('should compile class binding of map', () => { - const { render, staticRenderFns, errors } = compile(`<div v-bind:class="{ a: true, b: x }"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`class:{ a: true, b: x }`)) - expect(errors).toEqual([]) - }) - - it('should compile class binding of a variable', () => { - const { render, staticRenderFns, errors } = compile(`<div v-bind:class="x"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`class:x`)) - expect(errors).toEqual([]) - }) - - it('should compile class binding by shorthand', () => { - const { render, staticRenderFns, errors } = compile(`<div :class="['a', 'b', c]"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`class:['a', 'b', c]`)) - expect(errors).toEqual([]) - }) -}) diff --git a/test/weex/compiler/compile.spec.js b/test/weex/compiler/compile.spec.js deleted file mode 100644 index 7a717f19146..00000000000 --- a/test/weex/compiler/compile.spec.js +++ /dev/null @@ -1,76 +0,0 @@ -import { compile } from '../../../packages/weex-template-compiler' -import { strToRegExp } from '../helpers/index' - -describe('compile basic', () => { - it('should be compiled', () => { - const { render, staticRenderFns, errors } = compile(`<div>{{hi}}</div>`) - expect(render).toEqual(`with(this){return _c('div',[_v(_s(hi))])}`) - expect(staticRenderFns.length).toBe(0) - expect(errors).toEqual([]) - }) - - it('should compile data bindings', () => { - const { render, staticRenderFns, errors } = compile(`<div :a="b"></div>`) - expect(render).toEqual(`with(this){return _c('div',{attrs:{"a":b}})}`) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) - - it('should compile event bindings', () => { - const { render, staticRenderFns, errors } = compile(`<div @click="x"></div>`) - expect(render).toEqual(`with(this){return _c('div',{on:{"click":x}})}`) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) - - it('should compile data bindings with children', () => { - const { render, staticRenderFns, errors } = compile(`<foo :a="b"><text>Hello</text></foo>`) - expect(render).toEqual(`with(this){return _c('foo',{attrs:{"a":b}},[_c('text',[_v("Hello")])])}`) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) - - it('should compile unary tag', () => { - const inputCase = compile(`<div><input><text>abc</text></div>`) - expect(inputCase.render).toMatch(strToRegExp(`return _m(0)`)) - expect(inputCase.staticRenderFns).toMatch(strToRegExp(`_c('div',[_c('input'),_c('text',[_v("abc")])])`)) - expect(inputCase.errors).toEqual([]) - - const imageCase = compile(`<div><image src="path"><text>abc</text></div>`) - expect(imageCase.render).toMatch(strToRegExp(`return _m(0)`)) - expect(imageCase.staticRenderFns).toMatch(strToRegExp(`_c('div',[_c('image',{attrs:{"src":"path"}}),_c('text',[_v("abc")])])`)) - expect(imageCase.errors).toEqual([]) - - const complexCase = compile(` - <div> - <image src="path"> - <image></image> - <div> - <embed> - <text>start</text> - <input type="text"> - <input type="url" /> - <text>end</text> - </div> - </div> - `) - expect(complexCase.render).toMatch(strToRegExp(`return _m(0)`)) - expect(complexCase.staticRenderFns).toMatch(strToRegExp(`_c('image',{attrs:{"src":"path"}}),_c('image'),_c('div'`)) - expect(complexCase.staticRenderFns).toMatch(strToRegExp(`_c('div',[_c('embed'),_c('text',[_v("start")]),_c('input',{attrs:{"type":"text"}}),_c('input',{attrs:{"type":"url"}}),_c('text',[_v("end")])]`)) - expect(complexCase.errors).toEqual([]) - }) - - it('should compile more complex situation', () => { - // from examples of https://github.com/alibaba/weex - const { render, staticRenderFns, errors } = compile(` - <refresh class="refresh" @refresh="handleRefresh" :display="displayRefresh" - style="flex-direction:row;"> - <loading-indicator></loading-indicator> - <text style="margin-left:36px;color:#eee;">Load more...</text> - </refresh> - `) - expect(render).toEqual(`with(this){return _c('refresh',{staticClass:["refresh"],staticStyle:{flexDirection:"row"},attrs:{"display":displayRefresh},on:{"refresh":handleRefresh}},[_c('loading-indicator'),_c('text',{staticStyle:{marginLeft:"36px",color:"#eee"}},[_v("Load more...")])])}`) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) -}) diff --git a/test/weex/compiler/props.spec.js b/test/weex/compiler/props.spec.js deleted file mode 100644 index 85f4e10ab70..00000000000 --- a/test/weex/compiler/props.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -import { compile } from '../../../packages/weex-template-compiler' -import { strToRegExp } from '../helpers/index' - -describe('compile props', () => { - it('custom props', () => { - const { render, staticRenderFns, errors } = compile(`<div custom="whatever"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(0) - expect(render).toMatch(strToRegExp(`attrs:{"custom":"whatever"}`)) - expect(errors).toEqual([]) - }) - - it('camelize props', () => { - const { render, staticRenderFns, errors } = compile(`<div kebab-case="whatever"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(0) - expect(render).toMatch(strToRegExp(`attrs:{"kebabCase":"whatever"}`)) - expect(errors).toEqual([]) - }) -}) diff --git a/test/weex/compiler/style.spec.js b/test/weex/compiler/style.spec.js deleted file mode 100644 index efde3c35aad..00000000000 --- a/test/weex/compiler/style.spec.js +++ /dev/null @@ -1,83 +0,0 @@ -import { compile } from '../../../packages/weex-template-compiler' -import { strToRegExp } from '../helpers/index' - -describe('compile style', () => { - it('should be compiled', () => { - const { render, staticRenderFns, errors } = compile(`<div style="a: x; b: y"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(0) - expect(render).toMatch(strToRegExp(`staticStyle:{a:"x",b:"y"}`)) - expect(errors).toEqual([]) - }) - - it('should compile empty style value', () => { - const { render, staticRenderFns, errors } = compile(`<div style=""></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(0) - expect(render).toMatch(/[(^style|^staticStyle)]/) - expect(errors).toEqual([]) - }) - - it('should compile style value with trailing semicolon', () => { - const { render, staticRenderFns, errors } = compile(`<div style="a: x; b: y;"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(0) - expect(render).toMatch(strToRegExp(`staticStyle:{a:"x",b:"y"}`)) - expect(errors).toEqual([]) - }) - - it('should compile hyphenated style name & value', () => { - const { render, staticRenderFns, errors } = compile(`<div style="-abc-def: x-y; abc-def: x-y"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).not.toBeUndefined() - expect(staticRenderFns.length).toEqual(0) - expect(render).toMatch(strToRegExp(`staticStyle:{AbcDef:"x-y",abcDef:"x-y"}`)) - expect(errors).toEqual([]) - }) - - it('should compile dynamic style', () => { - const { render, staticRenderFns, errors } = compile(`<div style="a: x; b: {{y}}"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`style:{a:"x",b:_s(y)}`)) - expect(errors).not.toBeUndefined() - expect(errors.length).toEqual(1) - expect(errors[0]).toMatch(strToRegExp(`b: {{y}}`)) - expect(errors[0]).toMatch(strToRegExp(`v-bind`)) - }) - - it('should compile style binding of array', () => { - const { render, staticRenderFns, errors } = compile(`<div v-bind:style="[a, b, c]"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`style:[a, b, c]`)) - expect(errors).toEqual([]) - }) - - it('should compile style binding of map', () => { - const { render, staticRenderFns, errors } = compile(`<div v-bind:style="{ a: x, b: 'y' + z }"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`style:{ a: x, b: 'y' + z }`)) - expect(errors).toEqual([]) - }) - - it('should compile style binding of a variable', () => { - const { render, staticRenderFns, errors } = compile(`<div v-bind:style="x"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`style:x`)) - expect(errors).toEqual([]) - }) - - it('should compile style binding by shorthand', () => { - const { render, staticRenderFns, errors } = compile(`<div :style="[a, b, c]"></div>`) - expect(render).not.toBeUndefined() - expect(staticRenderFns).toEqual([]) - expect(render).toMatch(strToRegExp(`style:[a, b, c]`)) - expect(errors).toEqual([]) - }) -}) diff --git a/test/weex/compiler/v-model.spec.js b/test/weex/compiler/v-model.spec.js deleted file mode 100644 index c5f0401dc7b..00000000000 --- a/test/weex/compiler/v-model.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -import { compile } from '../../../packages/weex-template-compiler' -import { strToRegExp } from '../helpers/index' - -describe('compile v-model', () => { - it('should compile modelable native component', () => { - const { render, staticRenderFns, errors } = compile(`<div><input v-model="x" /></div>`) - expect(render).not.toBeUndefined() - expect(render).toMatch(strToRegExp(`attrs:{"value":(x)}`)) - expect(render).toMatch(strToRegExp(`on:{"input":function($event){x=$event.target.attr.value}}`)) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) - - it('should compile other component with whole $event as the value', () => { - const { render, staticRenderFns, errors } = compile(`<div><foo v-model="x" /></div>`) - expect(render).not.toBeUndefined() - expect(render).toMatch(strToRegExp(`model:{value:(x),callback:function ($$v) {x=$$v},expression:"x"}`)) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) - - it('should compile with trim modifier for modelable native component', () => { - const { render, staticRenderFns, errors } = compile(`<div><input v-model.trim="x" /></div>`) - expect(render).not.toBeUndefined() - expect(render).toMatch(strToRegExp(`attrs:{"value":(x)}`)) - expect(render).toMatch(strToRegExp(`on:{"input":function($event){x=$event.target.attr.value.trim()}}`)) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) - - it('should compile with trim & lazy modifier', () => { - const { render, staticRenderFns, errors } = compile(`<div><input v-model.trim.lazy="x" /><input v-model.lazy.trim="y" /></div>`) - expect(render).not.toBeUndefined() - expect(render).toMatch(strToRegExp(`attrs:{"value":(x)}`)) - expect(render).toMatch(strToRegExp(`attrs:{"value":(y)}`)) - expect(render).toMatch(strToRegExp(`on:{"change":function($event){x=$event.target.attr.value.trim()}}`)) - expect(render).toMatch(strToRegExp(`on:{"change":function($event){y=$event.target.attr.value.trim()}}`)) - expect(staticRenderFns).toEqual([]) - expect(errors).toEqual([]) - }) -}) diff --git a/test/weex/helpers/index.js b/test/weex/helpers/index.js deleted file mode 100644 index 653ded34c16..00000000000 --- a/test/weex/helpers/index.js +++ /dev/null @@ -1,168 +0,0 @@ -import * as Vue from '../../../packages/weex-vue-framework' -import { compile } from '../../../packages/weex-template-compiler' -import WeexRuntime from 'weex-js-runtime' -import styler from 'weex-styler' - -const styleRE = /<\s*style\s*\w*>([^(<\/)]*)<\/\s*style\s*>/g -const scriptRE = /<\s*script.*>([^]*)<\/\s*script\s*>/ -const templateRE = /<\s*template\s*>([^]*)<\/\s*template\s*>/ - -console.debug = () => {} - -// http://stackoverflow.com/a/35478115 -const matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g -export function strToRegExp (str) { - return new RegExp(str.replace(matchOperatorsRe, '\\$&')) -} - -function parseStatic (fns) { - return '[' + fns.map(fn => `function () { ${fn} }`).join(',') + ']' -} - -export function compileAndStringify (template) { - const { render, staticRenderFns } = compile(template) - return { - render: `function () { ${render} }`, - staticRenderFns: parseStatic(staticRenderFns) - } -} - -/** - * Compile *.vue file into js code - * @param {string} source raw text of *.vue file - * @param {string} componentName whether compile to a component - */ -export function compileVue (source, componentName) { - return new Promise((resolve, reject) => { - if (!templateRE.test(source)) { - return reject('No Template!') - } - const scriptMatch = scriptRE.exec(source) - const script = scriptMatch ? scriptMatch[1] : '' - const { render, staticRenderFns } = compile(templateRE.exec(source)[1]) - - const generateCode = styles => (` - var test_case = Object.assign({ - style: ${JSON.stringify(styles)}, - render: function () { ${render} }, - staticRenderFns: ${parseStatic(staticRenderFns)}, - }, (function(){ - var module = { exports: {} }; - ${script}; - return module.exports; - })()); - ` + (componentName - ? `Vue.component('${componentName}', test_case);\n` - : `test_case.el = 'body';new Vue(test_case);`) - ) - - let cssText = '' - let styleMatch = null - while ((styleMatch = styleRE.exec(source))) { - cssText += `\n${styleMatch[1]}\n` - } - styler.parse(cssText, (error, result) => { - if (error) { - return reject(error) - } - resolve(generateCode(result.jsonStyle)) - }) - resolve(generateCode({})) - }) -} - -function isObject (object) { - return object !== null && typeof object === 'object' -} - -function isEmptyObject (object) { - return isObject(object) && Object.keys(object).length < 1 -} - -function omitUseless (object) { - if (isObject(object)) { - delete object.ref - for (const key in object) { - if (isEmptyObject(object[key]) || object[key] === undefined) { - delete object[key] - } - omitUseless(object[key]) - } - } - return object -} - -export function getRoot (instance) { - return omitUseless(instance.document.body.toJSON()) -} - -// Get all binding events in the instance -export function getEvents (instance) { - const events = [] - const recordEvent = node => { - if (!node) { return } - if (Array.isArray(node.event)) { - node.event.forEach(type => { - events.push({ ref: node.ref, type }) - }) - } - if (Array.isArray(node.children)) { - node.children.forEach(recordEvent) - } - } - recordEvent(instance.document.body.toJSON()) - return events -} - -export function fireEvent (instance, ref, type, event = {}) { - const el = instance.document.getRef(ref) - if (el) { - instance.document.fireEvent(el, type, event = {}) - } -} - -export function createInstance (id, code, ...args) { - WeexRuntime.config.frameworks = { Vue } - const context = WeexRuntime.init(WeexRuntime.config) - context.registerModules({ - timer: ['setTimeout', 'setInterval'] - }) - const instance = context.createInstance(id, `// { "framework": "Vue" }\n${code}`, ...args) - instance.$refresh = (data) => context.refreshInstance(id, data) - instance.$destroy = () => context.destroyInstance(id) - return instance -} - -export function compileAndExecute (template, additional = '') { - return new Promise(resolve => { - const id = String(Date.now() * Math.random()) - const { render, staticRenderFns } = compile(template) - const instance = createInstance(id, ` - new Vue({ - el: '#whatever', - render: function () { ${render} }, - staticRenderFns: ${parseStatic(staticRenderFns)}, - ${additional} - }) - `) - setTimeout(() => resolve(instance), 10) - }) -} - -export function syncPromise (arr) { - let p = Promise.resolve() - arr.forEach(item => { - p = p.then(item) - }) - return p -} - -export function checkRefresh (instance, data, checker) { - return () => new Promise(res => { - instance.$refresh(data) - setTimeout(() => { - checker(getRoot(instance)) - res() - }) - }) -} diff --git a/test/weex/jasmine.json b/test/weex/jasmine.json deleted file mode 100644 index 1dba6930851..00000000000 --- a/test/weex/jasmine.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "spec_dir": "test/weex", - "spec_files": [ - "**/*[sS]pec.js" - ], - "helpers": [ - "../../node_modules/babel-register/lib/node.js" - ] -} diff --git a/test/weex/runtime/attrs.spec.js b/test/weex/runtime/attrs.spec.js deleted file mode 100644 index 037067ac1a2..00000000000 --- a/test/weex/runtime/attrs.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -import { getRoot, fireEvent, compileAndExecute } from '../helpers/index' - -describe('generate attribute', () => { - it('should be generated', (done) => { - compileAndExecute(` - <div> - <text value="Hello World" style="font-size: 100"></text> - </div> - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - style: { fontSize: '100' }, - attr: { value: 'Hello World' } - }] - }) - done() - }).catch(e => done.fail(e)) - }) - - it('should be updated', (done) => { - compileAndExecute(` - <div @click="foo"> - <text :value="x"></text> - </div> - `, ` - data: { x: 'Hello World' }, - methods: { - foo: function () { - this.x = 'Hello Vue' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [ - { type: 'text', attr: { value: 'Hello World' }} - ] - }) - fireEvent(instance, '_root', 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [ - { type: 'text', attr: { value: 'Hello Vue' }} - ] - }) - done() - }).catch(e => done.fail(e)) - }) - - it('should be cleared', (done) => { - compileAndExecute(` - <div @click="foo"> - <text :value="x"></text> - </div> - `, ` - data: { x: 'Hello World' }, - methods: { - foo: function () { - this.x = '' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [ - { type: 'text', attr: { value: 'Hello World' }} - ] - }) - fireEvent(instance, '_root', 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [ - { type: 'text', attr: { value: '' }} - ] - }) - done() - }) - }) -}) diff --git a/test/weex/runtime/class.spec.js b/test/weex/runtime/class.spec.js deleted file mode 100644 index 5809ba75633..00000000000 --- a/test/weex/runtime/class.spec.js +++ /dev/null @@ -1,161 +0,0 @@ -import { getRoot, fireEvent, compileAndExecute } from '../helpers/index' - -describe('generate class', () => { - it('should be generated', () => { - compileAndExecute(` - <div> - <text class="a b c">Hello World</text> - </div> - `, ` - style: { - a: { fontSize: '100' }, - b: { color: '#ff0000' }, - c: { fontWeight: 'bold' } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - style: { fontSize: '100', color: '#ff0000', fontWeight: 'bold' }, - attr: { value: 'Hello World' } - }] - }) - }) - }) - - it('should be updated', (done) => { - compileAndExecute(` - <div @click="foo"> - <text :class="['a', x]">Hello World</text> - </div> - `, ` - data: { x: 'b' }, - style: { - a: { fontSize: '100' }, - b: { color: '#ff0000' }, - c: { fontWeight: 'bold' }, - d: { - color: '#0000ff', - fontWeight: 'bold' - } - }, - methods: { - foo: function () { - this.x = 'd' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - style: { fontSize: '100', color: '#ff0000' }, - attr: { value: 'Hello World' } - }] - }) - fireEvent(instance, '_root', 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - style: { fontSize: '100', color: '#0000ff', fontWeight: 'bold' }, - attr: { value: 'Hello World' } - }] - }) - done() - }) - }) - - it('should be applied in order', (done) => { - compileAndExecute(` - <div @click="foo"> - <text :class="arr">Hello World</text> - </div> - `, ` - data: { - arr: ['b', 'a'] - }, - style: { - a: { color: '#ff0000' }, - b: { color: '#00ff00' }, - c: { color: '#0000ff' } - }, - methods: { - foo: function () { - this.arr.push('c') - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - style: { color: '#ff0000' }, - attr: { value: 'Hello World' } - }] - }) - fireEvent(instance, '_root', 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - style: { color: '#0000ff' }, - attr: { value: 'Hello World' } - }] - }) - done() - }) - }) - - it('should be cleared', (done) => { - compileAndExecute(` - <div @click="foo"> - <text :class="['a', x]">Hello World</text> - </div> - `, ` - data: { x: 'b' }, - style: { - a: { fontSize: '100' }, - b: { color: '#ff0000' }, - c: { fontWeight: 'bold' } - }, - methods: { - foo: function () { - this.x = 'c' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - style: { fontSize: '100', color: '#ff0000' }, - attr: { value: 'Hello World' } - }] - }) - fireEvent(instance, '_root', 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - style: { fontSize: '100', color: '', fontWeight: 'bold' }, - attr: { value: 'Hello World' } - }] - }) - done() - }) - }) -}) diff --git a/test/weex/runtime/components/richtext.spec.js b/test/weex/runtime/components/richtext.spec.js deleted file mode 100644 index f1c21caf9d1..00000000000 --- a/test/weex/runtime/components/richtext.spec.js +++ /dev/null @@ -1,691 +0,0 @@ -import { - compileAndStringify, - getRoot, - fireEvent, - createInstance -} from '../../helpers/index' - -function compileSnippet (snippet, additional) { - const { render, staticRenderFns } = compileAndStringify(`<div>${snippet}</div>`) - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - el: 'body', - render: ${render}, - staticRenderFns: ${staticRenderFns}, - ${additional} - }) - `) - return getRoot(instance).children[0] -} - -describe('richtext component', () => { - it('with no child', () => { - expect(compileSnippet(` - <richtext></richtext> - `)).toEqual({ - type: 'richtext' - }) - }) - - it('with single text node', () => { - expect(compileSnippet(` - <richtext>single</richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { - value: 'single' - } - }] - } - }) - }) - - describe('span', () => { - it('single node', () => { - expect(compileSnippet(` - <richtext> - <span>single</span> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { - value: 'single' - } - }] - } - }) - }) - - it('multiple node', () => { - expect(compileSnippet(` - <richtext> - <span>AAA</span> - <span>BBB</span> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { value: 'AAA' } - }, { - type: 'span', - attr: { value: 'BBB' } - }] - } - }) - }) - - it('with raw text', () => { - expect(compileSnippet(` - <richtext> - AAA - <span>BBB</span>CCC - <span>DDD</span> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { value: 'AAA' } - }, { - type: 'span', - attr: { value: 'BBB' } - }, { - type: 'span', - attr: { value: 'CCC' } - }, { - type: 'span', - attr: { value: 'DDD' } - }] - } - }) - }) - }) - - describe('a', () => { - it('single node', () => { - expect(compileSnippet(` - <richtext> - <a href="http://whatever.com"></a> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'a', - attr: { href: 'http://whatever.com' } - }] - } - }) - }) - - it('multiple node', () => { - expect(compileSnippet(` - <richtext> - <a href="http://a.whatever.com"></a> - <a href="http://b.whatever.com"></a> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'a', - attr: { href: 'http://a.whatever.com' } - }, { - type: 'a', - attr: { href: 'http://b.whatever.com' } - }] - } - }) - }) - }) - - describe('image', () => { - it('single node', () => { - expect(compileSnippet(` - <richtext> - <image src="path/to/profile.png"></image> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'image', - attr: { src: 'path/to/profile.png' } - }] - } - }) - }) - - it('multiple node', () => { - expect(compileSnippet(` - <richtext> - <image src="path/to/A.png"></image> - <image src="path/to/B.png"></image> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'image', - attr: { src: 'path/to/A.png' } - }, { - type: 'image', - attr: { src: 'path/to/B.png' } - }] - } - }) - }) - - it('with width and height', () => { - expect(compileSnippet(` - <richtext> - <image - style="width:150px;height:150px;" - src="path/to/profile.png"> - </image> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'image', - style: { width: '150px', height: '150px' }, - attr: { src: 'path/to/profile.png' } - }] - } - }) - }) - }) - - describe('nested', () => { - it('span', () => { - expect(compileSnippet(` - <richtext> - <span>AAA - <span> - <span>BBB</span> - <span><span>CCC</span>DDD</span> - </span> - </span> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - children: [{ - type: 'span', - attr: { value: 'AAA' } - }, { - type: 'span', - children: [{ - type: 'span', - attr: { value: 'BBB' } - }, { - type: 'span', - children: [{ - type: 'span', - attr: { value: 'CCC' } - }, { - type: 'span', - attr: { value: 'DDD' } - }] - }] - }] - }] - } - }) - }) - - it('image and a', () => { - expect(compileSnippet(` - <richtext> - <span>title</span> - <a href="http://remote.com/xx.js"> - <span><span>name</span></span> - <image src="path/to/yy.gif"></image> - </a> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { value: 'title' } - }, { - type: 'a', - attr: { href: 'http://remote.com/xx.js' }, - children: [{ - type: 'span', - children: [{ - type: 'span', - attr: { value: 'name' } - }] - }, { - type: 'image', - attr: { src: 'path/to/yy.gif' } - }] - }] - } - }) - }) - }) - - describe('with styles', () => { - it('inline', () => { - expect(compileSnippet(` - <richtext> - <span style="font-size:16px;color:#FF6600;">ABCD</span> - <image style="width:33.33px;height:66.67px" src="path/to/A.png"></image> - </richtext> - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - style: { fontSize: '16px', color: '#FF6600' }, - attr: { value: 'ABCD' } - }, { - type: 'image', - style: { width: '33.33px', height: '66.67px' }, - attr: { src: 'path/to/A.png' } - }] - } - }) - }) - - it('class list', () => { - expect(compileSnippet(` - <richtext> - <image class="icon" src="path/to/A.png"></image> - <span class="title large">ABCD</span> - </richtext> - `, ` - style: { - title: { color: '#FF6600' }, - large: { fontSize: 24 }, - icon: { width: 40, height: 60 } - } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'image', - style: { width: 40, height: 60 }, - attr: { src: 'path/to/A.png' } - }, { - type: 'span', - style: { fontSize: 24, color: '#FF6600' }, - attr: { value: 'ABCD' } - }] - } - }) - }) - }) - - describe('data binding', () => { - it('simple', () => { - expect(compileSnippet(` - <richtext> - <span>{{name}}</span> - </richtext> - `, `data: { name: 'ABCDEFG' }`)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { value: 'ABCDEFG' } - }] - } - }) - }) - - it('nested', () => { - expect(compileSnippet(` - <richtext> - <span>{{a}}</span> - <span>{{b}}<span>{{c.d}}</span></span> - <span>{{e}}</span> - </richtext> - `, ` - data: { a: 'A', b: 'B', c: { d: 'CD' }, e: 'E' } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { value: 'A' } - }, { - type: 'span', - children: [{ - type: 'span', - attr: { value: 'B' } - }, { - type: 'span', - attr: { value: 'CD' } - }] - }, { - type: 'span', - attr: { value: 'E' } - }] - } - }) - }) - - it('update', () => { - expect(compileSnippet(` - <richtext> - <span>{{name}}</span> - </richtext> - `, ` - data: { name: 'default' }, - created: function () { - this.name = 'updated' - } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { value: 'updated' } - }] - } - }) - }) - - it('attribute', () => { - expect(compileSnippet(` - <richtext> - <span :label="label">{{name}}</span> - </richtext> - `, ` - data: { - label: 'uid', - name: '10100' - } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { - label: 'uid', - value: '10100' - } - }] - } - }) - }) - - it('update attribute', () => { - expect(compileSnippet(` - <richtext> - <span :label="label">{{name}}</span> - </richtext> - `, ` - data: { - label: 'name', - name: 'Hanks' - }, - created: function () { - this.label = 'uid'; - this.name = '10100'; - } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - attr: { - label: 'uid', - value: '10100' - } - }] - } - }) - }) - - it('inline style', () => { - expect(compileSnippet(` - <richtext> - <span :style="styleObject">ABCD</span> - <span :style="{ textAlign: align, color: 'red' }">EFGH</span> - </richtext> - `, ` - data: { - styleObject: { fontSize: '32px', color: '#F6F660' }, - align: 'center' - } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - style: { fontSize: '32px', color: '#F6F660' }, - attr: { value: 'ABCD' } - }, { - type: 'span', - style: { textAlign: 'center', color: 'red' }, - attr: { value: 'EFGH' } - }] - } - }) - }) - - it('class list', () => { - expect(compileSnippet(` - <richtext> - <image :class="classList" src="path/to/A.png"></image> - <span :class="['title', size]">ABCD</span> - <span class="large" style="color:#F6F0F4">EFGH</span> - </richtext> - `, ` - style: { - title: { color: '#FF6600' }, - large: { fontSize: 24 }, - icon: { width: 40, height: 60 } - }, - data: { - classList: ['unknown'], - size: 'small' - }, - created: function () { - this.classList = ['icon']; - this.size = 'large'; - } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'image', - style: { width: 40, height: 60 }, - attr: { src: 'path/to/A.png' } - }, { - type: 'span', - style: { fontSize: 24, color: '#FF6600' }, - attr: { value: 'ABCD' } - }, { - type: 'span', - style: { fontSize: 24, color: '#F6F0F4' }, - attr: { value: 'EFGH' } - }] - } - }) - }) - - it('update inline style', () => { - expect(compileSnippet(` - <richtext> - <span :style="styleObject">ABCD</span> - <span :style="{ textAlign: align, color: 'red' }">EFGH</span> - </richtext> - `, ` - data: { - styleObject: { fontSize: '32px', color: '#F6F660' } - }, - created: function () { - this.styleObject = { fontSize: '24px', color: 'blue' } - this.styleObject.color = '#ABCDEF' - this.align = 'left' - } - `)).toEqual({ - type: 'richtext', - attr: { - value: [{ - type: 'span', - style: { fontSize: '24px', color: '#ABCDEF' }, - attr: { value: 'ABCD' } - }, { - type: 'span', - style: { textAlign: 'left', color: 'red' }, - attr: { value: 'EFGH' } - }] - } - }) - }) - }) - - describe('itself', () => { - it('inline styles', () => { - expect(compileSnippet(` - <richtext style="background-color:red"> - <span>empty</span> - </richtext> - `)).toEqual({ - type: 'richtext', - style: { backgroundColor: 'red' }, - attr: { - value: [{ - type: 'span', - attr: { value: 'empty' } - }] - } - }) - }) - - it('class list', () => { - expect(compileSnippet(` - <richtext class="title"> - <span class="large">ABCD</span> - </richtext> - `, ` - style: { - title: { backgroundColor: '#FF6600', height: 200 }, - large: { fontSize: 24 } - } - `)).toEqual({ - type: 'richtext', - style: { backgroundColor: '#FF6600', height: 200 }, - attr: { - value: [{ - type: 'span', - style: { fontSize: 24 }, - attr: { value: 'ABCD' } - }] - } - }) - }) - - it('update styles', () => { - expect(compileSnippet(` - <richtext :class="classList" :style="{ backgroundColor: color }"> - <span class="large">ABCD</span> - </richtext> - `, ` - data: { classList: ['unknow'], color: '#FF6600' }, - style: { - title: { height: 200 }, - large: { fontSize: 24 } - }, - created: function () { - this.classList = ['title'] - } - `)).toEqual({ - type: 'richtext', - style: { backgroundColor: '#FF6600', height: 200 }, - attr: { - value: [{ - type: 'span', - style: { fontSize: 24 }, - attr: { value: 'ABCD' } - }] - } - }) - }) - - it('bind events', (done) => { - const { render, staticRenderFns } = compileAndStringify(` - <div> - <richtext @click="handler"> - <span>Label: {{label}}</span> - </richtext> - </div> - `) - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - el: 'body', - render: ${render}, - staticRenderFns: ${staticRenderFns}, - data: { label: 'AAA' }, - methods: { - handler: function () { - this.label = 'BBB' - } - } - }) - `) - const richtext = instance.document.body.children[0] - fireEvent(instance, richtext.ref, 'click') - setTimeout(() => { - expect(getRoot(instance).children[0]).toEqual({ - type: 'richtext', - event: ['click'], - attr: { - value: [{ - type: 'span', - attr: { value: 'Label: BBB' } - }] - } - }) - done() - }, 0) - }) - - it('v-for', () => { - expect(compileSnippet(` - <div> - <richtext v-for="k in labels"> - <span>{{k}}</span> - </richtext> - </div> - `, ` - data: { - labels: ['A', 'B', 'C'] - } - `)).toEqual({ - type: 'div', - children: [{ - type: 'richtext', - attr: { value: [{ type: 'span', attr: { value: 'A' }}] } - }, { - type: 'richtext', - attr: { value: [{ type: 'span', attr: { value: 'B' }}] } - }, { - type: 'richtext', - attr: { value: [{ type: 'span', attr: { value: 'C' }}] } - }] - }) - }) - }) -}) diff --git a/test/weex/runtime/events.spec.js b/test/weex/runtime/events.spec.js deleted file mode 100644 index 79ddbdb176e..00000000000 --- a/test/weex/runtime/events.spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import { getRoot, fireEvent, compileAndStringify, compileAndExecute } from '../helpers/index' - -describe('generate events', () => { - it('should be bound and fired for native component', (done) => { - compileAndExecute(` - <div @click="foo"> - <text>Hello {{x}}</text> - </div> - `, ` - data: { x: 'World' }, - methods: { - foo: function () { - this.x = 'Weex' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - attr: { value: 'Hello World' } - }] - }) - fireEvent(instance, '_root', 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - event: ['click'], - children: [{ - type: 'text', - attr: { value: 'Hello Weex' } - }] - }) - done() - }) - }) - - it('should be bound and fired by custom component', (done) => { - const { render, staticRenderFns } = compileAndStringify(`<text>Hello {{x}}</text>`) - compileAndExecute(` - <div> - <text>Hello {{x}}</text> - <sub @click="foo" @click.native="bar"></sub> - </div> - `, ` - data: { x: 'World' }, - components: { - sub: { - data: function () { - return { x: 'Sub' } - }, - render: ${render}, - staticRenderFns: ${staticRenderFns}, - created: function () { - this.$emit('click') - } - } - }, - methods: { - foo: function () { - this.x = 'Foo' - }, - bar: function () { - this.x = 'Bar' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - attr: { value: 'Hello Foo' } - }, { - type: 'text', - event: ['click'], - attr: { value: 'Hello Sub' } - }] - }) - fireEvent(instance, instance.document.body.children[1].ref, 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - attr: { value: 'Hello Bar' } - }, { - type: 'text', - event: ['click'], - attr: { value: 'Hello Sub' } - }] - }) - done() - }) - }) -}) diff --git a/test/weex/runtime/framework.spec.js b/test/weex/runtime/framework.spec.js deleted file mode 100644 index 3079c9e0657..00000000000 --- a/test/weex/runtime/framework.spec.js +++ /dev/null @@ -1,302 +0,0 @@ -import * as framework from '../../../packages/weex-vue-framework' -import { getRoot, createInstance } from '../helpers/index' - -describe('framework APIs', () => { - it('createInstance', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []) - ]) - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: 'Hello' }}] - }) - }) - - it('createInstance with config', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: JSON.stringify(this.$getConfig()) }}, []) - ]) - }, - el: "body" - }) - `, { bundleUrl: 'http://example.com/', a: 1, b: 2 }) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - attr: { value: '{"bundleUrl":"http://example.com/","a":1,"b":2,"env":{}}' } - }] - }) - }) - - it('createInstance with external data', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - a: 1, - b: 2 - }, - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: this.a + '-' + this.b }}, []) - ]) - }, - el: "body" - }) - `, undefined, { a: 111 }) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: '111-2' }}] - }) - }) - - it('destroyInstance', (done) => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - x: 'Hello' - }, - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: this.x }}, []) - ]) - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: 'Hello' }}] - }) - instance.$destroy() - setTimeout(() => { - expect(instance.document).toBeUndefined() - expect(instance.app).toBeUndefined() - done() - }, 0) - }) - - it('refreshInstance', (done) => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - x: 'Hello' - }, - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: this.x }}, []) - ]) - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: 'Hello' }}] - }) - instance.$refresh({ x: 'World' }) - setTimeout(() => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: 'World' }}] - }) - instance.$destroy() - const result = instance.$refresh({ x: 'World' }) - expect(result instanceof Error).toBe(true) - done() - }) - }) - - it('getRoot', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - x: 'Hello' - }, - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: this.x }}, []) - ]) - }, - el: "body" - }) - `) - - let root = framework.getRoot(id) - expect(root.ref).toEqual('_root') - expect(root.type).toEqual('div') - expect(root.children.length).toEqual(1) - expect(root.children[0].type).toEqual('text') - expect(root.children[0].attr).toEqual({ value: 'Hello' }) - framework.destroyInstance(instance.id) - - root = framework.getRoot(instance.id) - expect(root instanceof Error).toBe(true) - expect(root).toMatch(/getRoot/) - expect(root).toMatch(/not found/) - }) - - // TODO: deprecated, move to weex-js-runtime - it('receiveTasks: fireEvent', (done) => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - x: 'Hello' - }, - methods: { - update: function (e) { - this.x = 'World' - } - }, - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: this.x }, on: { click: this.update }}, []) - ]) - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - attr: { value: 'Hello' }, - event: ['click'] - }] - }) - const textRef = framework.getRoot(id).children[0].ref - framework.receiveTasks(id, [ - { method: 'fireEvent', args: [textRef, 'click'] } - ]) - setTimeout(() => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - attr: { value: 'World' }, - event: ['click'] - }] - }) - framework.destroyInstance(id) - const result = framework.receiveTasks(id, [ - { method: 'fireEvent', args: [textRef, 'click'] } - ]) - expect(result instanceof Error).toBe(true) - done() - }) - }) - - it('vm.$getConfig', () => { - const id = String(Date.now() * Math.random()) - global.WXEnvironment = { - weexVersion: '0.10.0', - platform: 'Node.js' - } - const instance = createInstance(id, ` - new Vue({ - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: JSON.stringify(this.$getConfig()) }}, []) - ]) - }, - el: "body" - }) - `, { bundleUrl: 'http://whatever.com/x.js' }) - expect(JSON.parse(getRoot(instance).children[0].attr.value)).toEqual({ - bundleUrl: 'http://whatever.com/x.js', - env: { - weexVersion: '0.10.0', - platform: 'Node.js' - } - }) - delete global.WXEnvironment - }) - - it('registering global assets', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - Vue.component('test', { - render (h) { - return h('div', 'Hello') - } - }) - new Vue({ - render (h) { - return h('test') - }, - el: 'body' - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: 'Hello' }}] - }) - }) - - it('adding prototype methods', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - Vue.prototype.$test = () => 'Hello' - const Test = { - render (h) { - return h('div', this.$test()) - } - } - new Vue({ - render (h) { - return h(Test) - }, - el: 'body' - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: 'Hello' }}] - }) - }) - - it('using global mixins', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - Vue.mixin({ - created () { - this.test = true - } - }) - const Test = { - data: () => ({ test: false }), - render (h) { - return h('div', this.test ? 'Hello' : 'nope') - } - } - new Vue({ - data: { test: false }, - render (h) { - return this.test ? h(Test) : h('p') - }, - el: 'body' - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ type: 'text', attr: { value: 'Hello' }}] - }) - }) -}) diff --git a/test/weex/runtime/node.spec.js b/test/weex/runtime/node.spec.js deleted file mode 100644 index 06a20c63a85..00000000000 --- a/test/weex/runtime/node.spec.js +++ /dev/null @@ -1,513 +0,0 @@ -import { - compileAndStringify, - createInstance, - getRoot, - syncPromise, - checkRefresh -} from '../helpers/index' - -describe('node in render function', () => { - it('should be generated', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []) - ]) - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }} - ] - }) - }) - - it('should be generated with all types of text', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []), - 'World', - createElement('text', {}, ['Weex']) - ]) - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'World' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }) - - it('should be generated with comments', () => { - // todo - }) - - it('should be generated with module diff', (done) => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - counter: 0 - }, - methods: { - foo: function () {} - }, - render: function (createElement) { - switch (this.counter) { - case 1: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'World' }}, []) - ]) - - case 2: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'World' }, style: { fontSize: 100 }}, []) - ]) - - case 3: - return createElement('div', {}, [ - createElement('text', { - attrs: { value: 'World' }, - style: { fontSize: 100 }, - on: { click: this.foo } - }, []) - ]) - - case 4: - return createElement('div', {}, [ - createElement('text', { - attrs: { value: 'Weex' }, - style: { color: '#ff0000' } - }, []) - ]) - - default: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []) - ]) - } - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }} - ] - }) - - syncPromise([ - checkRefresh(instance, { counter: 1 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'World' }} - ] - }) - }), - checkRefresh(instance, { counter: 2 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'World' }, style: { fontSize: 100 }} - ] - }) - }), - checkRefresh(instance, { counter: 3 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'World' }, style: { fontSize: 100 }, event: ['click'] } - ] - }) - }), - checkRefresh(instance, { counter: 4 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Weex' }, style: { fontSize: '', color: '#ff0000' }} - ] - }) - done() - }) - ]) - }) - - it('should be generated with sub components', () => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - render: function (createElement) { - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []), - createElement('foo', { props: { x: 'Weex' }}) - ]) - }, - components: { - foo: { - props: { - x: { default: 'World' } - }, - render: function (createElement) { - return createElement('text', { attrs: { value: this.x }}, []) - } - } - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }) - - it('should be generated with if/for diff', (done) => { - const { render, staticRenderFns } = compileAndStringify(` - <div> - <text v-for="item in list" v-if="item.x">{{item.v}}</text> - </div> - `) - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - list: [ - { v: 'Hello', x: true }, - { v: 'World', x: false }, - { v: 'Weex', x: true } - ] - }, - computed: { - x: { - get: function () { return 0 }, - set: function (v) { - switch (v) { - case 1: - this.list[1].x = true - break - case 2: - this.list.push({ v: 'v-if' }) - break - case 3: - this.list.push({ v: 'v-for', x: true }) - break - case 4: - this.list.splice(1, 2) - break - } - } - } - }, - render: ${render}, - staticRenderFns: ${staticRenderFns}, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - - syncPromise([ - checkRefresh(instance, { x: 1 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'World' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }), - checkRefresh(instance, { x: 2 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'World' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }), - checkRefresh(instance, { x: 3 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'World' }}, - { type: 'text', attr: { value: 'Weex' }}, - { type: 'text', attr: { value: 'v-for' }} - ] - }) - }), - checkRefresh(instance, { x: 4 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'v-for' }} - ] - }) - done() - }) - ]) - }) - - it('should be generated with node structure diff', (done) => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - counter: 0 - }, - render: function (createElement) { - switch (this.counter) { - case 1: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []), - createElement('text', { attrs: { value: 'World' }}, []) - ]) - - case 2: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []), - createElement('text', { attrs: { value: 'World' }}, []), - createElement('text', { attrs: { value: 'Weex' }}, []) - ]) - - case 3: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []), - createElement('text', { attrs: { value: 'Weex' }}, []) - ]) - - case 4: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Weex' }}, []) - ]) - - case 5: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []), - createElement('text', { attrs: { value: 'Weex' }}, []) - ]) - - case 6: - return createElement('div', {}, [ - createElement('input', { attrs: { value: 'Hello' }}, []), - createElement('text', { attrs: { value: 'Weex' }}, []) - ]) - - default: - return createElement('div', {}, [ - createElement('text', { attrs: { value: 'Hello' }}, []), - ]) - } - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }} - ] - }) - - syncPromise([ - checkRefresh(instance, { counter: 1 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'World' }} - ] - }) - }), - checkRefresh(instance, { counter: 2 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'World' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }), - checkRefresh(instance, { counter: 3 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }), - checkRefresh(instance, { counter: 4 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }), - checkRefresh(instance, { counter: 5 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - }), - checkRefresh(instance, { counter: 6 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'input', attr: { value: 'Hello' }}, - { type: 'text', attr: { value: 'Weex' }} - ] - }) - done() - }) - ]) - }) - - it('should be generated with component diff', (done) => { - const id = String(Date.now() * Math.random()) - const instance = createInstance(id, ` - new Vue({ - data: { - counter: 0 - }, - components: { - foo: { - props: { a: { default: '1' }, b: { default: '2' }}, - render: function (createElement) { - return createElement('text', { attrs: { value: this.a + '-' + this.b }}, []) - } - }, - bar: { - render: function (createElement) { - return createElement('text', { attrs: { value: 'Bar' }, style: { fontSize: 100 }}) - } - }, - baz: { - render: function (createElement) { - return createElement('image', { attrs: { src: 'http://example.com/favicon.ico' }}) - } - } - }, - render: function (createElement) { - switch (this.counter) { - case 1: - return createElement('div', {}, [ - createElement('foo', { props: { a: '111', b: '222' }}, []) - ]) - - case 2: - return createElement('div', {}, [ - createElement('foo', {}, []) - ]) - - case 3: - return createElement('div', {}, [ - createElement('bar', {}, []) - ]) - - case 4: - return createElement('div', {}, [ - createElement('baz', {}, []) - ]) - - case 5: - return createElement('div', {}, [ - createElement('foo', {}, []), - createElement('bar', {}, []), - createElement('baz', {}, []) - ]) - - default: - return createElement('div', {}, [ - createElement('foo', { props: { a: '111' }}, []) - ]) - } - }, - el: "body" - }) - `) - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: '111-2' }} - ] - }) - - syncPromise([ - checkRefresh(instance, { counter: 1 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: '111-222' }} - ] - }) - }), - checkRefresh(instance, { counter: 2 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: '1-2' }} - ] - }) - }), - checkRefresh(instance, { counter: 3 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: 'Bar' }, style: { fontSize: 100 }} - ] - }) - }), - checkRefresh(instance, { counter: 4 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'image', attr: { src: 'http://example.com/favicon.ico' }} - ] - }) - }), - checkRefresh(instance, { counter: 5 }, result => { - expect(result).toEqual({ - type: 'div', - children: [ - { type: 'text', attr: { value: '1-2' }}, - { type: 'text', attr: { value: 'Bar' }, style: { fontSize: 100 }}, - { type: 'image', attr: { src: 'http://example.com/favicon.ico' }} - ] - }) - done() - }) - ]) - }) -}) diff --git a/test/weex/runtime/style.spec.js b/test/weex/runtime/style.spec.js deleted file mode 100644 index 752f57580bd..00000000000 --- a/test/weex/runtime/style.spec.js +++ /dev/null @@ -1,129 +0,0 @@ -import { getRoot, fireEvent, compileAndExecute } from '../helpers/index' - -describe('generate style', () => { - it('should be generated', () => { - compileAndExecute(` - <div> - <text style="font-size: 100">Hello World</text> - </div> - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [{ - type: 'text', - style: { fontSize: '100' }, - attr: { value: 'Hello World' } - }] - }) - }) - }) - - it('should be generated by array binding', (done) => { - compileAndExecute(` - <div> - <text :style="[x, y]" @click="foo">Hello {{z}}</text> - </div> - `, ` - data: { - x: { fontSize: 100, color: '#00ff00' }, - y: { color: '#ff0000', fontWeight: 'bold' }, - z: 'World' - }, - methods: { - foo: function () { - this.x.fontSize = 200 - this.x.color = '#0000ff' - Vue.delete(this.y, 'fontWeight') - this.z = 'Weex' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { - type: 'text', - event: ['click'], - style: { fontSize: 100, color: '#ff0000', fontWeight: 'bold' }, - attr: { value: 'Hello World' } - } - ] - }) - fireEvent(instance, instance.document.body.children[0].ref, 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { - type: 'text', - event: ['click'], - style: { fontSize: 200, color: '#ff0000', fontWeight: '' }, - attr: { value: 'Hello Weex' } - } - ] - }) - done() - }) - }) - - it('should be generated by map binding', (done) => { - compileAndExecute(` - <div> - <text :style="{ fontSize: x, color: '#00ff00' }" @click="foo">Hello</text> - <text :style="y">{{z}}</text> - </div> - `, ` - data: { - x: 100, - y: { color: '#ff0000', fontWeight: 'bold' }, - z: 'World' - }, - methods: { - foo: function () { - this.x = 200 - this.y.color = '#0000ff' - Vue.delete(this.y, 'fontWeight') - this.z = 'Weex' - } - } - `).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { - type: 'text', - event: ['click'], - style: { fontSize: 100, color: '#00ff00' }, - attr: { value: 'Hello' } - }, - { - type: 'text', - style: { color: '#ff0000', fontWeight: 'bold' }, - attr: { value: 'World' } - } - ] - }) - fireEvent(instance, instance.document.body.children[0].ref, 'click') - return instance - }).then(instance => { - expect(getRoot(instance)).toEqual({ - type: 'div', - children: [ - { - type: 'text', - event: ['click'], - style: { fontSize: 200, color: '#00ff00' }, - attr: { value: 'Hello' } - }, - { - type: 'text', - style: { color: '#0000ff', fontWeight: '' }, - attr: { value: 'Weex' } - } - ] - }) - done() - }) - }) -}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..214cb46c7c0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,38 @@ +{ + "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 new file mode 100644 index 00000000000..cad116b3dae --- /dev/null +++ b/types/built-in-components.d.ts @@ -0,0 +1,63 @@ +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 new file mode 100644 index 00000000000..01d1efbfc7a --- /dev/null +++ b/types/common.d.ts @@ -0,0 +1,21 @@ +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 da58517f42f..779ff922a56 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,10 +1,11 @@ -import { Vue } from "./vue"; +import { Vue } from './vue' +import './umd' +import './jsx' +export * from './jsx' -export default Vue; +export default Vue -export { - CreateElement -} from "./vue"; +export { CreateElement, VueConstructor } from './vue' export { Component, @@ -12,6 +13,7 @@ export { ComponentOptions, FunctionalComponentOptions, RenderContext, + PropType, PropOptions, ComputedOptions, WatchHandler, @@ -19,12 +21,9 @@ export { WatchOptionsWithHandler, DirectiveFunction, DirectiveOptions -} from "./options"; +} from './options' -export { - PluginFunction, - PluginObject -} from "./plugin"; +export { PluginFunction, PluginObject } from './plugin' export { VNodeChildren, @@ -32,5 +31,51 @@ export { VNode, VNodeComponentOptions, VNodeData, - VNodeDirective -} from "./vnode"; + 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' diff --git a/types/jsx.d.ts b/types/jsx.d.ts new file mode 100644 index 00000000000..845cfdcb030 --- /dev/null +++ b/types/jsx.d.ts @@ -0,0 +1,1353 @@ +// 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 c4d822f69ef..458bc9f2b94 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -1,25 +1,79 @@ -import { Vue, CreateElement, CombinedVueInstance } from "./vue"; -import { VNode, VNodeData, VNodeDirective } from "./vnode"; +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' type Constructor = { - new (...args: any[]): any; + new (...args: any[]): any } // we don't support infer props in async component -export type Component<Data=DefaultData<Vue>, Methods=DefaultMethods<Vue>, Computed=DefaultComputed, Props=DefaultProps> = +// 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> - | ThisTypedComponentOptionsWithArrayProps<Vue, Data, Methods, Computed, keyof Props> - | ThisTypedComponentOptionsWithRecordProps<Vue, Data, Methods, Computed, Props>; + | ComponentOptions<never, Data, Methods, Computed, Props, SetupBindings> + | DefineComponent<any, any, any, any, any, any, any, any, any, any, any> -interface EsModuleComponent { - default: Component -} +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>> + +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> -export type AsyncComponent<Data=DefaultData<Vue>, Methods=DefaultMethods<Vue>, Computed=DefaultComputed, Props=DefaultProps> = ( - resolve: (component: Component<Data, Methods, Computed, Props>) => void, +export type AsyncComponentPromise< + Data = DefaultData<never>, + Methods = DefaultMethods<never>, + Computed = DefaultComputed, + Props = DefaultProps, + SetupBindings = {} +> = ( + resolve: ( + component: Component<Data, Methods, Computed, Props, SetupBindings> + ) => void, reject: (reason?: any) => void -) => Promise<Component | EsModuleComponent> | 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 +} /** * When the `Computed` type parameter on `ComponentOptions` is inferred, @@ -31,148 +85,265 @@ export type Accessors<T> = { [K in keyof T]: (() => T[K]) | ComputedOptions<T[K]> } +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> = - object & - ComponentOptions<V, Data | ((this: Readonly<Record<PropNames, any>> & V) => Data), Methods, Computed, PropNames[]> & - ThisType<CombinedVueInstance<V, Data, Methods, Computed, Readonly<Record<PropNames, any>>>>; +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> = - object & - ComponentOptions<V, Data | ((this: Readonly<Props> & V) => Data), Methods, Computed, RecordPropsDefinition<Props>> & - ThisType<CombinedVueInstance<V, Data, Methods, Computed, Readonly<Props>>>; - -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 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>> { - data?: Data; - props?: PropsDef; - propsData?: Object; - computed?: Accessors<Computed>; - methods?: Methods; - watch?: Record<string, WatchOptionsWithHandler<any> | WatchHandler<any> | string>; - - el?: Element | String; - template?: string; - render?(createElement: CreateElement): VNode; - renderError?: (h: () => VNode, 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?(): boolean | void; - - directives?: { [key: string]: DirectiveFunction | DirectiveOptions }; - components?: { [key: string]: Component<any, any, any, any> | AsyncComponent<any, any, any, any> }; - transitions?: { [key: string]: Object }; - filters?: { [key: string]: Function }; - - provide?: Object | (() => Object); - inject?: InjectOptions; + 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; - }; + prop?: string + event?: string + } - parent?: Vue; - mixins?: (ComponentOptions<Vue> | typeof Vue)[]; - name?: 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?: ComponentOptions<Vue> | typeof Vue; - delimiters?: [string, string]; - comments?: boolean; - inheritAttrs?: boolean; + extends?: Extends | ComponentOptions<Vue> | typeof Vue + delimiters?: [string, string] + comments?: boolean + inheritAttrs?: boolean } -export interface FunctionalComponentOptions<Props = DefaultProps, PropDefs = PropsDefinition<Props>> { - name?: string; - props?: PropDefs; - inject?: InjectOptions; - functional: boolean; - render(this: undefined, createElement: CreateElement, context: RenderContext<Props>): VNode; +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; +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: any[]): T & object } +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> | Prop<T> | Prop<T>[]; +export type PropValidator<T> = PropOptions<T> | PropType<T> -export interface PropOptions<T=any> { - type?: Prop<T> | Prop<T>[]; - required?: boolean; - default?: T | null | undefined | (() => object); - validator?(value: T): boolean; +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 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; + get?(): T + set?(value: T): void + cache?: boolean } -export type WatchHandler<T> = (val: T, oldVal: T) => void; +export type WatchHandler<T> = string | ((val: T, oldVal: T) => void) export interface WatchOptions { - deep?: boolean; - immediate?: boolean; + deep?: boolean + immediate?: boolean } export interface WatchOptionsWithHandler<T> extends WatchOptions { - handler: WatchHandler<T>; + handler: WatchHandler<T> } +export interface DirectiveBinding extends Readonly<VNodeDirective> { + readonly modifiers: { [key: string]: boolean } +} + +/** + * @deprecated use {@link FunctionDirective} instead + */ export type DirectiveFunction = ( el: HTMLElement, - binding: VNodeDirective, + binding: DirectiveBinding, vnode: VNode, oldVnode: VNode -) => void; +) => void +/** + * @deprecated use {@link ObjectDirective} instead + */ export interface DirectiveOptions { - bind?: DirectiveFunction; - inserted?: DirectiveFunction; - update?: DirectiveFunction; - componentUpdated?: DirectiveFunction; - unbind?: DirectiveFunction; + bind?: DirectiveFunction + inserted?: DirectiveFunction + update?: DirectiveFunction + componentUpdated?: DirectiveFunction + unbind?: DirectiveFunction } -export type InjectKey = string | symbol; +export type InjectKey = string | symbol -export type InjectOptions = { - [key: string]: InjectKey | { from?: InjectKey, default?: any } -} | string[]; +export type InjectOptions = + | { + [key: string]: InjectKey | { from?: InjectKey; default?: any } + } + | string[] diff --git a/types/plugin.d.ts b/types/plugin.d.ts index 5741f862c55..8ebdfae9057 100644 --- a/types/plugin.d.ts +++ b/types/plugin.d.ts @@ -1,8 +1,8 @@ -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 } diff --git a/types/test/async-component-test.ts b/types/test/async-component-test.ts new file mode 100644 index 00000000000..79978ddf8de --- /dev/null +++ b/types/test/async-component-test.ts @@ -0,0 +1,44 @@ +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 bb77672103e..6e165857893 100644 --- a/types/test/augmentation-test.ts +++ b/types/test/augmentation-test.ts @@ -1,46 +1,53 @@ -import Vue from "../index"; +import Vue, { defineComponent } from '../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; + staticProperty: string + 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"], + props: ['bar'], data: { a: true }, - foo: "foo", + foo: 'foo', methods: { foo() { - this.a = false; + this.a = false } }, computed: { BAR(): string { - return this.bar.toUpperCase(); + return this.bar.toUpperCase() } } -}); +}) -vm.$instanceProperty; -vm.$instanceMethod(); +vm.$instanceProperty +vm.$instanceMethod() -Vue.staticProperty; -Vue.staticMethod(); +Vue.staticProperty +Vue.staticMethod() + +defineComponent({ + mounted() { + this.$instanceMethod + this.$instanceProperty + } +}) diff --git a/types/test/options-test.ts b/types/test/options-test.ts index e7cd8bacdab..8b724083709 100644 --- a/types/test/options-test.ts +++ b/types/test/options-test.ts @@ -1,17 +1,29 @@ -import Vue from "../index"; -import { AsyncComponent, ComponentOptions, FunctionalComponentOptions } from "../index"; -import { CreateElement } from "../vue"; +import Vue, { PropType, VNode } from '../index' +import { ComponentOptions, Component } from '../index' +import { CreateElement } from '../vue' -interface Component extends Vue { - a: number; +interface MyComponent 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(""), + a: Vue.component(''), b: {} } -}); +}) Vue.component('prop-component', { props: { @@ -19,7 +31,7 @@ Vue.component('prop-component', { name: { type: String, default: '0', - required: true, + required: true } }, data() { @@ -28,7 +40,7 @@ Vue.component('prop-component', { capName: this.name.toUpperCase() } } -}); +}) Vue.component('string-prop', { props: ['size', 'name'], @@ -38,34 +50,72 @@ Vue.component('string-prop', { capName: this.name.isany } } -}); +}) class User { - private u: number + private u = 1 } class Cat { - private u: number + 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: { - primitive: [String, Number], - object: [Cat, User], - regex: RegExp, - mixed: [RegExp, Array], - union: [User, Number] as {new(): User | Number}[] // requires annotation + 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.primitive; - this.object; - this.union; - this.regex.compile; - this.mixed; + this.cat + this.complexUnion + this.kittyUser + this.callback(true) + this.union return { - fixedSize: this.union, + 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() { @@ -80,103 +130,128 @@ Vue.component('component', { name: { type: String, default: '0', - required: true, + required: true } }, propsData: { - msg: "Hello" + msg: 'Hello' }, computed: { aDouble(): number { - return this.a * 2; + return this.a * 2 }, aPlus: { get(): number { - return this.a + 1; + return this.a + 1 }, set(v: number) { - this.a = v - 1; + this.a = v - 1 }, cache: false } }, methods: { - plus() { - this.a++; - this.aDouble.toFixed(); - this.aPlus = 1; - this.size.toFixed(); + plus(): void { + this.a++ + this.aDouble.toFixed() + this.aPlus = 1 + this.size.toFixed() } }, 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>", + el: '#app', + template: '<div>{{ message }}</div>', render(createElement) { - return createElement("div", { - attrs: { - id: "foo" - }, - props: { - myProp: "bar" + 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 }, - 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' - }, [ - createElement(), - createElement("div", "message"), - createElement(Vue.component("component")), - createElement({} as ComponentOptions<Vue>), - createElement({ - functional: true, - render(c: CreateElement) { - return createElement() - } - }), + [ + 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(); - }), + createElement(() => Vue.component('component')), + createElement(() => ({} as ComponentOptions<Vue>)), + createElement((resolve, reject) => { + resolve({} as ComponentOptions<Vue>) + reject() + }), - "message", + 'message', - [createElement("div", "message")] - ]); + [createElement('div', 'message')] + ] + ) + }, + renderError(createElement, err) { + return createElement('pre', { style: { color: 'red' } }, err.stack) }, staticRenderFns: [], beforeCreate() { - (this as any).a = 1; + ;(this as any).a = 1 }, created() {}, beforeDestroy() {}, @@ -187,45 +262,62 @@ Vue.component('component', { updated() {}, activated() {}, deactivated() {}, - errorCaptured() { + errorCaptured(err, vm, info) { + err.message + vm.$emit('error') + info.toUpperCase() return true }, + serverPrefetch() { + return Promise.resolve() + }, directives: { a: { bind() {}, inserted() {}, update() {}, - componentMounted() {}, + componentUpdated() {}, unbind() {} }, b(el, binding, vnode, oldVnode) { - el.textContent; - - binding.name; - binding.value; - binding.oldValue; - binding.expression; - binding.arg; - binding.modifiers["modifier"]; + el.textContent + + binding.name + binding.value + binding.oldValue + binding.expression + binding.arg + binding.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: ["${", "}"] -}); + 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: { @@ -236,7 +328,7 @@ Vue.component('provide-inject', { injectBar: Symbol(), injectBaz: { from: 'baz' }, injectQux: { default: 1 }, - injectQuux: { from: 'quuz', default: () => ({ value: 1 })} + injectQuux: { from: 'quuz', default: () => ({ value: 1 }) } } }) @@ -246,8 +338,14 @@ Vue.component('provide-function', { }) }) +Vue.component('component-with-slot', { + render(h): VNode { + return h('div', this.$slots.default) + } +}) + Vue.component('component-with-scoped-slot', { - render (h) { + render(h) { interface ScopedSlotProps { msg: string } @@ -262,34 +360,79 @@ Vue.component('component-with-scoped-slot', { // 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) { + render(this: Vue, h: CreateElement) { + const defaultSlot = this.$scopedSlots['default']!({ msg: 'hi' }) + defaultSlot && + defaultSlot.forEach(vnode => { + vnode.tag + }) return h('div', [ - this.$scopedSlots['default']({ msg: 'hi' }), - this.$scopedSlots['item']({ msg: 'hello' }) + 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; - return createElement("div", {}, context.children); + 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, @@ -298,20 +441,76 @@ Vue.component('functional-component-object-inject', { bar: Symbol(), baz: { from: 'baz' }, qux: { default: 1 }, - quux: { from: 'quuz', default: () => ({ value: 1 })} + quux: { from: 'quuz', default: () => ({ value: 1 }) } }, render(h) { return h('div') } }) -Vue.component("async-component", ((resolve, reject) => { +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) => { setTimeout(() => { - resolve(Vue.component("component")); - }, 0); - return new Promise((resolve) => { - resolve({ functional: true }); + resolve(Vue.component('component')) + }, 0) + return new Promise(resolve => { + resolve({ + functional: true, + render(h: CreateElement) { + return h('div') + } + }) }) -})); +}) + +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 15055614e80..c6c480343f3 100644 --- a/types/test/plugin-test.ts +++ b/types/test/plugin-test.ts @@ -1,20 +1,20 @@ -import Vue from "../index"; -import { PluginFunction, PluginObject } from "../index"; +import Vue from '../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()) +Vue.use(installer, new Option(), new Option(), new Option()) diff --git a/types/test/setup-helpers-test.ts b/types/test/setup-helpers-test.ts new file mode 100644 index 00000000000..4876154b67d --- /dev/null +++ b/types/test/setup-helpers-test.ts @@ -0,0 +1,144 @@ +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/ssr-test.ts b/types/test/ssr-test.ts deleted file mode 100644 index a9377a48506..00000000000 --- a/types/test/ssr-test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import Vue, { VNode, VNodeDirective } from '../index'; -import VueSSRClientPlugin = require('../../packages/vue-server-renderer/client-plugin'); -import VueSSRServerPlugin = require('../../packages/vue-server-renderer/server-plugin'); -import webpack = require('webpack'); -import { readFileSync } from 'fs'; -import { createRenderer, createBundleRenderer } from '../../packages/vue-server-renderer'; - -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.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.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/types/test/tsconfig.json b/types/test/tsconfig.json deleted file mode 100644 index 15809b5f786..00000000000 --- a/types/test/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "es2015" - ], - "module": "commonjs", - "strict": true, - "noEmit": true, - "baseUrl": ".", - "paths": { - "vue": ["../index.d.ts"] - } - }, - "files": [ - "../index.d.ts", - "../options.d.ts", - "../plugin.d.ts", - "../vnode.d.ts", - "../vue.d.ts", - "options-test.ts", - "plugin-test.ts", - "vue-test.ts", - "augmentation-test.ts", - "ssr-test.ts" - ], - "compileOnSave": false -} diff --git a/types/test/umd-test.ts b/types/test/umd-test.ts new file mode 100644 index 00000000000..d8e05b88fbf --- /dev/null +++ b/types/test/umd-test.ts @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000000..48c4fa882df --- /dev/null +++ b/types/test/utils.ts @@ -0,0 +1,14 @@ +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 new file mode 100644 index 00000000000..f4fbe4ba452 --- /dev/null +++ b/types/test/v3/define-async-component-test.tsx @@ -0,0 +1,19 @@ +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 new file mode 100644 index 00000000000..7e6d1968ca3 --- /dev/null +++ b/types/test/v3/define-component-test.tsx @@ -0,0 +1,1227 @@ +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 new file mode 100644 index 00000000000..dd294be724d --- /dev/null +++ b/types/test/v3/inject-test.ts @@ -0,0 +1,16 @@ +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 new file mode 100644 index 00000000000..c357bf8d5c7 --- /dev/null +++ b/types/test/v3/reactivity-test.ts @@ -0,0 +1,398 @@ +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 new file mode 100644 index 00000000000..328941fd0b4 --- /dev/null +++ b/types/test/v3/setup-test.ts @@ -0,0 +1,106 @@ +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 new file mode 100644 index 00000000000..6a9dcce4fc1 --- /dev/null +++ b/types/test/v3/tsx-test.tsx @@ -0,0 +1,69 @@ +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 new file mode 100644 index 00000000000..aeb5ff36c36 --- /dev/null +++ b/types/test/v3/watch-test.ts @@ -0,0 +1,92 @@ +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 031a909b67a..fdf37b52abb 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -1,165 +1,198 @@ -import Vue, { VNode } from "../index"; -import { ComponentOptions } from "../options"; +import Vue, { VNode, defineComponent } from '../index' +import { ComponentOptions } from '../options' class Test extends Vue { - a: number; + 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.$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] } // test property reification - $refs: { - vue: Vue, - element: HTMLInputElement, - vues: Vue[], + $el!: HTMLElement | SVGElement + $refs!: { + vue: Vue + element: HTMLInputElement + vues: Vue[] 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.element.value + this.$refs.vues[0].$data + this.$refs.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.$mount('#app', false) + this.$forceUpdate() + this.$destroy() + this.$set({}, 'key', 'value') + this.$delete({}, 'key') + 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( + () => 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') } static testConfig() { - const { config } = this; - config.silent; - config.optionMergeStrategies; - config.devtools; + const { config } = this + config.silent + config.optionMergeStrategies + config.devtools config.errorHandler = (err, vm) => { if (vm instanceof Test) { - vm.testProperties(); - vm.testMethods(); + vm.testProperties() + vm.testMethods() } - }; + } config.warnHandler = (msg, vm) => { if (vm instanceof Test) { - vm.testProperties(); - vm.testMethods(); + vm.testProperties() + vm.testMethods() } - }; - config.keyCodes = { esc: 27 }; - config.ignoredElements = ['foo', /^ion-/]; + } + config.keyCodes = { esc: 27 } + config.ignoredElements = ['foo', /^ion-/] + config.async = false } static testMethods() { this.extend({ data() { return { - msg: "" - }; + msg: '' + } } - }); - this.nextTick(() => {}); - this.nextTick().then(() => {}); - this.set({}, "", ""); - this.set([true, false, true], 1, true); - this.delete({}, ""); - 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.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({}) } } const HelloWorldComponent = Vue.extend({ - props: ["name"], + props: ['name'], data() { return { - message: "Hello " + this.name, + message: 'Hello ' + this.name } }, computed: { shouted(): string { - return this.message.toUpperCase(); + return this.message.toUpperCase() } }, methods: { getMoreExcited() { - this.message += "!"; + this.message += '!' } }, watch: { message(a: string) { - console.log(`Message ${this.message} was changed!`); + console.log(`Message ${this.message} was changed!`) } } -}); +}) const FunctionalHelloWorldComponent = Vue.extend({ functional: true, - props: ["name"], + props: ['name'], render(createElement, ctxt) { - return createElement("div", "Hello " + ctxt.props.name) + 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()); + console.log(this.greeting.toLowerCase()) } } -}); +}) const GrandChild = Child.extend({ computed: { lower(): string { - return this.greeting.toLowerCase(); + return this.greeting.toLowerCase() } } -}); +}) -new GrandChild().lower.toUpperCase(); -for (let _ in (new Test()).$options) { +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); +declare const options: ComponentOptions<Vue> +Vue.extend(options) +Vue.component('test-comp', options) +new Vue(options) // cyclic example Vue.extend({ @@ -171,12 +204,82 @@ Vue.extend({ methods: { foo() {} }, - mounted () { + mounted() { this.foo() }, // manual annotation - render (h): VNode { + 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 index dc2f0455c71..36c5afee057 100644 --- a/types/tsconfig.json +++ b/types/tsconfig.json @@ -1,11 +1,18 @@ -{ - "compilerOptions": { - "strict": true, - "lib": [ - "es2015", "dom" - ] - }, - "include": [ - "./*.ts" - ] -} \ No newline at end of file +{ + "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 new file mode 100644 index 00000000000..3df7afef21c --- /dev/null +++ b/types/umd.d.ts @@ -0,0 +1,68 @@ +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 new file mode 100644 index 00000000000..e2da34e753f --- /dev/null +++ b/types/v3-component-options.d.ts @@ -0,0 +1,252 @@ +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 new file mode 100644 index 00000000000..f0b2b706082 --- /dev/null +++ b/types/v3-component-props.d.ts @@ -0,0 +1,99 @@ +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 new file mode 100644 index 00000000000..1c55908ac73 --- /dev/null +++ b/types/v3-component-public-instance.d.ts @@ -0,0 +1,232 @@ +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 new file mode 100644 index 00000000000..8648ef6229f --- /dev/null +++ b/types/v3-define-async-component.d.ts @@ -0,0 +1,26 @@ +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 new file mode 100644 index 00000000000..03ef52d1856 --- /dev/null +++ b/types/v3-define-component.d.ts @@ -0,0 +1,201 @@ +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 new file mode 100644 index 00000000000..f6b091f80e9 --- /dev/null +++ b/types/v3-directive.d.ts @@ -0,0 +1,29 @@ +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 new file mode 100644 index 00000000000..8636c11d6b2 --- /dev/null +++ b/types/v3-manual-apis.d.ts @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000000..77b49bed8a6 --- /dev/null +++ b/types/v3-setup-context.d.ts @@ -0,0 +1,41 @@ +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 new file mode 100644 index 00000000000..165605ee51b --- /dev/null +++ b/types/v3-setup-helpers.d.ts @@ -0,0 +1,154 @@ +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 index ae72065f9b8..533e61f169b 100644 --- a/types/vnode.d.ts +++ b/types/vnode.d.ts @@ -1,69 +1,117 @@ -import { Vue } from "./vue"; +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' -export type ScopedSlot = (props: any) => VNodeChildrenArrayContents | string; +/** + * For extending allowed non-declared props on components in TSX + */ +export interface ComponentCustomProps {} -export type VNodeChildren = VNodeChildrenArrayContents | [ScopedSlot] | string; -export interface VNodeChildrenArrayContents { - [x: number]: VNode | string | VNodeChildren; +/** + * 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; - componentOptions?: VNodeComponentOptions; - componentInstance?: Vue; - parent?: VNode; - raw?: boolean; - isStatic?: boolean; - isRootInsert: boolean; - isComment: boolean; + 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?: VNodeChildren; - tag?: string; + 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 }; - ref?: string; - tag?: string; - staticClass?: string; - class?: any; - staticStyle?: { [key: string]: any }; - style?: Object[] | Object; - 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; + 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; + render: Function + staticRenderFns: Function[] + } + directives?: VNodeDirective[] + keepAlive?: boolean } export interface VNodeDirective { - readonly name: string; - readonly value: any; - readonly oldValue: any; - readonly expression: any; - readonly arg: string; - readonly modifiers: { [key: string]: boolean }; + 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 2b025150bc7..f158cf01716 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -3,120 +3,444 @@ import { AsyncComponent, ComponentOptions, FunctionalComponentOptions, - WatchOptionsWithHandler, - WatchHandler, DirectiveOptions, DirectiveFunction, RecordPropsDefinition, ThisTypedComponentOptionsWithArrayProps, ThisTypedComponentOptionsWithRecordProps, - WatchOptions, -} from "./options"; -import { VNode, VNodeData, VNodeChildren, ScopedSlot } from "./vnode"; -import { PluginFunction, PluginObject } from "./plugin"; + 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>, children?: VNodeChildren): VNode; - (tag?: string | Component<any, any, any, any> | AsyncComponent<any, any, any, any>, data?: VNodeData, children?: VNodeChildren): VNode; + ( + 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 } -export interface Vue { - readonly $el: HTMLElement; - readonly $options: ComponentOptions<Vue>; - readonly $parent: Vue; - readonly $root: Vue; - readonly $children: Vue[]; - readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] }; - readonly $slots: { [key: string]: VNode[] }; - readonly $scopedSlots: { [key: string]: ScopedSlot }; - readonly $isServer: boolean; - readonly $data: Record<string, any>; - readonly $props: Record<string, any>; - readonly $ssrContext: any; - readonly $vnode: VNode; - readonly $attrs: Record<string, string>; - readonly $listeners: Record<string, Function | Function[]>; - - $mount(elementOrSelector?: Element | String, hydrating?: boolean): this; - $forceUpdate(): void; - $destroy(): void; - $set: typeof Vue.set; - $delete: typeof Vue.delete; +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[]> + + $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, options?: WatchOptions - ): (() => void); + ): () => 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, callback: Function): this; - $off(event?: string | string[], callback?: Function): this; - $emit(event: string, ...args: any[]): this; - $nextTick(callback: (this: this) => void): void; - $nextTick(): Promise<void>; - $createElement: CreateElement; + ): () => 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> = Data & Methods & Computed & Props & Instance; -export type ExtendedVue<Instance extends Vue, Data, Methods, Computed, Props> = VueConstructor<CombinedVueInstance<Instance, Data, Methods, Computed, Props> & Vue>; +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 <Data = object, Methods = object, Computed = object, PropNames extends string = never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): CombinedVueInstance<V, Data, Methods, Computed, Record<PropNames, any>>; - // ideally, the return type should just contains Props, not Record<keyof Props, any>. But TS requires Base constructors must all have the same return type. - new <Data = object, Methods = object, Computed = object, Props = object>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): CombinedVueInstance<V, Data, Methods, Computed, Record<keyof Props, any>>; - new (options?: ComponentOptions<V>): CombinedVueInstance<V, object, object, object, Record<keyof object, any>>; - - extend<PropNames extends string = never>(definition: FunctionalComponentOptions<Record<PropNames, any>, PropNames[]>): ExtendedVue<V, {}, {}, {}, Record<PropNames, any>>; - extend<Props>(definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>): ExtendedVue<V, {}, {}, {}, Props>; - extend<Data, Methods, Computed, PropNames extends string>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): ExtendedVue<V, Data, Methods, Computed, Record<PropNames, any>>; - extend<Data, Methods, Computed, Props>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>; - extend(options?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}>; - - nextTick(callback: () => void, context?: any[]): void; + /** + * 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, value: T): T; - set<T>(array: T[], key: number, value: T): T; - delete(object: Object, key: string): void; - delete<T>(array: T[], key: number): 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; - filter(id: string, definition?: Function): Function; - - component(id: string): VueConstructor; - component<VC extends VueConstructor>(id: string, constructor: VC): VC; - component<Data, Methods, Computed, Props>(id: string, definition: AsyncComponent<Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>; - 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<Data, Methods, Computed, PropNames extends string>(id: string, definition?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): ExtendedVue<V, Data, Methods, Computed, Record<PropNames, any>>; - component<Data, Methods, Computed, Props>(id: string, definition?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>; - component(id: string, definition?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}>; - - use<T>(plugin: PluginObject<T> | PluginFunction<T>, options?: T): void; - use(plugin: PluginObject<any> | PluginFunction<any>, ...options: any[]): void; - mixin(mixin: VueConstructor | ComponentOptions<Vue>): void; + ): 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)[]; - }; - - config: { - 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[] }; + render(createElement: typeof Vue.prototype.$createElement): VNode + staticRenderFns: (() => VNode)[] } + + observable<T>(obj: T): T + + util: { + warn(msg: string, vm?: InstanceType<VueConstructor>): void + } + + config: VueConfiguration + version: string } -export const Vue: VueConstructor; +export const Vue: VueConstructor diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000000..52e5ea66489 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,29 @@ +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') + } +})