diff --git a/.babelrc.js b/.babelrc.js deleted file mode 100644 index 400866bfa7e..00000000000 --- a/.babelrc.js +++ /dev/null @@ -1,23 +0,0 @@ -const babelPresetFlowVue = { - plugins: [ - require('@babel/plugin-proposal-class-properties'), - // require('@babel/plugin-syntax-flow'), // not needed, included in transform-flow-strip-types - require('@babel/plugin-transform-flow-strip-types') - ] -} - -module.exports = { - presets: [ - require('@babel/preset-env'), - // require('babel-preset-flow-vue') - babelPresetFlowVue - ], - plugins: [ - require('babel-plugin-transform-vue-jsx'), - require('@babel/plugin-syntax-dynamic-import') - ], - ignore: [ - 'dist/*.js', - 'packages/**/*.js' - ] -} diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 9075ab237c9..00000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,131 +0,0 @@ -version: 2 - -defaults: &defaults - working_directory: ~/project/vue - docker: - - image: vuejs/ci - -jobs: - install: - <<: *defaults - steps: - - checkout - - restore_cache: - keys: - - v1-vue-{{ .Branch }}-{{ checksum "yarn.lock" }} - - v1-vue-{{ .Branch }}- - - v1-vue- - - run: npm install - - save_cache: - key: v1-vue-{{ .Branch }}-{{ checksum "yarn.lock" }} - 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 - ./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 - - trigger-regression-test: - <<: *defaults - steps: - - run: - command: | - curl --user ${CIRCLE_TOKEN}: \ - --data build_parameters[CIRCLE_JOB]=update \ - --data build_parameters[VUE_REVISION]=${CIRCLE_SHA1} \ - https://circleci.com/api/v1.1/project/github/vuejs/regression-testing/tree/master - -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 - - trigger-regression-test: - filters: - branches: - only: - - "2.6" - - regression-test - requires: - - test-cover - - lint-flow-types - - test-e2e - - test-ssr-weex - weekly_regression_test: - triggers: - - schedule: - # At 13:00 UTC (9:00 EDT) on every Monday - cron: "0 13 * * 1" - filters: - branches: - only: - dev - jobs: - - install - - test-cover: - requires: - - install - - lint-flow-types: - requires: - - install - - test-e2e: - requires: - - install - - test-ssr-weex: - requires: - - install - - trigger-regression-test: - requires: - - test-cover - - lint-flow-types - - test-e2e - - test-ssr-weex 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.js b/.eslintrc.js deleted file mode 100644 index e27aad552a1..00000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - root: true, - parserOptions: { - parser: require.resolve('babel-eslint'), - ecmaVersion: 2018, - sourceType: 'module' - }, - env: { - es6: true, - node: true, - browser: true - }, - plugins: [ - "flowtype" - ], - extends: [ - "eslint:recommended", - "plugin:flowtype/recommended" - ], - globals: { - "__WEEX__": true, - "WXEnvironment": true - }, - rules: { - 'no-console': process.env.NODE_ENV !== 'production' ? 0 : 2, - 'no-useless-escape': 0, - 'no-empty': 0 - } -} diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index 2d6f9a739ea..00000000000 --- a/.flowconfig +++ /dev/null @@ -1,23 +0,0 @@ -[ignore] -.*/node_modules/.* -.*/test/.* -.*/scripts/.* -.*/examples/.* -.*/benchmarks/.* - -[include] - -[libs] -flow - -[options] -unsafe.enable_getters_and_setters=true -module.name_mapper='^compiler/\(.*\)$' -> '/src/compiler/\1' -module.name_mapper='^core/\(.*\)$' -> '/src/core/\1' -module.name_mapper='^shared/\(.*\)$' -> '/src/shared/\1' -module.name_mapper='^web/\(.*\)$' -> '/src/platforms/web/\1' -module.name_mapper='^weex/\(.*\)$' -> '/src/platforms/weex/\1' -module.name_mapper='^server/\(.*\)$' -> '/src/server/\1' -module.name_mapper='^entries/\(.*\)$' -> '/src/entries/\1' -module.name_mapper='^sfc/\(.*\)$' -> '/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 b39f08e2d28..82effcdc869 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -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/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5368b5481bc..e4ecadaea68 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -25,6 +25,7 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before - Make sure `npm test` passes. (see [development setup](#development-setup)) - If adding a new feature: + - Add accompanying test case. - Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it. @@ -35,12 +36,12 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before ## Development Setup -You will need [Node.js](http://nodejs.org) **version 8+**, [Java Runtime Environment](http://www.oracle.com/technetwork/java/javase/downloads/index.html) (for running Selenium server during e2e tests) and [yarn](https://yarnpkg.com/en/docs/install). +You will need [Node.js](https://nodejs.org) **version 12+** and [pnpm](https://pnpm.io/). After cloning the repo, run: -``` bash -$ yarn # install the dependencies of the project +```bash +$ pnpm i # install the dependencies of the project ``` ### Committing Changes @@ -49,17 +50,20 @@ Commit messages should follow the [commit message convention](./COMMIT_CONVENTIO ### 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, including linting/type checking +# run the full test suite, including unit/e2e/type checking $ npm test ``` @@ -79,9 +83,15 @@ The default test script will do the following: lint with ESLint -> type check wi 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/). @@ -111,16 +121,13 @@ 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 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`**: contains 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 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..d06717034a5 --- /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 16 + uses: actions/setup-node@v2 + with: + node-version: 16 + 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 16 + uses: actions/setup-node@v2 + with: + node-version: 16 + 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 16 + uses: actions/setup-node@v2 + with: + node-version: 16 + 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 16 + uses: actions/setup-node@v2 + with: + node-version: 16 + 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/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000000..05577cb11d4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1562 @@ +## [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 ` + + +
+

Latest Vue.js Commits

+ +

vuejs/vue@{{ currentBranch }}

+ +
+ + + 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 @@ + + + + + + Vue.js elastic header example + + + + + + + + + +
+ + + + +
+ + + + 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 @@ + + + + Vue.js firebase + validation example + + + + + + + + + + + +
+
    +
  • + {{user.name}} - {{user.email}} + +
  • +
+
+ + + +
+
    +
  • Name cannot be empty.
  • +
  • Please provide a valid email address.
  • +
+
+ + + 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 @@ + + + + + Vue.js grid component example + + + + + + + + + + +
+ + + +
+ + + + + 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 @@ + + + + + Vue.js markdown editor example + + + + + + + + +
+ +
+
+ + + + + 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 @@ + + + + + Vue.js modal component example + + + + + + + + + +
+ + + + +

custom header

+
+
+ + + + 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 @@ + + + + + Move Animations + + + + + + +
+ + + + + + + +
+ + + + 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 @@ + + + + + Vue.js SVG graph example + + + + + + + + + + + + + +
+ + + + + +
+ + + {{stat.value}} + +
+
+ + +
+
{{ stats }}
+
+ +

* input[type="range"] requires IE10 or above.

+ + + + + 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 @@ + + + + + Vue.js • TodoMVC + + + + +
+
+

todos

+ +
+
+ + +
    +
  • +
    + + + +
    + +
  • +
+
+
+ + {{ remaining }} {{ remaining | pluralize }} left + + + +
+
+ + + + + + + + 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 @@ + + + + + Vue.js tree view example + + + + + + + + + +

(You can double click on an item to turn it into a folder.)

+ + +
    + + +
+ + + + + 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 fbbd0d53034..00000000000 --- a/examples/commits/app.js +++ /dev/null @@ -1,56 +0,0 @@ -/* global Vue */ - -var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=' - -/** - * Actual 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 self = this - if (navigator.userAgent.indexOf('PhantomJS') > -1) { - // use mocks in e2e to avoid dependency on network / authentication - setTimeout(function () { - self.commits = window.MOCKS[self.currentBranch] - }, 0) - } else { - 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/commits/index.html b/examples/commits/index.html deleted file mode 100644 index c1e2e325db6..00000000000 --- a/examples/commits/index.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - Vue.js github commits example - - - - - -
-

Latest Vue.js Commits

- -

vuejs/vue@{{ currentBranch }}

- -
- - - - diff --git a/examples/commits/mock.js b/examples/commits/mock.js deleted file mode 100644 index 0e22a088686..00000000000 --- a/examples/commits/mock.js +++ /dev/null @@ -1,575 +0,0 @@ -window.MOCKS = { - master: [ - { - 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/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 @@ + + +
+

Latest Vue.js Commits

+ +

vuejs/vue@{{ currentBranch }}

+ +
+ + + + 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 @@ + + + + + + + + +
+ + + +
+ + + + 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 @@ + + + + +
+ +
+
+ + + + 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 @@ + + + + + + + + + +
+ + + + + +
+ + + {{stat.value}} + +
+
+ + +
+
{{ stats }}
+
+ + + + 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 @@ + + + +
+
+
+

todos

+ +
+
+ + +
    +
  • +
    + + + +
    + +
  • +
+
+
+ + {{ state.remaining }} + {{ state.remainingText }} + + + + +
+
+
+ + 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 @@ + + + + + + + +

(You can double click on an item to turn it into a folder.)

+ + +
    + +
+ + + + 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 @@ - - - - - - Vue.js elastic header example - - - - - - - - - -
- - - - -
- - - - diff --git a/examples/firebase/index.html b/examples/firebase/index.html deleted file mode 100644 index 7bab1a70b55..00000000000 --- a/examples/firebase/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - Vue.js firebase + validation example - - - - - - - - - - - -
-
    -
  • - {{user.name}} - {{user.email}} - -
  • -
-
- - - -
-
    -
  • Name cannot be empty.
  • -
  • Please provide a valid email address.
  • -
-
- - - 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 @@ - - - - - Vue.js grid component example - - - - - - - - - - -
- - - -
- - - - - 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 @@ - - - - - Vue.js markdown editor example - - - - - - - - -
- -
-
- - - - - diff --git a/examples/modal/index.html b/examples/modal/index.html deleted file mode 100644 index 34b4ebb1d2b..00000000000 --- a/examples/modal/index.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - Vue.js modal component example - - - - - - - - - -
- - - - -

custom header

-
-
- - - - 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 @@ - - - - - Move Animations - - - - - - -
- - - - - - - -
- - - - 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 @@ - - - - - Vue.js SVG graph example - - - - - - - - - - - - - - -
- - - - - -
- - - {{stat.value}} - -
-
- - -
-
{{ stats }}
-
- -

* input[type="range"] requires IE10 or above.

- - - - - 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 @@ - - - - - Vue.js • TodoMVC - - - - - -
-
-

todos

- -
-
- -
    -
  • -
    - - - -
    - -
  • -
-
-
- - {{ remaining }} {{ remaining | pluralize }} left - - - -
-
- - - - - - - - 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 @@ - - - - - Vue.js tree view example - - - - - - - - - -

(You can double click on an item to turn it into a folder.)

- - -
    - - -
- - - - - diff --git a/flow/compiler.js b/flow/compiler.js deleted file mode 100644 index ad09a2e2ebe..00000000000 --- a/flow/compiler.js +++ /dev/null @@ -1,227 +0,0 @@ -declare type CompilerOptions = { - warn?: Function; // allow customizing warning in different environments; e.g. node - modules?: Array; // 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; // 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 - 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, 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 - expectHTML?: boolean; // only false for non-web builds - isFromDOM?: boolean; - shouldDecodeTags?: boolean; - shouldDecodeNewlines?: boolean; - shouldDecodeNewlinesForHref?: boolean; - outputSourceRange?: boolean; - - // runtime user-configurable - delimiters?: [string, string]; // template delimiters - comments?: boolean; // preserve comments in template - - // for ssr optimization compiler - scopeId?: string; -}; - -declare type WarningMessage = { - msg: string; - start?: number; - end?: number; -}; - -declare type CompiledResult = { - ast: ?ASTElement; - render: string; - staticRenderFns: Array; - stringRenderFns?: Array; - errors?: Array; - tips?: Array; -}; - -declare type ModuleOptions = { - // transform an AST node before any attributes are processed - // returning an ASTElement from pre/transforms replaces the element - preTransformNode: (el: ASTElement) => ?ASTElement; - // transform an AST node after built-ins like v-if, v-for are processed - transformNode: (el: ASTElement) => ?ASTElement; - // 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; // AST properties to be considered static -}; - -declare type ASTModifiers = { [key: string]: boolean }; -declare type ASTIfCondition = { exp: ?string; block: ASTElement }; -declare type ASTIfConditions = Array; - -declare type ASTAttr = { - name: string; - value: any; - dynamic?: boolean; - start?: number; - end?: number -}; - -declare type ASTElementHandler = { - value: string; - params?: Array; - modifiers: ?ASTModifiers; - dynamic?: boolean; - start?: number; - end?: number; -}; - -declare type ASTElementHandlers = { - [key: string]: ASTElementHandler | Array; -}; - -declare type ASTDirective = { - name: string; - rawName: string; - value: string; - arg: ?string; - isDynamicArg: boolean; - modifiers: ?ASTModifiers; - start?: number; - end?: number; -}; - -declare type ASTNode = ASTElement | ASTText | ASTExpression; - -declare type ASTElement = { - type: 1; - tag: string; - attrsList: Array; - attrsMap: { [key: string]: any }; - rawAttrsMap: { [key: string]: ASTAttr }; - parent: ASTElement | void; - children: Array; - - start?: number; - end?: number; - - processed?: true; - - static?: boolean; - staticRoot?: boolean; - staticInFor?: boolean; - staticProcessed?: boolean; - hasBindings?: boolean; - - text?: string; - attrs?: Array; - dynamicAttrs?: Array; - props?: Array; - plain?: boolean; - pre?: true; - ns?: string; - - component?: string; - inlineTemplate?: true; - transitionMode?: string | null; - slotName?: ?string; - slotTarget?: ?string; - slotTargetDynamic?: boolean; - slotScope?: ?string; - scopedSlots?: { [name: string]: ASTElement }; - - ref?: string; - refInFor?: boolean; - - if?: string; - ifProcessed?: boolean; - elseif?: string; - else?: true; - ifConditions?: ASTIfConditions; - - for?: string; - forProcessed?: boolean; - key?: string; - alias?: string; - iterator1?: string; - iterator2?: string; - - staticClass?: string; - classBinding?: string; - staticStyle?: string; - styleBinding?: string; - events?: ASTElementHandlers; - nativeEvents?: ASTElementHandlers; - - transition?: string | true; - transitionOnAppear?: boolean; - - model?: { - value: string; - callback: string; - expression: string; - }; - - directives?: Array; - - forbidden?: true; - once?: true; - onceProcessed?: boolean; - wrapData?: (code: string) => string; - wrapListeners?: (code: string) => string; - - // 2.4 ssr optimization - ssrOptimizability?: number; - - // weex specific - appendAsTree?: boolean; -}; - -declare type ASTExpression = { - type: 2; - expression: string; - text: string; - tokens: Array; - static?: boolean; - // 2.4 ssr optimization - ssrOptimizability?: number; - start?: number; - end?: number; -}; - -declare type ASTText = { - type: 3; - text: string; - static?: boolean; - isComment?: boolean; - // 2.4 ssr optimization - ssrOptimizability?: number; - start?: number; - end?: number; -}; - -// SFC-parser related declarations - -// an object format describing a single-file component -declare type SFCDescriptor = { - template: ?SFCBlock; - script: ?SFCBlock; - styles: Array; - customBlocks: Array; - errors: Array; -} - -declare type SFCBlock = { - type: string; - content: string; - attrs: {[attribute:string]: 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 0dc67a48a3e..00000000000 --- a/flow/component.js +++ /dev/null @@ -1,148 +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; - // assets - static directive: (id: string, def?: Function | Object) => Function | Object | void; - static component: (id: string, def?: Class | Object) => Class; - static filter: (id: string, def?: Function) => Function | void; - // functional context constructor - static FunctionalRenderContext: Function; - - // 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; - $refs: { [key: string]: Component | Element | Array | void }; - $slots: { [key: string]: Array }; - $scopedSlots: { [key: string]: () => VNodeChildren }; - $vnode: VNode; // the placeholder node for the component in parent's render tree - $attrs: { [key: string] : string }; - $listeners: { [key: string]: Function | Array }; - $isServer: boolean; - - // public methods - $mount: (el?: Element | string, hydrating?: boolean) => Component; - $forceUpdate: () => void; - $destroy: () => void; - $set: (target: Object | Array, key: string | number, val: T) => T; - $delete: (target: Object | Array, key: string | number) => void; - $watch: (expOrFn: string | Function, cb: Function, options?: Object) => Function; - $on: (event: string | Array, fn: Function) => Component; - $once: (event: string, fn: Function) => Component; - $off: (event?: string | Array, fn?: Function) => Component; - $emit: (event: string, ...args: Array) => Component; - $nextTick: (fn: Function) => void | Promise<*>; - $createElement: (tag?: string | Component, data?: Object, children?: VNodeChildren) => VNode; - - // private properties - _uid: number | string; - _name: string; // this only exists in dev mode - _isVue: true; - _self: Component; - _renderProxy: Component; - _renderContext: ?Component; - _watcher: Watcher; - _watchers: Array; - _computedWatchers: { [key: string]: Watcher }; - _data: Object; - _props: Object; - _events: Object; - _inactive: boolean | null; - _directInactive: boolean; - _isMounted: boolean; - _isDestroyed: boolean; - _isBeingDestroyed: boolean; - _vnode: ?VNode; // self root node - _staticTrees: ?Array; // v-once cached trees - _hasHookEvent: boolean; - _provided: ?Object; - // _virtualComponents?: { [key: string]: Component }; - - // 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, index: number, key: string) => VNode | VNodeChildren; - // toString - _s: (value: mixed) => string; - // text to VNode - _v: (value: string | number) => VNode; - // toNumber - _n: (value: string) => number | string; - // empty vnode - _e: () => VNode; - // loose equal - _q: (a: mixed, b: mixed) => boolean; - // loose indexOf - _i: (arr: Array, val: mixed) => number; - // resolveFilter - _f: (id: string) => Function; - // renderList - _l: (val: mixed, render: Function) => ?Array; - // renderSlot - _t: (name: string, fallback: ?Array, props: ?Object) => ?Array; - // apply v-bind object - _b: (data: any, 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, 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 0ce54fda3f4..00000000000 --- a/flow/global-api.js +++ /dev/null @@ -1,23 +0,0 @@ -declare interface GlobalAPI { - cid: number; - options: Object; - config: Config; - util: Object; - - extend: (options: Object) => Function; - set: (target: Object | Array, key: string | number, value: T) => T; - delete: (target: Object| Array, key: string | number) => void; - nextTick: (fn: Function, context?: Object) => void | Promise<*>; - use: (plugin: Function | Object) => GlobalAPI; - mixin: (mixin: Object) => GlobalAPI; - compile: (template: string) => { render: Function, staticRenderFns: Array }; - - directive: (id: string, def?: Function | Object) => Function | Object | void; - component: (id: string, def?: Class | Object) => Class; - filter: (id: string, def?: Function) => Function | void; - - observable: (value: T) => T; - - // 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 09bd93f5d24..00000000000 --- a/flow/options.js +++ /dev/null @@ -1,90 +0,0 @@ -declare type InternalComponentOptions = { - _isComponent: true; - parent: Component; - _parentVnode: VNode; - render?: Function; - staticRenderFns?: Array -}; - -type InjectKey = string | Symbol; - -declare type ComponentOptions = { - componentId?: string; - - // 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; - serverPrefetch?: Function; - - // assets - directives?: { [key: string]: Object }; - components?: { [key: string]: Class }; - transitions?: { [key: string]: Object }; - filters?: { [key: string]: Function }; - - // context - provide?: { [key: string | Symbol]: any } | () => { [key: string | Symbol]: any }; - inject?: { [key: string]: InjectKey | { from?: InjectKey, default?: any }} | Array; - - // component v-model customization - model?: { - prop?: string; - event?: string; - }; - - // misc - parent?: Component; - mixins?: Array; - name?: string; - extends?: Class | Object; - delimiters?: [string, string]; - comments?: boolean; - inheritAttrs?: boolean; - - // private - _isComponent?: true; - _propKeys?: Array; - _parentVnode?: VNode; - _parentListeners?: ?Object; - _renderChildren?: ?Array; - _componentTag: ?string; - _scopeId: ?string; - _base: Class; -}; - -declare type PropOptions = { - type: Function | Array | 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; - key: string; -}; - -declare type ElementContext = { - type: 'Element'; - children: Array; - rendered: number; - endTag: string; - total: number; -}; - -declare type ComponentContext = { - type: 'Component'; - prevActive: Component; -}; - -declare type RenderState = ComponentContext | ComponentWithCacheContext | ElementContext; diff --git a/flow/vnode.js b/flow/vnode.js deleted file mode 100644 index c9f3b3887d3..00000000000 --- a/flow/vnode.js +++ /dev/null @@ -1,79 +0,0 @@ -declare type VNodeChildren = Array | string; - -declare type VNodeComponentOptions = { - Ctor: Class; - propsData: ?Object; - listeners: ?Object; - children: ?Array; - tag?: string; -}; - -declare type MountedComponentVNode = { - context: Component; - componentOptions: VNodeComponentOptions; - componentInstance: Component; - parent: VNode; - data: VNodeData; -}; - -// interface for vnodes in update modules -declare type VNodeWithData = { - tag: string; - data: VNodeData; - children: ?Array; - text: void; - elm: any; - ns: string | void; - context: Component; - key: string | number | void; - parent?: VNodeWithData; - 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?: string | Array | Object; - normalizedStyle?: Object; - props?: { [key: string]: any }; - attrs?: { [key: string]: string }; - domProps?: { [key: string]: any }; - hook?: { [key: string]: Function }; - on?: ?{ [key: string]: Function | Array }; - nativeOn?: { [key: string]: Function | Array }; - transition?: Object; - show?: boolean; // marker for v-show - inlineTemplate?: { - render: Function; - staticRenderFns: Array; - }; - directives?: Array; - keepAlive?: boolean; - scopedSlots?: { [key: string]: Function }; - model?: { - value: any; - callback: Function; - }; -}; - -declare type VNodeDirective = { - name: string; - rawName: string; - value?: any; - oldValue?: any; - arg?: string; - oldArg?: string; - modifiers?: ASTModifiers; - def?: Object; -}; - -declare type ScopedSlotsData = Array<{ key: string, fn: Function } | ScopedSlotsData>; diff --git a/flow/weex.js b/flow/weex.js deleted file mode 100644 index 6d21a57934a..00000000000 --- a/flow/weex.js +++ /dev/null @@ -1,122 +0,0 @@ -// global flag to be compiled away -declare var __WEEX__: boolean; - -// global object in Weex -declare var WXEnvironment: WeexEnvironment; - -declare type Weex = { - config: WeexConfigAPI; - document: WeexDocument; - requireModule: (name: string) => Object | void; - supports: (condition: string) => boolean | void; - isRegisteredModule: (name: string, method?: string) => boolean; - isRegisteredComponent: (name: string) => boolean; -}; - -declare type WeexConfigAPI = { - bundleUrl: string; // === weex.document.URL - bundleType: string; - env: WeexEnvironment; // === WXEnvironment -}; - -declare type WeexEnvironment = { - platform: string; // could be "Web", "iOS", "Android" - weexVersion: string; // the version of WeexSDK - - osName: string; // could be "iOS", "Android" or others - osVersion: string; - appName: string; // mobile app name or browser name - appVersion: string; - - // information about current running device - deviceModel: string; // phone device model - deviceWidth: number; - deviceHeight: number; - scale: number; - - // only available on the web - userAgent?: string; - dpr?: number; - rem?: number; -}; - -declare interface WeexDocument { - id: string; - URL: string; - taskCenter: WeexTaskCenter; - - open: () => void; - close: () => void; - createElement: (tagName: string, props?: Object) => WeexElement; - createComment: (text: string) => Object; - fireEvent: (type: string) => void; - destroy: () => void; -}; - -declare interface WeexTaskCenter { - instanceId: string; - callbackManager: Object; - send: (type: string, params: Object, args: Array, options?: Object) => void; - registerHook: (componentId: string, type: string, hook: string, fn: Function) => void; - updateData: (componentId: string, data: Object | void, callback?: Function) => void; -}; - -declare interface WeexElement { - nodeType: number; - nodeId: string; - type: string; - ref: string; - text?: string; - - parentNode: WeexElement | void; - children: Array; - previousSibling: WeexElement | void; - nextSibling: WeexElement | void; - - appendChild: (node: WeexElement) => void; - removeChild: (node: WeexElement, preserved?: boolean) => void; - insertBefore: (node: WeexElement, before: WeexElement) => void; - insertAfter: (node: WeexElement, after: WeexElement) => void; - setAttr: (key: string, value: any, silent?: boolean) => void; - setAttrs: (attrs: Object, silent?: boolean) => void; - setStyle: (key: string, value: any, silent?: boolean) => void; - setStyles: (attrs: Object, silent?: boolean) => void; - addEvent: (type: string, handler: Function, args?: Array) => void; - removeEvent: (type: string) => void; - fireEvent: (type: string) => void; - destroy: () => void; -}; - -declare type WeexInstanceOption = { - instanceId: string; - config: WeexConfigAPI; - document: WeexDocument; - Vue?: GlobalAPI; - app?: Component; - data?: Object; -}; - -declare type WeexRuntimeContext = { - weex: Weex; - service: Object; - BroadcastChannel?: Function; -}; - -declare type WeexInstanceContext = { - Vue: GlobalAPI; - - // DEPRECATED - setTimeout?: Function; - clearTimeout?: Function; - setInterval?: Function; - clearInterval?: Function; -}; - -declare type WeexCompilerOptions = CompilerOptions & { - // whether to compile special template for - recyclable?: boolean; -}; - -declare type WeexCompiledResult = CompiledResult & { - '@render'?: string; -}; diff --git a/package.json b/package.json index 6e793fa5a02..9fc45db9652 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "vue", - "version": "2.6.14", + "version": "2.7.14", + "packageManager": "pnpm@7.1.0", "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,51 @@ "files": [ "src", "dist/*.js", - "types/*.d.ts" + "dist/*.mjs", + "types/*.d.ts", + "compiler-sfc", + "packages/compiler-sfc" ], + "exports": { + ".": { + "import": { + "node": "./dist/vue.runtime.mjs", + "default": "./dist/vue.runtime.esm.js" + }, + "require": "./dist/vue.runtime.common.js", + "types": "./types/index.d.ts" + }, + "./compiler-sfc": { + "import": "./compiler-sfc/index.mjs", + "require": "./compiler-sfc/index.js" + }, + "./dist/*": "./dist/*", + "./types/*": "./types/*", + "./package.json": "./package.json" + }, "sideEffects": false, "scripts": { - "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev", - "dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev", - "dev:esm": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm", - "dev:test": "karma start test/unit/karma.dev.config.js", - "dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:web-server-renderer", - "dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:web-compiler ", - "dev:weex": "rollup -w -c scripts/config.js --environment TARGET:weex-framework", - "dev:weex:factory": "rollup -w -c scripts/config.js --environment TARGET:weex-factory", - "dev:weex:compiler": "rollup -w -c scripts/config.js --environment TARGET:weex-compiler ", + "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 -- 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.js", - "test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.js", - "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 scripts test", - "flow": "flow check", - "sauce": "karma start test/unit/karma.sauce.config.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 scripts/release.sh", - "release:weex": "bash scripts/release-weex.sh", - "release:note": "node scripts/gen-release-note.js", - "commit": "git-cz" + "release": "node scripts/release.js", + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" }, "gitHooks": { "pre-commit": "lint-staged", @@ -49,8 +63,10 @@ }, "lint-staged": { "*.js": [ - "eslint --fix", - "git add" + "prettier --write" + ], + "*.ts": [ + "prettier --parser=typescript --write" ] }, "repository": { @@ -66,85 +82,51 @@ "url": "https://github.com/vuejs/vue/issues" }, "homepage": "https://github.com/vuejs/vue#readme", + "dependencies": { + "@vue/compiler-sfc": "workspace:*", + "csstype": "^3.1.0" + }, "devDependencies": { - "@babel/core": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.1.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-transform-flow-strip-types": "^7.0.0", - "@babel/preset-env": "^7.0.0", - "@babel/register": "^7.0.0", - "@types/node": "^12.12.0", - "@types/webpack": "^4.4.22", - "acorn": "^5.2.1", - "babel-eslint": "^10.0.1", - "babel-helper-vue-jsx-merge-props": "^2.0.3", - "babel-loader": "^8.0.4", - "babel-plugin-istanbul": "^5.1.0", - "babel-plugin-transform-vue-jsx": "^4.0.1", - "babel-preset-flow-vue": "^1.0.0", - "buble": "^0.19.3", - "chalk": "^2.3.0", - "chromedriver": "^2.45.0", - "codecov": "^3.0.0", - "commitizen": "^2.9.6", - "conventional-changelog": "^1.1.3", - "cross-spawn": "^6.0.5", - "cz-conventional-changelog": "^2.0.0", - "de-indent": "^1.0.2", - "es6-promise": "^4.1.0", - "escodegen": "^1.8.1", - "eslint": "^5.7.0", - "eslint-plugin-flowtype": "^2.34.0", - "eslint-plugin-jasmine": "^2.8.4", - "file-loader": "^3.0.1", - "flow-bin": "^0.61.0", - "hash-sum": "^1.0.2", - "he": "^1.1.1", - "http-server": "^0.12.3", - "jasmine": "^2.99.0", - "jasmine-core": "^2.99.0", - "karma": "^3.1.1", - "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": "^2.0.2", - "karma-sourcemap-loader": "^0.3.7", - "karma-webpack": "^4.0.0-rc.2", - "lint-staged": "^8.0.0", - "lodash": "^4.17.4", - "lodash.template": "^4.4.0", - "lodash.uniq": "^4.5.0", - "lru-cache": "^5.1.1", - "nightwatch": "^0.9.16", - "nightwatch-helpers": "^1.2.0", - "phantomjs-prebuilt": "^2.1.14", - "puppeteer": "^1.11.0", - "resolve": "^1.3.3", - "rollup": "^1.0.0", - "rollup-plugin-alias": "^1.3.1", - "rollup-plugin-buble": "^0.19.6", - "rollup-plugin-commonjs": "^9.2.0", - "rollup-plugin-flow-no-whitespace": "^1.0.0", - "rollup-plugin-node-resolve": "^4.0.0", - "rollup-plugin-replace": "^2.0.0", - "selenium-server": "^2.53.1", - "serialize-javascript": "^3.1.0", - "shelljs": "^0.8.1", - "terser": "^3.10.2", - "typescript": "^3.6.4", - "webpack": "~4.28.4", - "weex-js-runtime": "^0.23.6", - "weex-styler": "^0.3.0", + "@babel/parser": "^7.18.4", + "@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": "^17.0.41", + "chalk": "^4.1.2", + "conventional-changelog-cli": "^2.2.2", + "cross-spawn": "^7.0.3", + "enquirer": "^2.3.6", + "esbuild": "^0.14.43", + "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.4", + "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": "^0.12.10", "yorkie": "^2.0.0" - }, - "config": { - "commitizen": { - "path": "./node_modules/cz-conventional-changelog" - } } } 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..637c2568fc5 --- /dev/null +++ b/packages/compiler-sfc/package.json @@ -0,0 +1,34 @@ +{ + "name": "@vue/compiler-sfc", + "version": "2.7.14", + "description": "compiler-sfc for Vue 2", + "main": "dist/compiler-sfc.js", + "types": "dist/compiler-sfc.d.ts", + "files": [ + "dist" + ], + "dependencies": { + "@babel/parser": "^7.18.4", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "devDependencies": { + "@babel/types": "^7.19.4", + "@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" + } +} 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 = 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 }, 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 }, 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 }, + child: Identifier, + knownIds: Record +) { + 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:
+ 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..db04dbef333 --- /dev/null +++ b/packages/compiler-sfc/src/compileScript.ts @@ -0,0 +1,1911 @@ +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 ` + + + `) + 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(` + + `) + 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(` + + `) + // 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(` + + `) + assertCode(content) + expect(content).toMatch(`export default { + props: propsModel,`) + }) + + // #4764 + test('defineProps w/ leading code', () => { + const { content } = compile(` + + `) + // 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(` + + `) + 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(` + + `) + 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(` + + `) + assertCode(content) + expect(content).toMatch(`props: ['item'],`) + expect(content).toMatch(`emits: ['a'],`) + }) + + test('defineExpose()', () => { + const { content } = compile(` + + `) + 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(' + + `) + assertCode(content) + }) + + describe(' + + `) + assertCode(content) + }) + + test('script setup first', () => { + const { content } = compile(` + + + `) + assertCode(content) + }) + + test('script setup first, named default export', () => { + const { content } = compile(` + + + `) + assertCode(content) + }) + + // #4395 + test('script setup first, lang="ts", script block content export default', () => { + const { content } = compile(` + + + `) + // 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(` + + + `) + assertCode(content) + }) + + test('with minimal spaces', () => { + const { content } = compile(` + + + `) + assertCode(content) + }) + }) + }) + + describe('imports', () => { + test('should hoist and expose imports', () => { + assertCode( + compile(``).content + ) + }) + + test('should extract comment for import or type declarations', () => { + assertCode( + compile(` + + `).content + ) + }) + + // #2740 + test('should allow defineProps/Emit at the start of imports', () => { + assertCode( + compile(``).content + ) + }) + + test('import dedupe between + + `) + 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(` + + + `) + // 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(` + + + `) + expect(content).toMatch(`return { vMyDir }`) + assertCode(content) + }) + + // https://github.com/vuejs/core/issues/4599 + test('attribute expressions', () => { + const { content } = compile(` + + + `) + expect(content).toMatch(`return { cond, bar, baz }`) + assertCode(content) + }) + + test('vue interpolations', () => { + const { content } = compile(` + + + `) + // 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(` + + + `) + // 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(` + + + `) + expect(content).toMatch(`return { FooBaz, Last }`) + assertCode(content) + }) + + test('TS annotations', () => { + const { content } = compile(` + + + `) + + expect(content).toMatch(`return { a, b, Baz }`) + assertCode(content) + }) + }) + + // describe('inlineTemplate mode', () => { + // test('should work', () => { + // const { content } = compile( + // ` + // + // + // `, + // { 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( + // ` + // + // `, + // { inlineTemplate: true } + // ) + // assertCode(content) + // expect(content).toMatch(`setup(__props, { expose })`) + // expect(content).toMatch(`expose({ count })`) + // }) + + // test('referencing scope components and directives', () => { + // const { content } = compile( + // ` + // + // + // `, + // { 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( + // ` + // + // `, + // { 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( + // ` + // + // `, + // { 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( + // ` + // + // `, + // { 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( + // ` + // + // `, + // { 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( + // ` + // + // `, + // { 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( + // ` + // + // + // + // `, + // { + // 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(` + `) + assertCode(content) + }) + + test('defineProps/Emit w/ runtime options', () => { + const { content } = compile(` + + `) + 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(` + `) + 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(` + + `) + 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(` + + `) + 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(` + + + `) + 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(` + + `) + 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(` + + `) + assertCode(content) + expect(content).toMatch(`x: { type: Number, required: false }`) + expect(bindings).toStrictEqual({ + x: BindingTypes.PROPS + }) + }) + + test('withDefaults (static)', () => { + const { content, bindings } = compile(` + + `) + 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(` + + `) + 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(` + + `) + 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(` + + `) + ).toThrow() + }) + + test('defineEmits w/ type (type literal w/ call signatures)', () => { + const type = `{(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}` + const { content } = compile(` + + `) + assertCode(content) + expect(content).toMatch(`emit: (${type}),`) + expect(content).toMatch(`emits: ["foo", "bar", "baz"]`) + }) + + test('defineEmits w/ type (interface)', () => { + const { content } = compile(` + + `) + 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(` + + `) + 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(` + + `) + 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(` + + `) + 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(` + + `) + 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(` + + `) + assertCode(content) + expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`) + expect(content).toMatch(`emits: ["foo", "bar"]`) + }) + + test('runtime Enum', () => { + const { content, bindings } = compile( + `` + ) + assertCode(content) + expect(bindings).toStrictEqual({ + Foo: BindingTypes.SETUP_CONST + }) + }) + + test('runtime Enum in normal script', () => { + const { content, bindings } = compile( + ` + ` + ) + 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( + `` + ) + assertCode(content) + expect(bindings).toStrictEqual({ + Foo: BindingTypes.SETUP_CONST + }) + }) + + test('import type', () => { + const { content } = compile( + `` + ) + expect(content).toMatch(`return { Baz }`) + assertCode(content) + }) + }) + + describe('errors', () => { + test('`) + ).toThrow(``) + ).toThrow(moduleErrorMsg) + + expect(() => + compile(``) + ).toThrow(moduleErrorMsg) + + expect(() => + compile(``) + ).toThrow(moduleErrorMsg) + }) + + test('defineProps/Emit() w/ both type and non-type args', () => { + expect(() => { + compile(``) + }).toThrow(`cannot accept both type and non-type arguments`) + + expect(() => { + compile(``) + }).toThrow(`cannot accept both type and non-type arguments`) + }) + + test('defineProps/Emit() referencing local var', () => { + expect(() => + compile(``) + ).toThrow(`cannot reference locally declared variables`) + + expect(() => + compile(``) + ).toThrow(`cannot reference locally declared variables`) + + // #4644 + expect(() => + compile(` + + `) + ).not.toThrow(`cannot reference locally declared variables`) + }) + + test('should allow defineProps/Emit() referencing scope var', () => { + assertCode( + compile(``).content + ) + }) + + test('should allow defineProps/Emit() referencing imported binding', () => { + assertCode( + compile(``).content + ) + }) + }) +}) + +describe('SFC analyze + `) + + expect(scriptAst).toBeDefined() + }) + + it('recognizes props array declaration', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + foo: BindingTypes.PROPS, + bar: BindingTypes.PROPS + }) + expect(bindings!.__isScriptSetup).toBe(false) + }) + + it('recognizes props object declaration', () => { + const { bindings } = compile(` + + `) + 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(` + + `) + 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(` + + + `) + expect(bindings).toStrictEqual({ + foo: BindingTypes.SETUP_CONST + }) + }) + + it('recognizes async setup return', () => { + const { bindings } = compile(` + + `) + 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(` + + `) + expect(bindings).toStrictEqual({ + foo: BindingTypes.DATA, + bar: BindingTypes.DATA + }) + }) + + it('recognizes methods', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS }) + }) + + it('recognizes computeds', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + foo: BindingTypes.OPTIONS, + bar: BindingTypes.OPTIONS + }) + }) + + it('recognizes injections array declaration', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + foo: BindingTypes.OPTIONS, + bar: BindingTypes.OPTIONS + }) + }) + + it('recognizes injections object declaration', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + foo: BindingTypes.OPTIONS, + bar: BindingTypes.OPTIONS + }) + }) + + it('works for mixed bindings', () => { + const { bindings } = compile(` + + `) + 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(` + + `) + + 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( + ` + `, + undefined, + { + filename: 'FooBar.vue' + } + ) + expect(content).toMatch(`export default { + __name: 'FooBar'`) + assertCode(content) + }) + + test('do not overwrite manual name (object)', () => { + const { content } = compile( + ` + + `, + 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( + ` + + `, + 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(` + + + `) + }) + + // #12841 + test('should not error when performing ts expression check for v-slot destructured default value', () => { + compile(` + + + `) + }) + }) +}) 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: + '\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: + '\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: + '\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: + '\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 = {}) { + 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 = `

{{ render }}

` + + 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: + '\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 = '\ + \ + ' // + 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 = '\ + \ + ' // + 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: '\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 = ` +
+ + + +
+` + 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 = ` +
+ + + + + + + + + + + + + + + +
+` + 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 = ` +
+ + + + + +
+` + 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 = ` +
+ + + +
+ ` + 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( + `\n` + + `` + ) + expect(content).toMatch(`_useCssVars((_vm, _setup) => ({ + "${mockId}-color": (_vm.color), + "${mockId}-font_size": (_vm.font.size) +})`) + assertCode(content) + }) + + test('w/ normal \n` + + `` + ) + expect(content).toMatch(`_useCssVars((_vm, _setup) => ({ + "${mockId}-size": (_vm.size) +})`) + expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`) + assertCode(content) + }) + + test('w/ \n` + + `` + ) + // 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( + `\n` + + ``, + { 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('\n` + + `` + ).content + ) + }) + + test('\n` + + `` + ).content + ) + }) + + test('\n` + `` + ).content + ) + }) + + test('w/ \n` + + `` + ).content + ) + }) + + //#4185 + test('should ignore comments', () => { + const { content } = compile( + `\n` + + `` + ) + + expect(content).not.toMatch(`"${mockId}-color": (_setup.color)`) + expect(content).toMatch(`"${mockId}-width": (_setup.width)`) + assertCode(content) + }) + + test('w/ \n` + + `` + ) + // 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( + `\n` + + `` + ) + 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: ` + ` + }) + 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( + ` + + + + + + +
+ +
+ ` + ) + expect(res.template!.content.trim()).toBe('
hi
') + 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(` + + `) + + expect(res.template!.content.trim()).toBe('') + }) + + it('should handle nested template', () => { + const res = parseComponent(` + + `) + expect(res.template!.content.trim()).toBe( + '
' + ) + }) + + it('deindent content', () => { + const content = ` + + + + ` + 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
\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
\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
\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 = ` + + + +` + 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( + ` + + ', + state: { a: 1 } + } + + const res = await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + + expect(res).toContain( + `${context.head}${context.styles}` + + `
hi
` + + `` + + `` + ) + }) + + it('renderToString with interpolation', async () => { + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + title: '', + snippet: '
foo
', + head: '', + styles: '', + state: { a: 1 } + } + + const res = await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + + expect(res).toContain( + `` + + // double mustache should be escaped + `<script>hacks</script>` + + `${context.head}${context.styles}` + + `
hi
` + + `` + + // triple should be raw + `
foo
` + + `` + ) + }) + + it('renderToString with interpolation and context.rendered', async () => { + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + title: '', + snippet: '
foo
', + head: '', + styles: '', + state: { a: 0 }, + rendered: context => { + context.state.a = 1 + } + } + + const res = await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + expect(res).toContain( + `` + + // double mustache should be escaped + `<script>hacks</script>` + + `${context.head}${context.styles}` + + `
hi
` + + `` + + // triple should be raw + `
foo
` + + `` + ) + }) + + it('renderToString w/ template function', async () => { + const renderer = createRenderer({ + template: (content, context) => + `${context.head}${content}` + }) + + const context = { + head: '' + } + + const res = await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + + expect(res).toContain( + `${context.head}
hi
` + ) + }) + + it('renderToString w/ template function returning Promise', async () => { + const renderer = createRenderer({ + template: (content, context) => + new Promise(resolve => { + setTimeout(() => { + resolve(`${context.head}${content}`) + }, 0) + }) + }) + + const context = { + head: '' + } + + const res = await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + + expect(res).toContain( + `${context.head}
hi
` + ) + }) + + 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: '' + } + + try { + await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + } catch (err: any) { + expect(err.message).toBe(`foo`) + } + }) + + it('renderToStream', async () => { + const renderer = createRenderer({ + template: defaultTemplate + }) + + const context = { + head: '', + styles: '', + state: { a: 1 } + } + + const res = await new Promise((resolve, reject) => { + const stream = renderer.renderToStream( + new Vue({ + template: '
hi
' + }), + context + ) + + let res = '' + stream.on('data', chunk => { + res += chunk + }) + stream.on('error', reject) + stream.on('end', () => { + resolve(res) + }) + }) + + expect(res).toContain( + `${context.head}${context.styles}` + + `
hi
` + + `` + + `` + ) + }) + + it('renderToStream with interpolation', async () => { + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + title: '', + snippet: '
foo
', + head: '', + styles: '', + state: { a: 1 } + } + + const res = await new Promise((resolve, reject) => { + const stream = renderer.renderToStream( + new Vue({ + template: '
hi
' + }), + context + ) + + let res = '' + stream.on('data', chunk => { + res += chunk + }) + stream.on('error', reject) + stream.on('end', () => { + resolve(res) + }) + }) + + expect(res).toContain( + `` + + // double mustache should be escaped + `<script>hacks</script>` + + `${context.head}${context.styles}` + + `
hi
` + + `` + + // triple should be raw + `
foo
` + + `` + ) + }) + + it('renderToStream with interpolation and context.rendered', async () => { + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + title: '', + snippet: '
foo
', + head: '', + styles: '', + state: { a: 0 }, + rendered: context => { + context.state.a = 1 + } + } + + const res = await new Promise((resolve, reject) => { + const stream = renderer.renderToStream( + new Vue({ + template: '
hi
' + }), + context + ) + + let res = '' + stream.on('data', chunk => { + res += chunk + }) + stream.on('error', reject) + stream.on('end', () => { + resolve(res) + }) + }) + + expect(res).toContain( + `` + + // double mustache should be escaped + `<script>hacks</script>` + + `${context.head}${context.styles}` + + `
hi
` + + `` + + // triple should be raw + `
foo
` + + `` + ) + }) + + it('bundleRenderer + renderToString', async () => { + const renderer = await createWebpackBundleRenderer('app.js', { + asBundle: true, + template: defaultTemplate + }) + const context: any = { + head: '', + styles: '', + state: { a: 1 }, + url: '/test' + } + const res = await renderer.renderToString(context) + expect(res).toContain( + `${context.head}${context.styles}` + + `
/test
` + + `` + + `` + ) + expect(context.msg).toBe('hello') + }) + + it('bundleRenderer + renderToStream', async () => { + const renderer = await createWebpackBundleRenderer('app.js', { + asBundle: true, + template: defaultTemplate + }) + const context: any = { + head: '', + styles: '', + 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( + `${context.head}${context.styles}` + + `
/test
` + + `` + + `` + ) + expect(context.msg).toBe('hello') + }) + + const expectedHTMLWithManifest = (options: any = {}) => + `` + + // used chunks should have preload + `` + + `` + + `` + + `` + + // images and fonts are only preloaded when explicitly asked for + (options.preloadOtherAssets + ? `` + : ``) + + (options.preloadOtherAssets + ? `` + : ``) + + // unused chunks should have prefetch + (options.noPrefetch ? `` : ``) + + // css assets should be loaded + `` + + `` + + `
async test.woff2 test.png
` + + // state should be inlined before scripts + `` + + // manifest chunk should be first + `` + + // async chunks should be before main chunk + `` + + `` + + `` + + 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: + `` + + `{{{ renderResourceHints() }}}{{{ renderStyles() }}}` + + `{{{ renderState({ windowKey: '__FOO__', contextKey: 'foo' }) }}}{{{ renderScripts() }}}` + + ``, + 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 = `${ + context.renderResourceHints() + context.renderStyles() + }${ + res + + context.renderState({ + windowKey: '__FOO__', + contextKey: 'foo' + }) + + context.renderScripts() + }` + + expect(customOutput).toContain( + expectedHTMLWithManifest({ + stateKey: '__FOO__' + }) + ) + }) + + it('whitespace insensitive interpolation', async () => { + const interpolateTemplate = `{{title}}{{{snippet}}}` + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + title: '', + snippet: '
foo
', + head: '', + styles: '', + state: { a: 1 } + } + + const res = await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + expect(res).toContain( + `` + + // double mustache should be escaped + `<script>hacks</script>` + + `${context.head}${context.styles}` + + `
hi
` + + `` + + // triple should be raw + `
foo
` + + `` + ) + }) + + it('renderToString + nonce', async () => { + const interpolateTemplate = `hello` + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + state: { a: 1 }, + nonce: '4AEemGb0xJptoIGFP3Nd' + } + + const res = await renderer.renderToString( + new Vue({ + template: '
hi
' + }), + context + ) + expect(res).toContain( + `` + + `hello` + + `` + + `
hi
` + + `` + + `` + ) + }) + + 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: '
hi
' + }), + context + ) + expect(res).toContain( + `` + ) + }) + } +}) 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/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 + renderToString(vm: Vue, context: object): Promise + + renderToStream(vm: Vue, context?: object): Readable +} + +interface BundleRenderer { + renderToString(callback: RenderCallback): void + renderToString(context: object, callback: RenderCallback): void + renderToString(): Promise + renderToString(context: object): Promise + + 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: `
The visited URL is: {{ url }}
` + }) +} + +// Renderer test +const app = createApp({ url: 'http://localhost:8000/' }) + +const renderer = createRenderer({ + template: readFileSync('./index.template.html', 'utf-8') +}) + +const context = { + title: 'Hello', + meta: ` + + ` +} + +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/vue-template-compiler/README.md b/packages/template-compiler/README.md similarity index 100% rename from packages/vue-template-compiler/README.md rename to packages/template-compiler/README.md 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..7e7d506ed93 --- /dev/null +++ b/packages/template-compiler/package.json @@ -0,0 +1,35 @@ +{ + "name": "vue-template-compiler", + "version": "2.7.14", + "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 + preserveWhitespace?: boolean + whitespace?: 'preserve' | 'condense' + outputSourceRange?: any +} + +interface CompilerOptionsWithSourceRange extends CompilerOptions { + outputSourceRange: true +} + +interface ErrorWithRange { + msg: string + start: number + end: number +} + +interface CompiledResult { + 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 + 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 + + 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)[] + 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 + 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 + +export function compile( + template: string, + options?: CompilerOptions +): CompiledResult + +export function compileToFunctions(template: string): CompiledResultFunctions + +export function ssrCompile( + template: string, + options: CompilerOptionsWithSourceRange +): CompiledResult + +export function ssrCompile( + template: string, + options?: CompilerOptions +): CompiledResult + +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('
hi
', { + 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('
hi
') + +// 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('', { + 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 cd1221a5121..00000000000 --- a/packages/vue-server-renderer/basic.js +++ /dev/null @@ -1,8988 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = global || self, global.renderVueComponentToString = factory()); -}(this, function () { 'use strict'; - - /* */ - - var emptyObject = Object.freeze({}); - - // These helpers produce 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' || - // $flow-disable-line - typeof value === 'symbol' || - 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) - } - - function isPromise (val) { - return ( - isDef(val) && - typeof val.then === 'function' && - typeof val.catch === 'function' - ) - } - - /** - * Convert a value to a string that is actually rendered. - */ - function toString (val) { - return val == null - ? '' - : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) - ? JSON.stringify(val, null, 2) - : String(val) - } - - /** - * Convert an 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 an 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 an 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 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, ctx) { - function boundFn (a) { - var 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, ctx) { - return fn.bind(ctx) - } - - var bind = Function.prototype.bind - ? nativeBind - : polyfillBind; - - /** - * 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 - } - - /* 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/). - */ - function noop (a, b, c) {} - - /** - * Always return false. - */ - var no = function (a, b, c) { return false; }; - - /* eslint-enable no-unused-vars */ - - /** - * Return the same value. - */ - var identity = function (_) { return _; }; - - /** - * Generate a string containing static keys 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 (a instanceof Date && b instanceof Date) { - return a.getTime() === b.getTime() - } 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 - } - } - - /** - * 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. - */ - 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,' + - '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' - ); - - var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; // eslint-disable-line no-control-regex - var isSSRUnsafeAttr = function (name) { - return unsafeAttrCharRE.test(name) - }; - - /* 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 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 - }; - - /* */ - - // 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 isValidContentEditableValue = makeMap('events,caret,typing,plaintext-only'); - - var convertEnumeratedValue = function (key, value) { - return isFalsyAttrValue(value) || value === 'false' - ? 'false' - // allow arbitrary string value for contenteditable - : key === 'contenteditable' && isValidContentEditableValue(value) - ? value - : 'true' - }; - - 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,' + - '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)) { - // 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 (var key in attrs) { - if (isSSRUnsafeAttr(key)) { - continue - } - 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 + "=\"" + (escape(convertEnumeratedValue(key, value))) + "\"") - } 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.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; - }; - - 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) { - var 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 - } - - /* */ - - 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, toString(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]; - } - - /* */ - - /** - * 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 - */ - var 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/; - - /** - * 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 inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; - var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); - 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) || (weexPlatform === 'android'); - var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); - var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - var isPhantomJS = UA && /phantomjs/.test(UA); - var isFF = UA && UA.match(/firefox\/(\d+)/); - - // Firefox has a "watch" function on Object.prototype... - var nativeWatch = ({}).watch; - if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - } - })); // 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 && !inWeex && 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 - 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 = /*@__PURE__*/(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', - 'serverPrefetch' - ]; - - /* */ - - - - var config = ({ - /** - * 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: "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 - */ - // $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 - }); - - /* */ - - 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 (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 '' - } - 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)) + ">") : "") + - (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 only one watcher - // can be evaluated at a time. - Dep.target = null; - var targetStack = []; - - function pushTarget (target) { - targetStack.push(target); - Dep.target = target; - } - - function popTarget () { - targetStack.pop(); - Dep.target = targetStack[targetStack.length - 1]; - } - - /* - * 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); - - var methodsToPatch = [ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' - ]; - - /** - * Intercept mutating methods and emit events - */ - methodsToPatch.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); - - /** - * In some cases we may want to disable observation inside a component's - * update computation. - */ - var shouldObserve = true; - - function toggleObserving (value) { - shouldObserve = value; - } - - /** - * 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. - */ - var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - if (hasProto) { - protoAugment(value, arrayMethods); - } else { - copyAugment(value, arrayMethods, arrayKeys); - } - this.observeArray(value); - } else { - this.walk(value); - } - }; - - /** - * Walk through all properties 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$$1(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 a target Object or Array by intercepting - * the prototype chain using __proto__ - */ - function protoAugment (target, src) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ - } - - /** - * Augment a 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 ( - shouldObserve && - !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$$1 ( - 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; - if ((!getter || setter) && arguments.length === 2) { - val = obj[key]; - } - - 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 (customSetter) { - customSetter(); - } - // #7981: for accessor properties without setter - if (getter && !setter) { return } - 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 (isUndef(target) || isPrimitive(target) - ) { - warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target)))); - } - 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)) { - 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$$1(ob.value, key, val); - ob.dep.notify(); - return val - } - - /** - * 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 = hasSymbol - ? Reflect.ownKeys(from) - : Object.keys(from); - - for (var 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 (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if ( - toVal !== fromVal && - 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, this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm, vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm, vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } - } - - strats.data = function ( - parentVal, - childVal, - vm - ) { - if (!vm) { - if (childVal && typeof childVal !== 'function') { - 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 - ) { - var res = childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal; - return res - ? dedupeHooks(res) - : res - } - - function dedupeHooks (hooks) { - var res = []; - for (var i = 0; i < hooks.length; i++) { - if (res.indexOf(hooks[i]) === -1) { - res.push(hooks[i]); - } - } - return res - } - - 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) { - 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) { - validateComponentName(key); - } - } - - function validateComponentName (name) { - 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, 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; - if (!inject) { return } - 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 { - 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$$1 = dirs[key]; - if (typeof def$$1 === 'function') { - dirs[key] = { bind: def$$1, update: def$$1 }; - } - } - } - } - - 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); - - // 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 (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 (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]; - // boolean casting - var 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 - var 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. - var prevShouldObserve = shouldObserve; - toggleObserving(true); - observe(value); - toggleObserving(prevShouldObserve); - } - { - 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 (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], vm); - expectedTypes.push(assertedType.expectedType || ''); - valid = assertedType.valid; - } - } - - var haveExpectedTypes = expectedTypes.some(function (t) { return t; }); - if (!valid && haveExpectedTypes) { - warn( - getInvalidTypeMessage(name, value, expectedTypes), - 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|BigInt)$/; - - function assertType (value, type, vm) { - 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 { - try { - valid = value instanceof type; - } catch (e) { - warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm); - valid = false; - } - } - return { - valid: valid, - expectedType: expectedType - } - } - - var 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) { - var match = fn && fn.toString().match(functionTypeCheckRE); - return match ? match[1] : '' - } - - function isSameType (a, b) { - return getType(a) === getType(b) - } - - function getTypeIndex (type, expectedTypes) { - if (!Array.isArray(expectedTypes)) { - return isSameType(expectedTypes, type) ? 0 : -1 - } - for (var i = 0, len = expectedTypes.length; i < len; i++) { - if (isSameType(expectedTypes[i], type)) { - return i - } - } - return -1 - } - - function getInvalidTypeMessage (name, value, expectedTypes) { - var message = "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')); - var expectedType = expectedTypes[0]; - var 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) - } - } - - var EXPLICABLE_TYPES = ['string', 'number', 'boolean']; - function isExplicable (value) { - return EXPLICABLE_TYPES.some(function (elem) { return value.toLowerCase() === elem; }) - } - - function isBoolean () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; }) - } - - /* */ - - function handleError (err, vm, info) { - // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. - // See: https://github.com/vuejs/vuex/issues/1505 - pushTarget(); - try { - 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); - } finally { - popTarget(); - } - } - - function invokeWithErrorHandling ( - handler, - context, - args, - vm, - info - ) { - var res; - try { - res = args ? handler.apply(context, args) : handler.call(context); - if (res && !res._isVue && isPromise(res) && !res._handled) { - res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); }); - // issue #9511 - // avoid catch triggering multiple times when nested calls - res._handled = true; - } - } catch (e) { - handleError(e, vm, info); - } - return res - } - - function globalHandleError (err, vm, info) { - logError(err, vm, info); - } - - function logError (err, vm, info) { - { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if ((inBrowser || inWeex) && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } - } - - /* */ - - var callbacks = []; - - function flushCallbacks () { - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } - } - - // The nextTick behavior leverages the microtask queue, which can be accessed - // via either native Promise.then or MutationObserver. - // MutationObserver has wider support, however it is seriously bugged in - // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It - // completely stops working after triggering a few times... so, if native - // Promise is available, we will use it: - /* istanbul ignore next, $flow-disable-line */ - if (typeof Promise !== 'undefined' && isNative(Promise)) ; 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) - var counter = 1; - var observer = new MutationObserver(flushCallbacks); - var textNode = document.createTextNode(String(counter)); - observer.observe(textNode, { - characterData: true - }); - } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) ; - - /* */ - - /* */ - - function genClassForVnode (vnode) { - var data = vnode.data; - var parentNode = vnode; - var childNode = vnode; - while (isDef(childNode.componentInstance)) { - childNode = childNode.componentInstance._vnode; - if (childNode && childNode.data) { - data = mergeClassData(childNode.data, data); - } - } - while (isDef(parentNode = parentNode.parent)) { - if (parentNode && 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 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'); - - /* */ - - /* */ - - function renderClass$1 (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 && 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 += normalizeValue(hyphenatedKey, value[i]); - } - } else { - styleText += normalizeValue(hyphenatedKey, value); - } - } - return styleText - } - - function normalizeValue(key, value) { - if ( - typeof value === 'string' || - (typeof value === 'number' && noUnitNumericStyleProps[key]) || - value === 0 - ) { - return (key + ":" + value + ";") - } else { - // invalid values - return "" - } - } - - function renderStyle (vnode) { - var styleText = genStyle(getStyle(vnode, false)); - if (styleText !== '') { - return (" style=" + (JSON.stringify(escape(styleText)))) - } - } - - var modules = [ - renderAttrs, - renderDOMProps, - renderClass$1, - renderStyle - ]; - - /* */ - - function show (node, dir) { - if (!dir.value) { - var style = node.data.style || (node.data.style = {}); - if (Array.isArray(style)) { - style.push({ display: 'none' }); - } else { - style.display = 'none'; - } - } - } - - /* */ - - // this is only applied for