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/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5368b5481bc..3f3302a572e 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](http://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/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..3ccae2540a9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1506 @@ +## [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..3ba66367de6 --- /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/todomvc/app.js b/examples/classic/todomvc/app.js similarity index 100% rename from examples/todomvc/app.js rename to examples/classic/todomvc/app.js diff --git a/examples/classic/todomvc/index.html b/examples/classic/todomvc/index.html new file mode 100644 index 00000000000..504d93597d7 --- /dev/null +++ b/examples/classic/todomvc/index.html @@ -0,0 +1,69 @@ + + + + + Vue.js • TodoMVC + + + + +
+
+

todos

+ +
+
+ + +
    +
  • +
    + + + +
    + +
  • +
+
+
+ + {{ remaining }} {{ remaining | pluralize }} left + + + +
+
+ + + + + + + + diff --git a/examples/todomvc/readme.md b/examples/classic/todomvc/readme.md similarity index 100% rename from examples/todomvc/readme.md rename to examples/classic/todomvc/readme.md 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/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/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..e8750ff7385 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "vue", - "version": "2.6.14", + "version": "2.7.10", + "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.75.6", + "rollup-plugin-typescript2": "^0.31.2", + "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.7.3", + "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..115ae6e775b --- /dev/null +++ b/packages/compiler-sfc/package.json @@ -0,0 +1,34 @@ +{ + "name": "@vue/compiler-sfc", + "version": "2.7.10", + "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.18.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..094120c38f7 --- /dev/null +++ b/packages/compiler-sfc/src/compileScript.ts @@ -0,0 +1,1905 @@ +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 } 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(` + +