diff --git a/.circleci/config.yml b/.circleci/config.yml index ab8973c4acf..9075ab237c9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -93,6 +93,7 @@ workflows: filters: branches: only: + - "2.6" - regression-test requires: - test-cover diff --git a/.editorconfig b/.editorconfig index f1cc3ad329c..01a20f16fe3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# http://editorconfig.org +# https://editorconfig.org root = true diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 4ead0e1daf2..b39f08e2d28 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -2,7 +2,7 @@ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. -We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. +We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. diff --git a/.github/COMMIT_CONVENTION.md b/.github/COMMIT_CONVENTION.md index 02e7ad26ab1..381bf17baab 100644 --- a/.github/COMMIT_CONVENTION.md +++ b/.github/COMMIT_CONVENTION.md @@ -58,24 +58,24 @@ The **header** is mandatory and the **scope** of the header is optional. ### Revert -If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted. +If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted. ### Type -If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog. +If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog. Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks. ### Scope -The scope could be anything specifying place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc... +The scope could be anything specifying the place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc... ### Subject -The subject contains succinct description of the change: +The subject contains a succinct description of the change: * use the imperative, present tense: "change" not "changed" nor "changes" -* don't capitalize first letter +* don't capitalize the first letter * no dot (.) at the end ### Body diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 38d9618e4a9..5368b5481bc 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Vue.js Contributing Guide -Hi! I’m really excited that you are interested in contributing to Vue.js. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines. +Hi! I'm really excited that you are interested in contributing to Vue.js. Before submitting your contribution, please make sure to take a moment and read through the following guidelines: - [Code of Conduct](https://github.com/vuejs/vue/blob/dev/.github/CODE_OF_CONDUCT.md) - [Issue Reporting Guidelines](#issue-reporting-guidelines) @@ -14,33 +14,33 @@ Hi! I’m really excited that you are interested in contributing to Vue.js. Befo ## Pull Request Guidelines -- The `master` branch is basically just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.** +- The `master` branch is just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.** - Checkout a topic branch from the relevant branch, e.g. `dev`, and merge back against that branch. - Work in the `src` folder and **DO NOT** checkin `dist` in the commits. -- It's OK to have multiple small commits as you work on the PR - we will let GitHub automatically squash it before merging. +- It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging. - Make sure `npm test` passes. (see [development setup](#development-setup)) -- If adding new feature: +- If adding a new feature: - Add accompanying test case. - - Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it. + - Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it. -- If fixing a bug: - - If you are resolving a special issue, add `(fix #xxxx[,#xxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`. - - Provide detailed description of the bug in the PR. Live demo preferred. +- If fixing bug: + - If you are resolving a special issue, add `(fix #xxxx[,#xxxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`. + - Provide a detailed description of the bug in the PR. Live demo preferred. - Add appropriate test coverage if applicable. ## Development Setup -You will need [Node.js](http://nodejs.org) **version 6+** and [Java Runtime Environment](http://www.oracle.com/technetwork/java/javase/downloads/index.html) (needed for running Selenium server during e2e tests). +You will need [Node.js](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). After cloning the repo, run: ``` bash -$ npm install # or yarn +$ yarn # install the dependencies of the project ``` ### Committing Changes @@ -59,17 +59,17 @@ $ npm run dev:test # build all dist files, including npm packages $ npm run build -# run the full test suite, include linting / type checking +# run the full test suite, including linting/type checking $ npm test ``` There are some other scripts available in the `scripts` section of the `package.json` file. -The default test script will do the following: lint with ESLint -> type check with Flow -> unit tests with coverage -> e2e tests. **Please make sure to have this pass successfully before submitting a PR.** Although the same tests will be run against your PR on the CI server, it is better to have it working locally beforehand. +The default test script will do the following: lint with ESLint -> type check with Flow -> unit tests with coverage -> e2e tests. **Please make sure to have this pass successfully before submitting a PR.** Although the same tests will be run against your PR on the CI server, it is better to have it working locally. ## Project Structure -- **`scripts`**: contains build-related scripts and configuration files. In most cases you don't need to touch them. However, it would be helpful to familiarize yourself with the following files: +- **`scripts`**: contains build-related scripts and configuration files. Usually, you don't need to touch them. However, it would be helpful to familiarize yourself with the following files: - `scripts/alias.js`: module import aliases used across all source code and tests. @@ -85,15 +85,15 @@ The default test script will do the following: lint with ESLint -> type check wi - **`test`**: contains all tests. The unit tests are written with [Jasmine](http://jasmine.github.io/2.3/introduction.html) and run with [Karma](http://karma-runner.github.io/0.13/index.html). The e2e tests are written for and run with [Nightwatch.js](http://nightwatchjs.org/). -- **`src`**: contains the source code, obviously. The codebase is written in ES2015 with [Flow](https://flowtype.org/) type annotations. +- **`src`**: contains the source code. The codebase is written in ES2015 with [Flow](https://flowtype.org/) type annotations. - **`compiler`**: contains code for the template-to-render-function compiler. - The compiler consists of a parser (converts template strings to element ASTs), an optimizer (detects static trees for vdom render optimization), and a code generator (generate render function code from element ASTs). Note the codegen directly generates code strings from the element AST - it's done this way for smaller code size because the compiler is shipped to the browser in the standalone build. + The compiler consists of a parser (converts template strings to element ASTs), an optimizer (detects static trees for vdom render optimization), and a code generator (generate render function code from element ASTs). Note that codegen directly generates code strings from the element AST - it's done this way for smaller code size because the compiler is shipped to the browser in the standalone build. - **`core`**: contains universal, platform-agnostic runtime code. - The Vue 2.0 core is platform-agnostic - which means code inside `core` should be able to run in any JavaScript environment, be it the browser, Node.js, or an embedded JavaScript runtime in native applications. + The Vue 2.0 core is platform-agnostic. That is, the code inside `core` is able to be run in any JavaScript environment, be it the browser, Node.js, or an embedded JavaScript runtime in native applications. - **`observer`**: contains code related to the reactivity system. @@ -101,9 +101,9 @@ The default test script will do the following: lint with ESLint -> type check wi - **`instance`**: contains Vue instance constructor and prototype methods. - - **`global-api`**: as the name suggests. + - **`global-api`**: contains Vue global api. - - **`components`**: universal abstract components. Currently `keep-alive` is the only one. + - **`components`**: contains universal abstract components. - **`server`**: contains code related to server-side rendering. @@ -111,7 +111,7 @@ The default test script will do the following: lint with ESLint -> type check wi Entry files for dist builds are located in their respective platform directory. - Each platform module contains three parts: `compiler`, `runtime` and `server`, corresponding to the three directories above. Each part contains platform-specific modules/utilities which are then imported and injected to the core counterparts in platform-specific entry files. For example, the code implementing the logic behind `v-bind:class` is in `platforms/web/runtime/modules/class.js` - which is imported in `entries/web-runtime.js` and used to create the browser-specific vdom patching function. + Each platform module contains three parts: `compiler`, `runtime` and `server`, corresponding to the three directories above. Each part contains platform-specific modules/utilities which are imported and injected to the core counterparts in platform-specific entry files. For example, the code implementing the logic behind `v-bind:class` is in `platforms/web/runtime/modules/class.js` - which is imported in `entries/web-runtime.js` 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. @@ -119,19 +119,12 @@ The default test script will do the following: lint with ESLint -> type check wi - **`types`**: contains TypeScript type definitions - - **`test`**: type definitions tests + - **`test`**: contains type definitions tests ## Financial Contribution -As a pure community-driven project without major corporate backing, we also welcome financial contributions via Patreon or OpenCollective. - -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou) -- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs) - -### What's the difference between Patreon and OpenCollective? - -Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. +As a pure community-driven project without major corporate backing, we also welcome financial contributions via GitHub Sponsors and OpenCollective. Please consult the [Sponsor Page](https://vuejs.org/sponsor/) for more details. ## Credits diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..652c1192b7b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,8 @@ +# These are supported funding model platforms + +github: [yyx990803, posva] +patreon: evanyou +open_collective: vuejs +ko_fi: # Replace with a single Ko-fi username +tidelift: npm/vue +custom: # Replace with a single custom sponsorship URL diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index cb7f961992c..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,16 +0,0 @@ - - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..675eea91e7c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Create new issue + url: https://new-issue.vuejs.org/ + about: Please use the following link to create a new issue. + - name: Patreon + url: https://www.patreon.com/evanyou + about: Love Vue.js? Please consider supporting us via Patreon. + - name: Open Collective + url: https://opencollective.com/vuejs/donate + about: Love Vue.js? Please consider supporting us via Open Collective. diff --git a/BACKERS.md b/BACKERS.md index 2f493dad4dd..fa66d206698 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -1,442 +1,9 @@

Sponsors & Backers

-Vue.js is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider: - -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou). -- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs). -- [One-time donation via PayPal or crypto-currencies.](https://vuejs.org/support-vuejs/#One-time-Donations) - -#### What's the difference between Patreon and OpenCollective? - -Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. - -

- -

Special Sponsors

- - +Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of the awesome sponsors and backers listed in this file. If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/).

- - + + sponsors

- - - -

Platinum via Patreon

- - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - -
- - - -

Platinum Sponsors (China)

- - - - - - - -
- - - - - - - -
- - -

Platinum via OpenCollective

- - - - -

Gold via Patreon

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - -

Gold via OpenCollective

- - - - - - - - - - - - - -

Silver via Patreon

- -- Matt Mullenweg - - - - - - - - -
- - - -
- - -

Silver via OpenCollective

- - - - - -

Bronze via Patreon

- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - -

Bronze via OpenCollective

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Generous Backers via Patreon ($50+)

- - -- Wasim Khamlichi -- errorrik -- Alex Balashov -- Konstantin Levinski -- Evan Leonardi -- Ernest Sim -- Blaise Laflamme -- Dilettant - - -

Backers via Patreon

- - -- Luca Borghini -- kazuya kawaguchi -- Keisuke KITA -- Santa Cruz -- Simon East -- Benjamin Listwon -- Lars Andreas Ness -- Victor Tolbert -- Stephen Hartley -- Wen-Tien Chang -- Kirk Lewis -- Karol F -- Miljan Aleksic -- 叶解 -- Jake Ingman -- Barbara Liau -- 4 -- Jarek Tkaczyk -- David Hess -- Niannian Modisette -- Ivan Sieder -- Matt Jones -- Duncan J Kenzie -- Mike Margerum -- Guy Gavergun -- Intevation GmbH -- Luiz Eduardo Tanure Bacelar -- Chengzhi Yin -- Zoran Knezevic -- Pierre Vanhulst -- Jon Hobbs-Smith -- Akiho Nagao -- Asaf Yishai -- Estebe Anthony -- Haim Yulzari -- Jeremy Tan -- Jim Raden -- Fille Åström -- Samuel Smith -- Tyler Scott -- Thong Yong Jun -- Tai Shi Lgin -- Matias Verdier -- Jamie McElwain -- Vivekanandhan Natarajan -- Rafael Belvederese -- Mickaël Andrieu -- Guilherme S L de Souza -- Rob Yedlin -- Daniel Waghorn -- Chih-Hsuan, Fan -- Jordan Oroshiba -- Cliff Hess -- Joe Ray Gregory -- RADD Creative -- Rua Cura D'ars -- Richard Simpson -- Alok Pant -- Jessie Hernandez -- Eric Fong -- Aparajita Fishman -- Romain Lienard -- Bohdan Kokotko -- Donald Fischer -- Alexander Weiher -- Shinya Katayama -- Jere Sjöroos -- Wakana Seki -- David Ang -- Dom -- Ben Hong -- David Kaplan -- John Cleveland -- Jaeyoung Lee -- Amor -- Tom Ootes -- Andy Foster -- Joe Cochran -- Matt Sencenbaugh -- The Commission Cafe, LLC. -- Daniel Mattingley -- Teon Ooi -- Hannes Kochniß -- Juan Bermudez -- Alberto T. Payero Mota -- Colt Borg -- Chris Calo -- Milan Zivkovic -- Christo Crampton -- Johnny Austin -- Aaron Hung -- Soichiro Isshiki -- Tom Meagher -- Rob Mellett -- Abraham Arango -- Marko Bošković -- Ed Linklater -- Garion Herman -- Chris Bemister -- Anfrew Willis -- Carlos Adrián -- Timothy J Bass -- Yusuke Kawabata - - -

Backers via OpenCollective

- - diff --git a/README.md b/README.md index 3cb536cfa84..ed68ab8bbbc 100644 --- a/README.md +++ b/README.md @@ -1,228 +1,27 @@

Vue logo

- Build Status - Coverage Status - Downloads - Version - License - Chat -
- Build Status + Build Status + Coverage Status + Downloads + Version + License + Chat

-

Supporting Vue.js

+## This repo is for Vue 2 -Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider: +You are looking at the repository for Vue 2. The repo for Vue 3 is [vuejs/core](https://github.com/vuejs/core). -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou). -- [Become a backer or sponsor on Open Collective](https://opencollective.com/vuejs). -- [One-time donation via PayPal or crypto-currencies.](https://vuejs.org/support-vuejs/#One-time-Donations) +## Sponsors -#### What's the difference between Patreon and OpenCollective? - -Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. - -

Special Sponsors

- +Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/core/blob/main/BACKERS.md). If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/).

- - + + sponsors

- - - -

Platinum Sponsors

- - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - -
- - - -

Platinum Sponsors (China)

- - - - - - - -
- - - - - - - -
- - -

Gold Sponsors

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - -

Sponsors via Open Collective

- -

Platinum

- - - - -

Gold

- - - - - - --- @@ -232,20 +31,20 @@ Vue (pronounced `/vjuː/`, like view) is a **progressive framework** for buildin #### Browser Compatibility -Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). +Vue.js supports all browsers that are [ES5-compliant](https://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). ## Ecosystem -| Project | Status | Description | -|---------|--------|-------------| -| [vue-router] | [![vue-router-status]][vue-router-package] | Single-page application routing | -| [vuex] | [![vuex-status]][vuex-package] | Large-scale state management | -| [vue-cli] | [![vue-cli-status]][vue-cli-package] | Project scaffolding | -| [vue-loader] | [![vue-loader-status]][vue-loader-package] | Single File Component (`*.vue` file) loader for webpack | -| [vue-server-renderer] | [![vue-server-renderer-status]][vue-server-renderer-package] | Server-side rendering support | -| [vue-class-component] | [![vue-class-component-status]][vue-class-component-package] | TypeScript decorator for a class-based API | -| [vue-rx] | [![vue-rx-status]][vue-rx-package] | RxJS integration | -| [vue-devtools] | [![vue-devtools-status]][vue-devtools-package] | Browser DevTools extension | +| Project | Status | Description | +| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | +| [vue-router] | [![vue-router-status]][vue-router-package] | Single-page application routing | +| [vuex] | [![vuex-status]][vuex-package] | Large-scale state management | +| [vue-cli] | [![vue-cli-status]][vue-cli-package] | Project scaffolding | +| [vue-loader] | [![vue-loader-status]][vue-loader-package] | Single File Component (`*.vue` file) loader for webpack | +| [vue-server-renderer] | [![vue-server-renderer-status]][vue-server-renderer-package] | Server-side rendering support | +| [vue-class-component] | [![vue-class-component-status]][vue-class-component-package] | TypeScript decorator for a class-based API | +| [vue-rx] | [![vue-rx-status]][vue-rx-package] | RxJS integration | +| [vue-devtools] | [![vue-devtools-status]][vue-devtools-package] | Browser DevTools extension | [vue-router]: https://github.com/vuejs/vue-router [vuex]: https://github.com/vuejs/vuex @@ -254,8 +53,7 @@ Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/co [vue-server-renderer]: https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer [vue-class-component]: https://github.com/vuejs/vue-class-component [vue-rx]: https://github.com/vuejs/vue-rx -[vue-devtools]: https://github.com/vuejs/vue-devtools - +[vue-devtools]: https://github.com/vuejs/vue-devtools [vue-router-status]: https://img.shields.io/npm/v/vue-router.svg [vuex-status]: https://img.shields.io/npm/v/vuex.svg [vue-cli-status]: https://img.shields.io/npm/v/@vue/cli.svg @@ -264,7 +62,6 @@ Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/co [vue-class-component-status]: https://img.shields.io/npm/v/vue-class-component.svg [vue-rx-status]: https://img.shields.io/npm/v/vue-rx.svg [vue-devtools-status]: https://img.shields.io/chrome-web-store/v/nhdogjmejiglipccpnnnanhbledajbpd.svg - [vue-router-package]: https://npmjs.com/package/vue-router [vuex-package]: https://npmjs.com/package/vuex [vue-cli-package]: https://npmjs.com/package/@vue/cli @@ -280,7 +77,7 @@ To check out [live examples](https://vuejs.org/v2/examples/) and docs, visit [vu ## Questions -For questions and support please use [the official forum](http://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests. +For questions and support please use [the official forum](https://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests. ## Issues @@ -304,9 +101,8 @@ Thank you to all the people who already contributed to Vue! - ## License -[MIT](http://opensource.org/licenses/MIT) +[MIT](https://opensource.org/licenses/MIT) Copyright (c) 2013-present, Yuxi (Evan) You diff --git a/benchmarks/ssr/README.md b/benchmarks/ssr/README.md index 2f0529c1fd8..c61d6daa57d 100644 --- a/benchmarks/ssr/README.md +++ b/benchmarks/ssr/README.md @@ -2,7 +2,7 @@ This benchmark renders a table of 1000 rows with 10 columns (10k components), with around 30k normal elements on the page. Note this is not something likely to be seen in a typical app. This benchmark is mostly for stress/regression testing and comparing between `renderToString` and `renderToStream`. -To view the results follow the run section. Note that the overall completion time for the results are variable, this is due to other system related variants at run time (available memory, processing power, etc). In ideal circumstances both should finish within similar results. +To view the results follow the run section. Note that the overall completion time for the results is variable, this is due to other system related variants at run time (available memory, processing power, etc). In ideal circumstances, both should finish within similar results. `renderToStream` pipes the content through a stream which provides considerable performance benefits (faster time-to-first-byte and non-event-loop-blocking) over `renderToString`. This can be observed through the benchmark. diff --git a/benchmarks/ssr/common.js b/benchmarks/ssr/common.js index 688832f6dda..0411ac896dc 100644 --- a/benchmarks/ssr/common.js +++ b/benchmarks/ssr/common.js @@ -1,6 +1,6 @@ 'use strict' -const self = (global || root) +const self = (global || root) // eslint-disable-line self.performance = { now: function () { @@ -35,11 +35,11 @@ module.exports = { } }, // template: '
123{{ item.id }}
', - template: '
', + template: '
', components: { row: { props: ['row'], - template: '{{ Math.random() }}', + template: '{{ Math.random() }}', components: { column: { template: '' + diff --git a/benchmarks/uptime/index.html b/benchmarks/uptime/index.html index 4a375152cf2..d43c93010a6 100644 --- a/benchmarks/uptime/index.html +++ b/benchmarks/uptime/index.html @@ -38,7 +38,7 @@ .days { display: flex; flex-direction: row; - flex-flow: wrap; + flex-wrap: wrap; } .uptime-day { @@ -82,7 +82,7 @@ diff --git a/examples/commits/mock.js b/examples/commits/mock.js new file mode 100644 index 00000000000..0e22a088686 --- /dev/null +++ b/examples/commits/mock.js @@ -0,0 +1,575 @@ +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/modal/index.html b/examples/modal/index.html index e2121617646..34b4ebb1d2b 100644 --- a/examples/modal/index.html +++ b/examples/modal/index.html @@ -10,7 +10,7 @@ ") + ? ("window." + windowKey + "=" + state + autoRemove + "") : '' }; @@ -8145,7 +9100,7 @@ TemplateRenderer.prototype.renderScripts = function renderScripts (context) { return isJS(file); }); - var needed = [initial[0]].concat(async || [], initial.slice(1)); + var needed = [initial[0]].concat(async, initial.slice(1)); return needed.map(function (ref) { var file = ref.file; @@ -8218,6 +9173,7 @@ function createRenderer (ref) { var shouldPreload = ref.shouldPreload; var shouldPrefetch = ref.shouldPrefetch; var clientManifest = ref.clientManifest; + var serializer = ref.serializer; var render = createRenderFunction(modules, directives, isUnaryTag, cache); var templateRenderer = new TemplateRenderer({ @@ -8225,7 +9181,8 @@ function createRenderer (ref) { inject: inject, shouldPreload: shouldPreload, shouldPrefetch: shouldPrefetch, - clientManifest: clientManifest + clientManifest: clientManifest, + serializer: serializer }); return { @@ -8257,11 +9214,26 @@ function createRenderer (ref) { }, cb); try { render(component, write, context, function (err) { - if (template) { - result = templateRenderer.renderSync(result, context); - } if (err) { - cb(err); + return cb(err) + } + if (context && context.rendered) { + context.rendered(context); + } + if (template) { + try { + var res = templateRenderer.render(result, context); + if (typeof res !== 'string') { + // function template returning promise + res + .then(function (html) { return cb(null, html); }) + .catch(cb); + } else { + cb(null, res); + } + } catch (e) { + cb(e); + } } else { cb(null, result); } @@ -8284,13 +9256,27 @@ function createRenderer (ref) { render(component, write, context, done); }); if (!template) { + if (context && context.rendered) { + var rendered = context.rendered; + renderStream.once('beforeEnd', function () { + rendered(context); + }); + } return renderStream + } else if (typeof template === 'function') { + throw new Error("function template is only supported in renderToString.") } else { var templateStream = templateRenderer.createStream(context); renderStream.on('error', function (err) { templateStream.emit('error', err); }); renderStream.pipe(templateStream); + if (context && context.rendered) { + var rendered$1 = context.rendered; + renderStream.once('beforeEnd', function () { + rendered$1(context); + }); + } return templateStream } } diff --git a/packages/vue-server-renderer/build.prod.js b/packages/vue-server-renderer/build.prod.js new file mode 100644 index 00000000000..01a786d489f --- /dev/null +++ b/packages/vue-server-renderer/build.prod.js @@ -0,0 +1 @@ +"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=(e=require("he"))&&"object"==typeof e&&"default"in e?e.default:e,r=Object.freeze({});function n(e){return null==e}function i(e){return null!=e}function o(e){return!0===e}function a(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function s(e){return null!==e&&"object"==typeof e}var c=Object.prototype.toString;function u(e){return"[object Object]"===c.call(e)}function l(e){return i(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function f(e){return null==e?"":Array.isArray(e)||u(e)&&e.toString===c?JSON.stringify(e,null,2):String(e)}function p(e){var t=parseFloat(e);return isNaN(t)?e:t}function d(e,t){for(var r=Object.create(null),n=e.split(","),i=0;i-1)return e.splice(r,1)}}var y=Object.prototype.hasOwnProperty;function g(e,t){return y.call(e,t)}function b(e){var t=Object.create(null);return function(r){return t[r]||(t[r]=e(r))}}var _=/-(\w)/g,w=b(function(e){return e.replace(_,function(e,t){return t?t.toUpperCase():""})}),x=b(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),S=/\B([A-Z])/g,$=b(function(e){return e.replace(S,"-$1").toLowerCase()});Function.prototype.bind;function A(e,t){for(var r in t)e[r]=t[r];return e}function O(e){for(var t={},r=0;r/="'\u0009\u000a\u000c\u0020]/,L=function(e){return N.test(e)},I=function(e){return E(e)||0===e.indexOf("data-")||0===e.indexOf("aria-")},M={acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},R={"<":"<",">":">",'"':""","&":"&"};function D(e){return e.replace(/[<>"&]/g,U)}function U(e){return R[e]||e}var z={"animation-iteration-count":!0,"border-image-outset":!0,"border-image-slice":!0,"border-image-width":!0,"box-flex":!0,"box-flex-group":!0,"box-ordinal-group":!0,"column-count":!0,columns:!0,flex:!0,"flex-grow":!0,"flex-positive":!0,"flex-shrink":!0,"flex-negative":!0,"flex-order":!0,"grid-row":!0,"grid-row-end":!0,"grid-row-span":!0,"grid-row-start":!0,"grid-column":!0,"grid-column-end":!0,"grid-column-span":!0,"grid-column-start":!0,"font-weight":!0,"line-clamp":!0,"line-height":!0,opacity:!0,order:!0,orphans:!0,"tab-size":!0,widows:!0,"z-index":!0,zoom:!0,"fill-opacity":!0,"flood-opacity":!0,"stop-opacity":!0,"stroke-dasharray":!0,"stroke-dashoffset":!0,"stroke-miterlimit":!0,"stroke-opacity":!0,"stroke-width":!0},B=d("input,textarea,option,select,progress"),q=d("contenteditable,draggable,spellcheck"),J=d("events,caret,typing,plaintext-only"),H=function(e,t){return V(t)||"false"===t?"false":"contenteditable"===e&&J(t)?t:"true"},K=d("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),V=function(e){return null==e||!1===e};function W(e,t){if(K(e)){if(!V(t))return" "+e+'="'+e+'"'}else{if(q(e))return" "+e+'="'+D(H(e,t))+'"';if(!V(t))return" "+e+'="'+D(String(t))+'"'}return""}var Z=function(e,t,r,n,i,o,a,s){this.tag=e,this.data=t,this.children=r,this.text=n,this.elm=i,this.ns=void 0,this.context=o,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=t&&t.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1},X={child:{configurable:!0}};X.child.get=function(){return this.componentInstance},Object.defineProperties(Z.prototype,X);var G=function(e){void 0===e&&(e="");var t=new Z;return t.text=e,t.isComment=!0,t};function Q(e){return new Z(void 0,void 0,void 0,String(e))}function Y(e,t,r){var n=new Z(void 0,void 0,void 0,t);n.raw=r,e.children=[n]}function ee(e,t,r,n){Object.defineProperty(e,t,{value:r,enumerable:!!n,writable:!0,configurable:!0})}var te,re="__proto__"in{},ne="undefined"!=typeof window,ie="undefined"!=typeof WXEnvironment&&!!WXEnvironment.platform,oe=ie&&WXEnvironment.platform.toLowerCase(),ae=ne&&window.navigator.userAgent.toLowerCase(),se=ae&&/msie|trident/.test(ae),ce=(ae&&ae.indexOf("msie 9.0"),ae&&ae.indexOf("edge/")>0),ue=(ae&&ae.indexOf("android"),ae&&/iphone|ipad|ipod|ios/.test(ae),ae&&/chrome\/\d+/.test(ae),ae&&/phantomjs/.test(ae),ae&&ae.match(/firefox\/(\d+)/),{}.watch);if(ne)try{var le={};Object.defineProperty(le,"passive",{get:function(){}}),window.addEventListener("test-passive",null,le)}catch(e){}var fe=function(){return void 0===te&&(te=!ne&&!ie&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),te};ne&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function pe(e){return"function"==typeof e&&/native code/.test(e.toString())}var de,ve="undefined"!=typeof Symbol&&pe(Symbol)&&"undefined"!=typeof Reflect&&pe(Reflect.ownKeys);de="undefined"!=typeof Set&&pe(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var he="data-server-rendered",me=["beforeCreate","created","beforeMount","mounted","beforeUpdate","updated","beforeDestroy","destroyed","activated","deactivated","errorCaptured","serverPrefetch"],ye={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:C,isReservedAttr:C,isUnknownElement:C,getTagNamespace:k,parsePlatformTagName:T,mustUseProp:C,async:!0,_lifecycleHooks:me},ge=k,be=0,_e=function(){this.id=be++,this.subs=[]};_e.prototype.addSub=function(e){this.subs.push(e)},_e.prototype.removeSub=function(e){m(this.subs,e)},_e.prototype.depend=function(){_e.target&&_e.target.addDep(this)},_e.prototype.notify=function(){for(var e=this.subs.slice(),t=0,r=e.length;t=0&&Math.floor(t)===t&&isFinite(e)}(t))return e.length=Math.max(e.length,t),e.splice(t,1,r),r;if(t in e&&!(t in Object.prototype))return e[t]=r,r;var n=e.__ob__;return e._isVue||n&&n.vmCount?r:n?(je(n.value,t,r),n.dep.notify(),r):(e[t]=r,r)}Te.prototype.walk=function(e){for(var t=Object.keys(e),r=0;r-1)if(o&&!g(i,"default"))a=!1;else if(""===a||a===$(e)){var c=He(String,i.type);(c<0||s1&&(t[n[0].trim()]=n[1].trim())}}),t});function ot(e){var t=at(e.style);return e.staticStyle?A(e.staticStyle,t):t}function at(e){return Array.isArray(e)?O(e):"string"==typeof e?it(e):e}function st(e){var t="";for(var r in e){var n=e[r],i=$(r);if(Array.isArray(n))for(var o=0,a=n.length;o-1&&ft(a);else if(F(r,lt(a)))return void ft(a)}}},dt=d("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),vt=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),ht=d("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),mt=800,yt=function(e){return e},gt="undefined"!=typeof process&&process.nextTick?process.nextTick:"undefined"!=typeof Promise?function(e){return Promise.resolve().then(e)}:"undefined"!=typeof setTimeout?setTimeout:yt;if(gt===yt)throw new Error("Your JavaScript runtime does not support any asynchronous primitives that are required by vue-server-renderer. Please use a polyfill for either Promise or setTimeout.");function bt(e,t){var r=0,n=function(i,o){i&&n.caching&&(n.cacheBuffer[n.cacheBuffer.length-1]+=i),!0!==e(i,o)&&(r>=mt?gt(function(){try{o()}catch(e){t(e)}}):(r++,o(),r--))};return n.caching=!1,n.cacheBuffer=[],n.componentBuffer=[],n}var _t=function(e){function t(t){var r=this;e.call(this),this.buffer="",this.render=t,this.expectedSize=0,this.write=bt(function(e,t){var n=r.expectedSize;return r.buffer+=e,r.buffer.length>=n&&(r.next=t,r.pushBySize(n),!0)},function(e){r.emit("error",e)}),this.end=function(){r.emit("beforeEnd"),r.done=!0,r.push(r.buffer)}}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.pushBySize=function(e){var t=this.buffer.substring(0,e);this.buffer=this.buffer.substring(e),this.push(t)},t.prototype.tryRender=function(){try{this.render(this.write,this.end)}catch(e){this.emit("error",e)}},t.prototype.tryNext=function(){try{this.next()}catch(e){this.emit("error",e)}},t.prototype._read=function(e){this.expectedSize=e,o(this.done)?this.push(null):this.buffer.length>=e?this.pushBySize(e):n(this.next)?this.tryRender():this.tryNext()},t}(require("stream").Readable),wt=function(e){this.userContext=e.userContext,this.activeInstance=e.activeInstance,this.renderStates=[],this.write=e.write,this.done=e.done,this.renderNode=e.renderNode,this.isUnaryTag=e.isUnaryTag,this.modules=e.modules,this.directives=e.directives;var t=e.cache;if(t&&(!t.get||!t.set))throw new Error("renderer cache must implement at least get & set.");this.cache=t,this.get=t&&xt(t,"get"),this.has=t&&xt(t,"has"),this.next=this.next.bind(this)};function xt(e,t){var r=e[t];return n(r)?void 0:r.length>1?function(t,n){return r.call(e,t,n)}:function(t,n){return n(r.call(e,t))}}wt.prototype.next=function(){for(;;){var e=this.renderStates[this.renderStates.length-1];if(n(e))return this.done();switch(e.type){case"Element":case"Fragment":var t=e.children,r=e.total,i=e.rendered++;if(i=0&&" "===(h=e.charAt(v));v--);h&&St.test(h)||(u=!0)}}else void 0===i?(d=n+1,i=e.slice(0,n).trim()):m();function m(){(o||(o=[])).push(e.slice(d,n).trim()),d=n+1}if(void 0===i?i=e.slice(0,n).trim():0!==d&&m(),o)for(n=0;n\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Xt=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Gt="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+/a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/.source+"]*",Qt="((?:"+Gt+"\\:)?"+Gt+")",Yt=new RegExp("^<"+Qt),er=/^\s*(\/?)>/,tr=new RegExp("^<\\/"+Qt+"[^>]*>"),rr=/^]+>/i,nr=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},cr=/&(?:lt|gt|quot|amp|#39);/g,ur=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,lr=d("pre,textarea",!0),fr=function(e,t){return e&&lr(e)&&"\n"===t[0]};function pr(e,t){var r=t?ur:cr;return e.replace(r,function(e){return sr[e]})}function dr(e,t,r){var n=r||{},i=n.number,o="$$v";n.trim&&(o="(typeof $$v === 'string'? $$v.trim(): $$v)"),i&&(o="_n("+o+")");var a=vr(t,o);e.model={value:"("+t+")",expression:JSON.stringify(t),callback:"function ($$v) {"+a+"}"}}function vr(e,t){var r=function(e){if(e=e.trim(),Bt=e.length,e.indexOf("[")<0||e.lastIndexOf("]")-1?{exp:e.slice(0,Ht),key:'"'+e.slice(Ht+1)+'"'}:{exp:e,key:null};qt=e,Ht=Kt=Vt=0;for(;!mr();)yr(Jt=hr())?br(Jt):91===Jt&&gr(Jt);return{exp:e.slice(0,Kt),key:e.slice(Kt+1,Vt)}}(e);return null===r.key?e+"="+t:"$set("+r.exp+", "+r.key+", "+t+")"}function hr(){return qt.charCodeAt(++Ht)}function mr(){return Ht>=Bt}function yr(e){return 34===e||39===e}function gr(e){var t=1;for(Kt=Ht;!mr();)if(yr(e=hr()))br(e);else if(91===e&&t++,93===e&&t--,0===t){Vt=Ht;break}}function br(e){for(var t=e;!mr()&&(e=hr())!==t;);}var _r,wr,xr,Sr,$r,Ar,Or,kr,Cr=/^@|^v-on:/,Tr=/^v-|^@|^:|^#/,Fr=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,jr=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Pr=/^\(|\)$/g,Er=/^\[.*\]$/,Nr=/:(.*)$/,Lr=/^:|^\.|^v-bind:/,Ir=/\.[^.\]]+(?=[^\]]*$)/g,Mr=/^v-slot(:|$)|^#/,Rr=/[\r\n]/,Dr=/[ \f\t\r\n]+/g,Ur=b(t.decode),zr="_empty_";function Br(e,t,r){return{type:1,tag:e,attrsList:t,attrsMap:Zr(t),rawAttrsMap:{},parent:r,children:[]}}function qr(e,t){_r=t.warn||Tt,Ar=t.isPreTag||C,Or=t.mustUseProp||C,kr=t.getTagNamespace||C;t.isReservedTag;xr=Ft(t.modules,"transformNode"),Sr=Ft(t.modules,"preTransformNode"),$r=Ft(t.modules,"postTransformNode"),wr=t.delimiters;var r,n,i=[],o=!1!==t.preserveWhitespace,a=t.whitespace,s=!1,c=!1;function u(e){if(l(e),s||e.processed||(e=Jr(e,t)),i.length||e===r||r.if&&(e.elseif||e.else)&&Kr(r,{exp:e.elseif,block:e}),n&&!e.forbidden)if(e.elseif||e.else)a=e,(u=function(e){var t=e.length;for(;t--;){if(1===e[t].type)return e[t];e.pop()}}(n.children))&&u.if&&Kr(u,{exp:a.elseif,block:a});else{if(e.slotScope){var o=e.slotTarget||'"default"';(n.scopedSlots||(n.scopedSlots={}))[o]=e}n.children.push(e),e.parent=n}var a,u;e.children=e.children.filter(function(e){return!e.slotScope}),l(e),e.pre&&(s=!1),Ar(e.tag)&&(c=!1);for(var f=0;f<$r.length;f++)$r[f](e,t)}function l(e){if(!c)for(var t;(t=e.children[e.children.length-1])&&3===t.type&&" "===t.text;)e.children.pop()}return function(e,t){for(var r,n,i=[],o=t.expectHTML,a=t.isUnaryTag||C,s=t.canBeLeftOpenTag||C,c=0;e;){if(r=e,n&&or(n)){var u=0,l=n.toLowerCase(),f=ar[l]||(ar[l]=new RegExp("([\\s\\S]*?)(]*>)","i")),p=e.replace(f,function(e,r,n){return u=n.length,or(l)||"noscript"===l||(r=r.replace(//g,"$1").replace(//g,"$1")),fr(l,r)&&(r=r.slice(1)),t.chars&&t.chars(r),""});c+=e.length-p.length,e=p,O(l,c-u,c)}else{var d=e.indexOf("<");if(0===d){if(nr.test(e)){var v=e.indexOf("--\x3e");if(v>=0){t.shouldKeepComment&&t.comment(e.substring(4,v),c,c+v+3),S(v+3);continue}}if(ir.test(e)){var h=e.indexOf("]>");if(h>=0){S(h+2);continue}}var m=e.match(rr);if(m){S(m[0].length);continue}var y=e.match(tr);if(y){var g=c;S(y[0].length),O(y[1],g,c);continue}var b=$();if(b){A(b),fr(b.tagName,e)&&S(1);continue}}var _=void 0,w=void 0,x=void 0;if(d>=0){for(w=e.slice(d);!(tr.test(w)||Yt.test(w)||nr.test(w)||ir.test(w)||(x=w.indexOf("<",1))<0);)d+=x,w=e.slice(d);_=e.substring(0,d)}d<0&&(_=e),_&&S(_.length),t.chars&&_&&t.chars(_,c-_.length,c)}if(e===r){t.chars&&t.chars(e);break}}function S(t){c+=t,e=e.substring(t)}function $(){var t=e.match(Yt);if(t){var r,n,i={tagName:t[1],attrs:[],start:c};for(S(t[0].length);!(r=e.match(er))&&(n=e.match(Xt)||e.match(Zt));)n.start=c,S(n[0].length),n.end=c,i.attrs.push(n);if(r)return i.unarySlash=r[1],S(r[0].length),i.end=c,i}}function A(e){var r=e.tagName,c=e.unarySlash;o&&("p"===n&&ht(r)&&O(n),s(r)&&n===r&&O(r));for(var u=a(r)||!!c,l=e.attrs.length,f=new Array(l),p=0;p=0&&i[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=i.length-1;u>=a;u--)t.end&&t.end(i[u].tag,r,o);i.length=a,n=a&&i[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,r,o):"p"===s&&(t.start&&t.start(e,[],!1,r,o),t.end&&t.end(e,r,o))}O()}(e,{warn:_r,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,o,a,l,f){var p=n&&n.ns||kr(e);se&&"svg"===p&&(o=function(e){for(var t=[],r=0;rc&&(s.push(o=e.slice(c,i)),a.push(JSON.stringify(o)));var u=$t(n[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),c=i+n[0].length}return c-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),It(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(n?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+vr(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+vr(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+vr(t,"$$c")+"}",null,!0)}(e,n,i);else if("input"===o&&"radio"===a)!function(e,t,r){var n=r&&r.number,i=Mt(e,"value")||"null";jt(e,"checked","_q("+t+","+(i=n?"_n("+i+")":i)+")"),It(e,"change",vr(t,i),null,!0)}(e,n,i);else{if("input"!==o&&"textarea"!==o)return dr(e,n,i),!1;!function(e,t,r){var n=e.attrsMap.type,i=r||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==n,u=o?"change":"range"===n?en:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=vr(t,l);c&&(f="if($event.target.composing)return;"+f),jt(e,"value","("+t+")"),It(e,u,f,null,!0),(s||a)&&It(e,"blur","$forceUpdate()")}(e,n,i)}return!0},text:function(e,t){t.value&&jt(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&jt(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:dt,mustUseProp:function(e,t,r){return"value"===r&&B(e)&&"button"!==t||"selected"===r&&"option"===e||"checked"===r&&"input"===e||"muted"===r&&"video"===e},canBeLeftOpenTag:vt,isReservedTag:function(e){return rt(e)||nt(e)},getTagNamespace:function(e){return nt(e)?"svg":"math"===e?"math":void 0},staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}(Yr)},rn=/^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/,nn=/\([^)]*?\);*$/,on=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,an={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},sn={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},cn=function(e){return"if("+e+")return null;"},un={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:cn("$event.target !== $event.currentTarget"),ctrl:cn("!$event.ctrlKey"),shift:cn("!$event.shiftKey"),alt:cn("!$event.altKey"),meta:cn("!$event.metaKey"),left:cn("'button' in $event && $event.button !== 0"),middle:cn("'button' in $event && $event.button !== 1"),right:cn("'button' in $event && $event.button !== 2")};function ln(e,t){var r=t?"nativeOn:":"on:",n="",i="";for(var o in e){var a=fn(e[o]);e[o]&&e[o].dynamic?i+=o+","+a+",":n+='"'+o+'":'+a+","}return n="{"+n.slice(0,-1)+"}",i?r+"_d("+n+",["+i.slice(0,-1)+"])":r+n}function fn(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return fn(e)}).join(",")+"]";var t=on.test(e.value),r=rn.test(e.value),n=on.test(e.value.replace(nn,""));if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(un[s])o+=un[s],an[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=cn(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map(pn).join("&&")+")return null;"}(a)),o&&(i+=o),"function($event){"+i+(t?"return "+e.value+".apply(null, arguments)":r?"return ("+e.value+").apply(null, arguments)":n?"return "+e.value:e.value)+"}"}return t||r?e.value:"function($event){"+(n?"return "+e.value:e.value)+"}"}function pn(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var r=an[e],n=sn[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(r)+",$event.key,"+JSON.stringify(n)+")"}var dn={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(r){return"_b("+r+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:k},vn=function(e){this.options=e,this.warn=e.warn||Tt,this.transforms=Ft(e.modules,"transformCode"),this.dataGenFns=Ft(e.modules,"genData"),this.directives=A(A({},dn),e.directives);var t=e.isReservedTag||C;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function hn(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return mn(e,t);if(e.once&&!e.onceProcessed)return yn(e,t);if(e.for&&!e.forProcessed)return bn(e,t);if(e.if&&!e.ifProcessed)return gn(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var r=e.slotName||'"default"',n=Sn(e,t),i="_t("+r+(n?",function(){return "+n+"}":""),o=e.attrs||e.dynamicAttrs?kn((e.attrs||[]).concat(e.dynamicAttrs||[]).map(function(e){return{name:w(e.name),value:e.value,dynamic:e.dynamic}})):null,a=e.attrsMap["v-bind"];!o&&!a||n||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var r;if(e.component)r=function(e,t,r){var n=t.inlineTemplate?null:Sn(t,r,!0);return"_c("+e+","+_n(t,r)+(n?","+n:"")+")"}(e.component,e,t);else{var n;(!e.plain||e.pre&&t.maybeComponent(e))&&(n=_n(e,t));var i=e.inlineTemplate?null:Sn(e,t,!0);r="_c('"+e.tag+"'"+(n?","+n:"")+(i?","+i:"")+")"}for(var o=0;o>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(r+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var r=e.children[0];if(r&&1===r.type){var n=function(e,t){var r=new vn(t);return{render:"with(this){return "+(e?"script"===e.tag?"null":hn(e,r):'_c("div")')+"}",staticRenderFns:r.staticRenderFns}}(r,t.options);return"inlineTemplate:{render:function(){"+n.render+"},staticRenderFns:["+n.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}(e,t);o&&(r+=o+",")}return r=r.replace(/,$/,"")+"}",e.dynamicAttrs&&(r="_b("+r+',"'+e.tag+'",'+kn(e.dynamicAttrs)+")"),e.wrapData&&(r=e.wrapData(r)),e.wrapListeners&&(r=e.wrapListeners(r)),r}function wn(e){return 1===e.type&&("slot"===e.tag||e.children.some(wn))}function xn(e,t){var r=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!r)return gn(e,t,xn,"null");if(e.for&&!e.forProcessed)return bn(e,t,xn);var n=e.slotScope===zr?"":String(e.slotScope),i="function("+n+"){return "+("template"===e.tag?e.if&&r?"("+e.if+")?"+(Sn(e,t)||"undefined")+":undefined":Sn(e,t)||"undefined":hn(e,t))+"}",o=n?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+i+o+"}"}function Sn(e,t,r,n,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=r?t.maybeComponent(a)?",1":",0":"";return""+(n||hn)(a,t)+s}var c=r?function(e,t){for(var r=0,n=0;n"'+(r?","+r:"")+")"}(e,t);case Pn.CHILDREN:return Dn(e,t,!0);case Pn.PARTIAL:return Dn(e,t,!1);default:return hn(e,t)}}function Dn(e,t,r){var n=e.plain?void 0:_n(e,t),i=r?"["+Bn(e,t)+"]":Un(e,t,!0);return"_c('"+e.tag+"'"+(n?","+n:"")+(i?","+i:"")+")"}function Un(e,t,r){return Sn(e,t,r,Rn,zn)}function zn(e,t){return 1===e.type?Rn(e,t):On(e)}function Bn(e,t){return e.children.length?"_ssrNode("+Vn(Kn(e,t))+")":""}function qn(e,t){return"("+Vn(Jn(e,t))+")"}function Jn(e,t){if(e.for&&!e.forProcessed)return e.forProcessed=!0,[{type:Mn,value:bn(e,t,qn,"_ssrList")}];if(e.if&&!e.ifProcessed)return e.ifProcessed=!0,[{type:Mn,value:gn(e,t,qn,'"\x3c!----\x3e"')}];if("template"===e.tag)return Kn(e,t);var r=Hn(e,t),n=Kn(e,t),i=t.options.isUnaryTag,o=i&&i(e.tag)?[]:[{type:Ln,value:""}];return r.concat(n,o)}function Hn(e,t){var r;!function(e,t){if(e.directives)for(var r=0;r"}),u}function Kn(e,t){var r;return(r=e.attrsMap["v-html"])?[{type:Mn,value:"_s("+r+")"}]:(r=e.attrsMap["v-text"])?[{type:In,value:"_s("+r+")"}]:"textarea"===e.tag&&(r=e.attrsMap["v-model"])?[{type:In,value:"_s("+r+")"}]:e.children?function(e,t){for(var r=[],n=0;n0&&(ti((u=e(u,(r||"")+"_"+c))[0])&&ti(f)&&(s[l]=Q(f.text+u[0].text),u.shift()),s.push.apply(s,u)):a(u)?ti(f)?s[l]=Q(f.text+u):""!==u&&s.push(Q(u)):ti(u)&&ti(f)?s[l]=Q(f.text+u.text):(o(t._isVList)&&i(u.tag)&&n(u.key)&&i(r)&&(u.key="__vlist"+r+"_"+c+"__"),s.push(u)));return s}(e):void 0}function ti(e){return i(e)&&i(e.text)&&!1===e.isComment}var ri={_ssrEscape:D,_ssrNode:function(e,t,r,n){return new ni(e,t,r,n)},_ssrList:function(e,t){var r,n,i,o,a="";if(Array.isArray(e)||"string"==typeof e)for(r=0,n=e.length;r0,a=e?!!e.$stable:!o,s=e&&e.$key;if(e){if(e._normalized)return e._normalized;if(a&&n&&n!==r&&s===n.$key&&!o&&!n.$hasNormal)return n;for(var c in i={},e)e[c]&&"$"!==c[0]&&(i[c]=Ti(t,c,e[c]))}else i={};for(var u in t)u in i||(i[u]=Fi(t,u));return e&&Object.isExtensible(e)&&(e._normalized=i),ee(i,"$stable",a),ee(i,"$key",s),ee(i,"$hasNormal",o),i}function Ti(e,t,r){var n=function(){var e,t=arguments.length?r.apply(null,arguments):r({}),n=(t=t&&"object"==typeof t&&!Array.isArray(t)?[t]:ei(t))&&t[0];return!t||n&&(1!==t.length||!n.isComment||(e=n).isComment&&e.asyncFactory)?t:void 0};return r.proxy&&Object.defineProperty(e,t,{get:n,enumerable:!0,configurable:!0}),n}function Fi(e,t){return function(){return e[t]}}var ji,Pi=null;function Ei(e,t){return(e.__esModule||ve&&"Module"===e[Symbol.toStringTag])&&(e=e.default),s(e)?t.extend(e):e}function Ni(e,t){ji.$on(e,t)}function Li(e,t){ji.$off(e,t)}function Ii(e,t){var r=ji;return function n(){null!==t.apply(null,arguments)&&r.$off(e,n)}}function Mi(e,t,r){ji=e,function(e,t,r,i,a,s){var c,u,l,f;for(c in e)u=e[c],l=t[c],f=ai(c),n(u)||(n(l)?(n(u.fns)&&(u=e[c]=si(u,s)),o(f.once)&&(u=e[c]=a(f.name,u,f.capture)),r(f.name,u,f.capture,f.passive,f.params)):u!==l&&(l.fns=u,e[c]=l));for(c in t)n(e[c])&&i((f=ai(c)).name,t[c],f.capture)}(t,r||{},Ni,Li,Ii,e),ji=void 0}function Ri(e){for(;e&&(e=e.$parent);)if(e._inactive)return!0;return!1}function Di(e,t){xe();var r=e.$options[t],n=t+" hook";if(r)for(var i=0,o=r.length;idocument.createEvent("Event").timeStamp&&(Ui=function(){return zi.now()})}function Bi(e,t,n,i,a){var s,c=this,u=a.options;g(i,"_uid")?(s=Object.create(i))._original=i:(s=i,i=i._original);var l=o(u._compiled),f=!l;this.data=e,this.props=t,this.children=n,this.parent=i,this.listeners=e.on||r,this.injections=function(e,t){if(e){for(var r=Object.create(null),n=ve?Reflect.ownKeys(e):Object.keys(e),i=0;i"}(e,r),u="";if(r.isUnaryTag(e.tag))a(c,s);else if(n(e.children)||0===e.children.length)a(c+u,s);else{var l=e.children;r.renderStates.push({type:"Element",children:l,rendered:0,total:l.length,endTag:u}),a(c,s)}}(e,t,r):o(e.isComment)?i(e.asyncFactory)?function(e,t,r){var n=e.asyncFactory,i=function(n){n.__esModule&&n.default&&(n=n.default);var i=e.asyncMeta,o=i.data,a=i.children,s=i.tag,c=e.asyncMeta.context,u=Vi(n,o,c,a,s);u?u.componentOptions?no(u,t,r):Array.isArray(u)?(r.renderStates.push({type:"Fragment",children:u,rendered:0,total:u.length}),r.next()):to(u,t,r):r.write("\x3c!----\x3e",r.next)};if(n.resolved)return void i(n.resolved);var o,a=r.done;try{o=n(i,a)}catch(e){a(e)}if(o)if("function"==typeof o.then)o.then(i,a).catch(a);else{var s=o.component;s&&"function"==typeof s.then&&s.then(i,a).catch(a)}}(e,t,r):r.write("\x3c!--"+e.text+"--\x3e",r.next):r.write(e.raw?e.text:D(String(e.text)),r.next)}function ro(e,t){var r=e._ssrRegister;return t.caching&&i(r)&&t.componentBuffer[t.componentBuffer.length-1].add(r),r}function no(e,t,r){var o=r.write,a=r.next,s=r.userContext,c=e.componentOptions.Ctor,u=c.options.serverCacheKey,l=c.options.name,f=r.cache,p=ro(c.options,o);if(i(u)&&i(f)&&i(l)){var d=u(e.componentOptions.propsData);if(!1===d)return void oo(e,t,r);var v=l+"::"+d,h=r.has,m=r.get;i(h)?h(v,function(n){!0===n&&i(m)?m(v,function(e){i(p)&&p(s),e.components.forEach(function(e){return e(s)}),o(e.html,a)}):io(e,t,v,r)}):i(m)&&m(v,function(n){i(n)?(i(p)&&p(s),n.components.forEach(function(e){return e(s)}),o(n.html,a)):io(e,t,v,r)})}else i(u)&&n(f)&&Gi("[vue-server-renderer] Component "+(c.options.name||"(anonymous)")+" implemented serverCacheKey, but no cache was provided to the renderer."),i(u)&&n(l)&&Gi('[vue-server-renderer] Components that implement "serverCacheKey" must also define a unique "name" option.'),oo(e,t,r)}function io(e,t,r,n){var i=n.write;i.caching=!0;var o=i.cacheBuffer,a=o.push("")-1,s=i.componentBuffer;s.push(new Set),n.renderStates.push({type:"ComponentWithCache",key:r,buffer:o,bufferIndex:a,componentBuffer:s}),oo(e,t,n)}function oo(e,t,r){var n=r.activeInstance;e.ssrContext=r.userContext;var i=r.activeInstance=Wi(e,r.activeInstance);Yi(i);var o=r.done;eo(i,function(){var o=i._render();o.parent=e,r.renderStates.push({type:"Component",prevActive:n}),to(o,t,r)},o)}function ao(e,t,r,n){return function(i,o,a,s){Xi=Object.create(null);var c=new wt({activeInstance:i,userContext:a,write:o,done:s,renderNode:to,isUnaryTag:r,modules:e,directives:t,cache:n});!function(e){if(!e._ssrNode){for(var t=e.constructor;t.super;)t=t.super;A(t.prototype,ri),t.FunctionalRenderContext&&A(t.FunctionalRenderContext.prototype,ri)}}(i),Yi(i);eo(i,function(){to(i._render(),!0,c)},s)}}var so=function(e){return/\.js(\?[^.]+)?$/.test(e)};function co(){var e,t;return{promise:new Promise(function(r,n){e=r,t=n}),cb:function(r,n){if(r)return t(r);e(n||"")}}}var uo=function(e){function t(t,r,n){e.call(this),this.started=!1,this.renderer=t,this.template=r,this.context=n||{},this.inject=t.inject}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype._transform=function(e,t,r){this.started||(this.emit("beforeStart"),this.start()),this.push(e),r()},t.prototype.start=function(){if(this.started=!0,this.push(this.template.head(this.context)),this.inject){this.context.head&&this.push(this.context.head);var e=this.renderer.renderResourceHints(this.context);e&&this.push(e);var t=this.renderer.renderStyles(this.context);t&&this.push(t)}this.push(this.template.neck(this.context))},t.prototype._flush=function(e){if(this.emit("beforeEnd"),this.inject){var t=this.renderer.renderState(this.context);t&&this.push(t);var r=this.renderer.renderScripts(this.context);r&&this.push(r)}this.push(this.template.tail(this.context)),e()},t}(require("stream").Transform),lo=require("lodash.template"),fo={escape:/{{([^{][\s\S]+?[^}])}}/g,interpolate:/{{{([\s\S]+?)}}}/g};function po(e){var t=function(e){var t=new Map;return Object.keys(e.modules).forEach(function(r){t.set(r,function(e,t){var r=[],n=t.modules[e];return n&&n.forEach(function(e){var n=t.all[e];n&&(t.async.indexOf(n)>-1||!/\.(js|css)($|\?)/.test(n))&&r.push(n)}),r}(r,e))}),t}(e);return function(e){for(var r=new Set,n=0;n"),n=e.indexOf(t);if(n<0)throw new Error("Content placeholder not found in template.");return r<0&&(r=e.indexOf(""))<0&&(r=n),{head:lo(e.slice(0,r),fo),neck:lo(e.slice(r,n),fo),tail:lo(e.slice(n+t.length),fo)}}(t):t:null,this.serialize=e.serializer||function(e){return ho(e,{isJSON:!0})},e.clientManifest){var r=this.clientManifest=e.clientManifest;this.publicPath=""===r.publicPath?"":r.publicPath.replace(/([^\/])$/,"$1/"),this.preloadFiles=(r.initial||[]).map(yo),this.prefetchFiles=(r.async||[]).map(yo),this.mapFiles=po(r)}};function yo(e){var t=e.replace(/\?.*/,""),r=vo.extname(t).slice(1);return{file:e,extension:r,fileWithoutQuery:t,asType:go(r)}}function go(e){return"js"===e?"script":"css"===e?"style":/jpe?g|png|svg|gif|webp|ico/.test(e)?"image":/woff2?|ttf|otf|eot/.test(e)?"font":""}mo.prototype.bindRenderFns=function(e){var t=this;["ResourceHints","State","Scripts","Styles"].forEach(function(r){e["render"+r]=t["render"+r].bind(t,e)}),e.getPreloadFiles=t.getPreloadFiles.bind(t,e)},mo.prototype.render=function(e,t){var r=this.parsedTemplate;if(!r)throw new Error("render cannot be called without a template.");return t=t||{},"function"==typeof r?r(e,t):this.inject?r.head(t)+(t.head||"")+this.renderResourceHints(t)+this.renderStyles(t)+r.neck(t)+e+this.renderState(t)+this.renderScripts(t)+r.tail(t):r.head(t)+r.neck(t)+e+r.tail(t)},mo.prototype.renderStyles=function(e){var t=this,r=this.preloadFiles||[],n=this.getUsedAsyncFiles(e)||[],i=r.concat(n).filter(function(e){return function(e){return/\.css(\?[^.]+)?$/.test(e)}(e.file)});return(i.length?i.map(function(e){var r=e.file;return''}).join(""):"")+(e.styles||"")},mo.prototype.renderResourceHints=function(e){return this.renderPreloadLinks(e)+this.renderPrefetchLinks(e)},mo.prototype.getPreloadFiles=function(e){var t=this.getUsedAsyncFiles(e);return this.preloadFiles||t?(this.preloadFiles||[]).concat(t||[]):[]},mo.prototype.renderPreloadLinks=function(e){var t=this,r=this.getPreloadFiles(e),n=this.options.shouldPreload;return r.length?r.map(function(e){var r=e.file,i=e.extension,o=e.fileWithoutQuery,a=e.asType,s="";return n||"script"===a||"style"===a?n&&!n(o,a)?"":("font"===a&&(s=' type="font/'+i+'" crossorigin'),'"):""}).join(""):""},mo.prototype.renderPrefetchLinks=function(e){var t=this,r=this.options.shouldPrefetch;if(this.prefetchFiles){var n=this.getUsedAsyncFiles(e);return this.prefetchFiles.map(function(e){var i=e.file,o=e.fileWithoutQuery,a=e.asType;return r&&!r(o,a)?"":function(e){return n&&n.some(function(t){return t.file===e})}(i)?"":''}).join("")}return""},mo.prototype.renderState=function(e,t){var r=t||{},n=r.contextKey;void 0===n&&(n="state");var i=r.windowKey;void 0===i&&(i="__INITIAL_STATE__");var o=this.serialize(e[n]),a=e.nonce?' nonce="'+e.nonce+'"':"";return e[n]?"window."+i+"="+o+";(function(){var s;(s=document.currentScript||document.scripts[document.scripts.length-1]).parentNode.removeChild(s);}());<\/script>":""},mo.prototype.renderScripts=function(e){var t=this;if(this.clientManifest){var r=this.preloadFiles.filter(function(e){var t=e.file;return so(t)}),n=(this.getUsedAsyncFiles(e)||[]).filter(function(e){var t=e.file;return so(t)});return[r[0]].concat(n,r.slice(1)).map(function(e){var r=e.file;return'` + ? `window.${windowKey}=${state}${autoRemove}` : '' } @@ -207,7 +224,7 @@ export default class TemplateRenderer { if (this.clientManifest) { const initial = this.preloadFiles.filter(({ file }) => isJS(file)) const async = (this.getUsedAsyncFiles(context) || []).filter(({ file }) => isJS(file)) - const needed = [initial[0]].concat(async || [], initial.slice(1)) + const needed = [initial[0]].concat(async, initial.slice(1)) return needed.map(({ file }) => { return `` }).join('') diff --git a/src/server/webpack-plugin/client.js b/src/server/webpack-plugin/client.js index ae2a2498672..ec7b87578b4 100644 --- a/src/server/webpack-plugin/client.js +++ b/src/server/webpack-plugin/client.js @@ -1,6 +1,6 @@ const hash = require('hash-sum') const uniq = require('lodash.uniq') -import { isJS, isCSS, onEmit } from './util' +import { isJS, isCSS, getAssetName, onEmit, stripModuleIdHash } from './util' export default class VueSSRClientPlugin { constructor (options = {}) { @@ -10,7 +10,8 @@ export default class VueSSRClientPlugin { } apply (compiler) { - onEmit(compiler, 'vue-client-plugin', (compilation, cb) => { + const stage = 'PROCESS_ASSETS_STAGE_ADDITIONAL' + onEmit(compiler, 'vue-client-plugin', stage, (compilation, cb) => { const stats = compilation.getStats().toJson() const allFiles = uniq(stats.assets @@ -19,6 +20,7 @@ export default class VueSSRClientPlugin { const initialFiles = uniq(Object.keys(stats.entrypoints) .map(name => stats.entrypoints[name].assets) .reduce((assets, all) => all.concat(assets), []) + .map(getAssetName) .filter((file) => isJS(file) || isCSS(file))) const asyncFiles = allFiles @@ -34,7 +36,7 @@ export default class VueSSRClientPlugin { } const assetModules = stats.modules.filter(m => m.assets.length) - const fileToIndex = file => manifest.all.indexOf(file) + const fileToIndex = asset => manifest.all.indexOf(getAssetName(asset)) stats.modules.forEach(m => { // ignore modules duplicated in multiple chunks if (m.chunks.length === 1) { @@ -43,7 +45,7 @@ export default class VueSSRClientPlugin { if (!chunk || !chunk.files) { return } - const id = m.identifier.replace(/\s\w+$/, '') // remove appended hash + const id = stripModuleIdHash(m.identifier) const files = manifest.modules[hash(id)] = chunk.files.map(fileToIndex) // find all asset modules associated with the same chunk assetModules.forEach(m => { diff --git a/src/server/webpack-plugin/server.js b/src/server/webpack-plugin/server.js index 305b4bab58b..02fab245bf9 100644 --- a/src/server/webpack-plugin/server.js +++ b/src/server/webpack-plugin/server.js @@ -1,4 +1,4 @@ -import { validate, isJS, onEmit } from './util' +import { validate, isJS, getAssetName, onEmit } from './util' export default class VueSSRServerPlugin { constructor (options = {}) { @@ -10,7 +10,8 @@ export default class VueSSRServerPlugin { apply (compiler) { validate(compiler) - onEmit(compiler, 'vue-server-plugin', (compilation, cb) => { + const stage = 'PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER' + onEmit(compiler, 'vue-server-plugin', stage, (compilation, cb) => { const stats = compilation.getStats().toJson() const entryName = Object.keys(stats.entrypoints)[0] const entryInfo = stats.entrypoints[entryName] @@ -20,7 +21,9 @@ export default class VueSSRServerPlugin { return cb() } - const entryAssets = entryInfo.assets.filter(isJS) + const entryAssets = entryInfo.assets + .map(getAssetName) + .filter(isJS) if (entryAssets.length > 1) { throw new Error( @@ -42,14 +45,14 @@ export default class VueSSRServerPlugin { maps: {} } - stats.assets.forEach(asset => { - if (isJS(asset.name)) { - bundle.files[asset.name] = compilation.assets[asset.name].source() - } else if (asset.name.match(/\.js\.map$/)) { - bundle.maps[asset.name.replace(/\.map$/, '')] = JSON.parse(compilation.assets[asset.name].source()) + Object.keys(compilation.assets).forEach(name => { + if (isJS(name)) { + bundle.files[name] = compilation.assets[name].source() + } else if (name.match(/\.js\.map$/)) { + bundle.maps[name.replace(/\.map$/, '')] = JSON.parse(compilation.assets[name].source()) } // do not emit anything else for server - delete compilation.assets[asset.name] + delete compilation.assets[name] }) const json = JSON.stringify(bundle, null, 2) diff --git a/src/server/webpack-plugin/util.js b/src/server/webpack-plugin/util.js index 94a204a22b2..844671bbd7a 100644 --- a/src/server/webpack-plugin/util.js +++ b/src/server/webpack-plugin/util.js @@ -1,16 +1,27 @@ const { red, yellow } = require('chalk') +const webpack = require('webpack') const prefix = `[vue-server-renderer-webpack-plugin]` const warn = exports.warn = msg => console.error(red(`${prefix} ${msg}\n`)) const tip = exports.tip = msg => console.log(yellow(`${prefix} ${msg}\n`)) +const isWebpack5 = !!(webpack.version && webpack.version[0] > 4) + export const validate = compiler => { if (compiler.options.target !== 'node') { warn('webpack config `target` should be "node".') } - if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') { - warn('webpack config `output.libraryTarget` should be "commonjs2".') + if (compiler.options.output) { + if (compiler.options.output.library) { + // Webpack >= 5.0.0 + if (compiler.options.output.library.type !== 'commonjs2') { + warn('webpack config `output.library.type` should be "commonjs2".') + } + } else if (compiler.options.output.libraryTarget !== 'commonjs2') { + // Webpack < 5.0.0 + warn('webpack config `output.libraryTarget` should be "commonjs2".') + } } if (!compiler.options.externals) { @@ -21,8 +32,20 @@ export const validate = compiler => { } } -export const onEmit = (compiler, name, hook) => { - if (compiler.hooks) { +export const onEmit = (compiler, name, stageName, hook) => { + if (isWebpack5) { + // Webpack >= 5.0.0 + compiler.hooks.compilation.tap(name, compilation => { + if (compilation.compiler !== compiler) { + // Ignore child compilers + return + } + const stage = webpack.Compilation[stageName] + compilation.hooks.processAssets.tapAsync({ name, stage }, (assets, cb) => { + hook(compilation, cb) + }) + }) + } else if (compiler.hooks) { // Webpack >= 4.0.0 compiler.hooks.emit.tapAsync(name, hook) } else { @@ -31,4 +54,20 @@ export const onEmit = (compiler, name, hook) => { } } +export const stripModuleIdHash = id => { + if (isWebpack5) { + // Webpack >= 5.0.0 + return id.replace(/\|\w+$/, '') + } + // Webpack < 5.0.0 + return id.replace(/\s\w+$/, '') +} + +export const getAssetName = asset => { + if (typeof asset === 'string') { + return asset + } + return asset.name +} + export { isJS, isCSS } from '../util' diff --git a/src/server/write.js b/src/server/write.js index 9d81d2a8e04..27a5e8a2667 100644 --- a/src/server/write.js +++ b/src/server/write.js @@ -1,6 +1,6 @@ /* @flow */ -const MAX_STACK_DEPTH = 900 +const MAX_STACK_DEPTH = 800 const noop = _ => _ const defer = typeof process !== 'undefined' && process.nextTick diff --git a/src/sfc/parser.js b/src/sfc/parser.js index f6e83d362a3..59c5fc3a2c0 100644 --- a/src/sfc/parser.js +++ b/src/sfc/parser.js @@ -8,11 +8,6 @@ const splitRE = /\r?\n/g const replaceRE = /./g const isSpecialTag = makeMap('script,style,template', true) -type Attribute = { - name: string, - value: string -}; - /** * Parse a single-file component (*.vue) file into an SFC Descriptor Object. */ @@ -24,14 +19,32 @@ export function parseComponent ( template: null, script: null, styles: [], - customBlocks: [] + customBlocks: [], + errors: [] } let depth = 0 let currentBlock: ?SFCBlock = null + let warn = msg => { + sfc.errors.push(msg) + } + + if (process.env.NODE_ENV !== 'production' && options.outputSourceRange) { + warn = (msg, range) => { + const data: WarningMessage = { msg } + if (range.start != null) { + data.start = range.start + } + if (range.end != null) { + data.end = range.end + } + sfc.errors.push(data) + } + } + function start ( tag: string, - attrs: Array, + attrs: Array, unary: boolean, start: number, end: number @@ -62,7 +75,7 @@ export function parseComponent ( } } - function checkAttrs (block: SFCBlock, attrs: Array) { + function checkAttrs (block: SFCBlock, attrs: Array) { for (let i = 0; i < attrs.length; i++) { const attr = attrs[i] if (attr.name === 'lang') { @@ -84,14 +97,13 @@ export function parseComponent ( if (depth === 1 && currentBlock) { currentBlock.end = start let text = content.slice(currentBlock.start, currentBlock.end) + if (options.deindent !== false) { + text = deindent(text) + } // pad content so that linters and pre-processors can output correct // line numbers in errors and warnings - if (options.pad) { + if (currentBlock.type !== 'template' && options.pad) { text = padContent(currentBlock, options.pad) + text - } else { - // avoid to deindent if pad option is specified - // to retain original source position. - text = deindent(text) } currentBlock.content = text currentBlock = null @@ -112,8 +124,10 @@ export function parseComponent ( } parseHTML(content, { + warn, start, - end + end, + outputSourceRange: options.outputSourceRange }) return sfc diff --git a/src/shared/constants.js b/src/shared/constants.js index 84d019fb4ca..a8b15e043dd 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -17,5 +17,6 @@ export const LIFECYCLE_HOOKS = [ 'destroyed', 'activated', 'deactivated', - 'errorCaptured' + 'errorCaptured', + 'serverPrefetch' ] diff --git a/src/shared/util.js b/src/shared/util.js index f4a503b0e93..3168a1ff9c0 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -35,7 +35,7 @@ export function isPrimitive (value: any): boolean %checks { /** * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value + * objects from primitive values when we know the value * is a JSON-compliant type. */ export function isObject (obj: mixed): boolean %checks { @@ -71,13 +71,21 @@ export function isValidArrayIndex (val: any): boolean { return n >= 0 && Math.floor(n) === n && isFinite(val) } +export function isPromise (val: any): boolean { + return ( + isDef(val) && + typeof val.then === 'function' && + typeof val.catch === 'function' + ) +} + /** * Convert a value to a string that is actually rendered. */ export function toString (val: any): string { return val == null ? '' - : typeof val === 'object' + : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) ? JSON.stringify(val, null, 2) : String(val) } diff --git a/test/e2e/nightwatch.config.js b/test/e2e/nightwatch.config.js index 2004f92b45b..8ec592494fe 100644 --- a/test/e2e/nightwatch.config.js +++ b/test/e2e/nightwatch.config.js @@ -50,7 +50,8 @@ module.exports = { 'desiredCapabilities': { 'browserName': 'phantomjs', 'javascriptEnabled': true, - 'acceptSslCerts': true + 'acceptSslCerts': true, + 'phantomjs.binary.path': require('phantomjs-prebuilt').path } } } diff --git a/test/e2e/specs/async-edge-cases.html b/test/e2e/specs/async-edge-cases.html index 4c7411b2e5d..7bca8f8cb9c 100644 --- a/test/e2e/specs/async-edge-cases.html +++ b/test/e2e/specs/async-edge-cases.html @@ -6,7 +6,6 @@ -
diff --git a/test/e2e/specs/async-edge-cases.js b/test/e2e/specs/async-edge-cases.js index 2873409400b..27212b76d65 100644 --- a/test/e2e/specs/async-edge-cases.js +++ b/test/e2e/specs/async-edge-cases.js @@ -14,7 +14,7 @@ module.exports = { .assert.containsText('#case-1', '3') .assert.checked('#case-1 input', false) - // #6566 + // // #6566 .assert.containsText('#case-2 button', 'Expand is True') .assert.containsText('.count-a', 'countA: 0') .assert.containsText('.count-b', 'countB: 0') diff --git a/test/helpers/trigger-event.js b/test/helpers/trigger-event.js index 4cd5d79db55..e2825135730 100644 --- a/test/helpers/trigger-event.js +++ b/test/helpers/trigger-event.js @@ -1,6 +1,9 @@ window.triggerEvent = function triggerEvent (target, event, process) { const e = document.createEvent('HTMLEvents') e.initEvent(event, true, true) + if (event === 'click') { + e.button = 0 + } if (process) process(e) target.dispatchEvent(e) } diff --git a/test/ssr/fixtures/cache-opt-out.js b/test/ssr/fixtures/cache-opt-out.js new file mode 100644 index 00000000000..772d403c04d --- /dev/null +++ b/test/ssr/fixtures/cache-opt-out.js @@ -0,0 +1,16 @@ +import Vue from '../../../dist/vue.runtime.common.js' + +const app = { + name: 'app', + props: ['id'], + serverCacheKey: props => props.id === 1 ? false : props.id, + render (h) { + return h('div', '/test') + } +} + +export default () => { + return Promise.resolve(new Vue({ + render: h => h(app, { props: { id: 1 }}) + })) +} diff --git a/test/ssr/jasmine.js b/test/ssr/jasmine.js index dfd70f7c253..b632601642d 100644 --- a/test/ssr/jasmine.js +++ b/test/ssr/jasmine.js @@ -4,6 +4,7 @@ module.exports = { '*.spec.js' ], helpers: [ - require.resolve('@babel/register') + require.resolve('@babel/register'), + '../helpers/to-have-been-warned.js' ] } diff --git a/test/ssr/ssr-basic-renderer.spec.js b/test/ssr/ssr-basic-renderer.spec.js index 09ce8e7dd46..04c8d80e6a7 100644 --- a/test/ssr/ssr-basic-renderer.spec.js +++ b/test/ssr/ssr-basic-renderer.spec.js @@ -52,7 +52,7 @@ describe('SSR: basicRenderer', () => { }) // #5941 - it('should work peoperly when accessing $ssrContext in root component', done => { + it('should work properly when accessing $ssrContext in root component', done => { let ssrContext renderToString(new Vue({ template: ` diff --git a/test/ssr/ssr-bundle-render.spec.js b/test/ssr/ssr-bundle-render.spec.js index 8ea05b32172..ce7f8780a90 100644 --- a/test/ssr/ssr-bundle-render.spec.js +++ b/test/ssr/ssr-bundle-render.spec.js @@ -195,7 +195,7 @@ function createAssertions (runInNewContext) { }) it('render with cache (nested)', done => { - const cache = LRU({ maxAge: Infinity }) + const cache = new LRU({ maxAge: Infinity }) spyOn(cache, 'get').and.callThrough() spyOn(cache, 'set').and.callThrough() const options = { @@ -231,6 +231,44 @@ function createAssertions (runInNewContext) { }) }) + it('render with cache (opt-out)', done => { + const cache = {} + const get = jasmine.createSpy('get') + const set = jasmine.createSpy('set') + const options = { + runInNewContext, + cache: { + // async + get: (key, cb) => { + setTimeout(() => { + get(key) + cb(cache[key]) + }, 0) + }, + set: (key, val) => { + set(key, val) + cache[key] = val + } + } + } + createRenderer('cache-opt-out.js', options, renderer => { + const expected = '
/test
' + renderer.renderToString((err, res) => { + expect(err).toBeNull() + expect(res).toBe(expected) + expect(get).not.toHaveBeenCalled() + expect(set).not.toHaveBeenCalled() + renderer.renderToString((err, res) => { + expect(err).toBeNull() + expect(res).toBe(expected) + expect(get).not.toHaveBeenCalled() + expect(set).not.toHaveBeenCalled() + done() + }) + }) + }) + }) + it('renderToString (bundle format with code split)', done => { createRenderer('split.js', { runInNewContext, asBundle: true }, renderer => { const context = { url: '/test' } diff --git a/test/ssr/ssr-stream.spec.js b/test/ssr/ssr-stream.spec.js index 5973b5ed0d8..04e79658984 100644 --- a/test/ssr/ssr-stream.spec.js +++ b/test/ssr/ssr-stream.spec.js @@ -102,4 +102,26 @@ describe('SSR: renderToStream', () => { stream1.read(1) stream2.read(1) }) + + it('should call context.rendered', done => { + let a = 0 + const stream = renderToStream(new Vue({ + template: ` +
Hello
+ ` + }), { + rendered: () => { + a = 42 + } + }) + let res = '' + stream.on('data', chunk => { + res += chunk + }) + stream.on('end', () => { + expect(res).toContain('
Hello
') + expect(a).toBe(42) + done() + }) + }) }) diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js index a84c7267e08..33094be9c33 100644 --- a/test/ssr/ssr-string.spec.js +++ b/test/ssr/ssr-string.spec.js @@ -640,6 +640,41 @@ describe('SSR: renderToString', () => { }) }) + it('renders nested async functional component', done => { + renderVmWithOptions({ + template: ` +
+ +
+ `, + components: { + outerAsync (resolve) { + setTimeout(() => resolve({ + functional: true, + render (h) { + return h('innerAsync') + } + }), 1) + }, + innerAsync (resolve) { + setTimeout(() => resolve({ + functional: true, + render (h) { + return h('span', { class: ['a'] }, 'inner') + }, + }), 1) + } + } + }, result => { + expect(result).toContain( + '
' + + 'inner' + + '
' + ) + done() + }) + }) + it('should catch async component error', done => { Vue.config.silent = true renderToString(new Vue({ @@ -659,6 +694,34 @@ describe('SSR: renderToString', () => { }) }) + // #11963, #10391 + it('renders async children passed in slots', done => { + const Parent = { + template: `
` + } + const Child = { + template: `

child

` + } + renderVmWithOptions({ + template: ` + + + + `, + components: { + Parent, + Child: () => Promise.resolve(Child) + } + }, result => { + expect(result).toContain( + `

child

` + ) + done() + }) + }) + it('everything together', done => { renderVmWithOptions({ template: ` @@ -826,6 +889,22 @@ describe('SSR: renderToString', () => { }) }) + it('should not warn for custom directives that do not have server-side implementation', done => { + renderToString(new Vue({ + directives: { + test: { + bind() { + // noop + } + } + }, + template: '
', + }), () => { + expect('Failed to resolve directive: test').not.toHaveBeenWarned() + done() + }) + }) + it('_scopeId', done => { renderVmWithOptions({ _scopeId: '_v-parent', @@ -1272,7 +1351,7 @@ describe('SSR: renderToString', () => {
` }, result => { - expect(result).toContain(`
`) + expect(result).toContain(`
`) done() }) }) @@ -1295,6 +1374,298 @@ describe('SSR: renderToString', () => { done() }) }) + + it('should support serverPrefetch option', done => { + renderVmWithOptions({ + template: ` +
{{ count }}
+ `, + data: { + count: 0 + }, + serverPrefetch () { + return new Promise((resolve) => { + setTimeout(() => { + this.count = 42 + resolve() + }, 1) + }) + } + }, result => { + expect(result).toContain('
42
') + done() + }) + }) + + it('should support serverPrefetch option (nested)', done => { + renderVmWithOptions({ + template: ` +
+ {{ count }} + +
+ `, + data: { + count: 0 + }, + serverPrefetch () { + return new Promise((resolve) => { + setTimeout(() => { + this.count = 42 + resolve() + }, 1) + }) + }, + components: { + nestedPrefetch: { + template: ` +
{{ message }}
+ `, + data () { + return { + message: '' + } + }, + serverPrefetch () { + return new Promise((resolve) => { + setTimeout(() => { + this.message = 'vue.js' + resolve() + }, 1) + }) + } + } + } + }, result => { + expect(result).toContain('
42
vue.js
') + done() + }) + }) + + it('should support serverPrefetch option (nested async)', done => { + renderVmWithOptions({ + template: ` +
+ {{ count }} + +
+ `, + data: { + count: 0 + }, + serverPrefetch () { + return new Promise((resolve) => { + setTimeout(() => { + this.count = 42 + resolve() + }, 1) + }) + }, + components: { + nestedPrefetch (resolve) { + resolve({ + template: ` +
{{ message }}
+ `, + data () { + return { + message: '' + } + }, + serverPrefetch () { + return new Promise((resolve) => { + setTimeout(() => { + this.message = 'vue.js' + resolve() + }, 1) + }) + } + }) + } + } + }, result => { + expect(result).toContain('
42
vue.js
') + done() + }) + }) + + it('should merge serverPrefetch option', done => { + const mixin = { + data: { + message: '' + }, + serverPrefetch () { + return new Promise((resolve) => { + setTimeout(() => { + this.message = 'vue.js' + resolve() + }, 1) + }) + } + } + renderVmWithOptions({ + mixins: [mixin], + template: ` +
+ {{ count }} +
{{ message }}
+
+ `, + data: { + count: 0 + }, + serverPrefetch () { + return new Promise((resolve) => { + setTimeout(() => { + this.count = 42 + resolve() + }, 1) + }) + } + }, result => { + expect(result).toContain('
42
vue.js
') + done() + }) + }) + + it(`should skip serverPrefetch option that doesn't return a promise`, done => { + renderVmWithOptions({ + template: ` +
{{ count }}
+ `, + data: { + count: 0 + }, + serverPrefetch () { + setTimeout(() => { + this.count = 42 + }, 1) + } + }, result => { + expect(result).toContain('
0
') + done() + }) + }) + + it('should call context.rendered', done => { + let a = 0 + renderToString(new Vue({ + template: '
Hello
' + }), { + rendered: () => { + a = 42 + } + }, (err, res) => { + expect(err).toBeNull() + expect(res).toContain('
Hello
') + expect(a).toBe(42) + done() + }) + }) + + it('invalid style value', done => { + renderVmWithOptions({ + template: '

', + data: { + // all invalid, should not even have "style" attribute + style: { + opacity: {}, + color: null + }, + // mix of valid and invalid + style2: { + opacity: 0, + color: null + } + } + }, result => { + expect(result).toContain( + '

' + ) + done() + }) + }) + + it('numeric style value', done => { + renderVmWithOptions({ + template: '
', + data: { + style: { + opacity: 0, // valid, opacity is unit-less + top: 0, // valid, top requires unit but 0 is allowed + left: 10, // invalid, left requires a unit + marginTop: '10px' // valid + } + } + }, result => { + expect(result).toContain( + '
' + ) + done() + }) + }) + + it('handling max stack size limit', done => { + const vueInstance = new Vue({ + template: `
+ +
`, + components: { + child: { + template: '
hi
' + } + }, + data: { + items: Array(1000).fill(0) + } + }) + + renderToString(vueInstance, err => done(err)) + }) + + it('undefined v-model with textarea', done => { + renderVmWithOptions({ + render (h) { + return h('div', [ + h('textarea', { + domProps: { + value: null + } + }) + ]) + } + }, result => { + expect(result).toContain( + '
' + ) + done() + }) + }) + + it('Options inheritAttrs in parent component', done => { + const childComponent = { + template: `
{{ someProp }}
`, + props: { + someProp: {} + }, + } + const parentComponent = { + template: ``, + components: { childComponent }, + inheritAttrs: false + } + renderVmWithOptions({ + template: ` +
+ +
+ `, + components: { parentComponent } + }, result => { + expect(result).toContain('
some-val
') + done() + }) + }) }) function renderVmWithOptions (options, cb) { diff --git a/test/ssr/ssr-template.spec.js b/test/ssr/ssr-template.spec.js index 2491b591d87..d30f096668a 100644 --- a/test/ssr/ssr-template.spec.js +++ b/test/ssr/ssr-template.spec.js @@ -99,6 +99,103 @@ describe('SSR: template option', () => { }) }) + it('renderToString with interpolation and context.rendered', done => { + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + title: '', + snippet: '
foo
', + head: '', + styles: '', + state: { a: 0 }, + rendered: context => { + context.state.a = 1 + } + } + + renderer.renderToString(new Vue({ + template: '
hi
' + }), context, (err, res) => { + expect(err).toBeNull() + expect(res).toContain( + `` + + // double mustache should be escaped + `<script>hacks</script>` + + `${context.head}${context.styles}` + + `
hi
` + + `` + + // triple should be raw + `
foo
` + + `` + ) + done() + }) + }) + + it('renderToString w/ template function', done => { + const renderer = createRenderer({ + template: (content, context) => `${context.head}${content}` + }) + + const context = { + head: '' + } + + renderer.renderToString(new Vue({ + template: '
hi
' + }), context, (err, res) => { + expect(err).toBeNull() + expect(res).toContain(`${context.head}
hi
`) + done() + }) + }) + + it('renderToString w/ template function returning Promise', done => { + const renderer = createRenderer({ + template: (content, context) => new Promise((resolve) => { + setTimeout(() => { + resolve(`${context.head}${content}`) + }, 0) + }) + }) + + const context = { + head: '' + } + + renderer.renderToString(new Vue({ + template: '
hi
' + }), context, (err, res) => { + expect(err).toBeNull() + expect(res).toContain(`${context.head}
hi
`) + done() + }) + }) + + it('renderToString w/ template function returning Promise w/ rejection', done => { + const renderer = createRenderer({ + template: () => new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error(`foo`)) + }, 0) + }) + }) + + const context = { + head: '' + } + + renderer.renderToString(new Vue({ + template: '
hi
' + }), context, (err, res) => { + expect(err.message).toBe(`foo`) + expect(res).toBeUndefined() + done() + }) + }) + it('renderToStream', done => { const renderer = createRenderer({ template: defaultTemplate @@ -166,6 +263,46 @@ describe('SSR: template option', () => { }) }) + it('renderToStream with interpolation and context.rendered', done => { + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + title: '', + snippet: '
foo
', + head: '', + styles: '', + state: { a: 0 }, + rendered: context => { + context.state.a = 1 + } + } + + const stream = renderer.renderToStream(new Vue({ + template: '
hi
' + }), context) + + let res = '' + stream.on('data', chunk => { + res += chunk + }) + stream.on('end', () => { + expect(res).toContain( + `` + + // double mustache should be escaped + `<script>hacks</script>` + + `${context.head}${context.styles}` + + `
hi
` + + `` + + // triple should be raw + `
foo
` + + `` + ) + done() + }) + }) + it('bundleRenderer + renderToString', done => { createBundleRenderer('app.js', { asBundle: true, @@ -387,5 +524,54 @@ describe('SSR: template option', () => { done() }) }) + + it('renderToString + nonce', done => { + const interpolateTemplate = `hello` + const renderer = createRenderer({ + template: interpolateTemplate + }) + + const context = { + state: { a: 1 }, + nonce: '4AEemGb0xJptoIGFP3Nd' + } + + renderer.renderToString(new Vue({ + template: '
hi
' + }), context, (err, res) => { + expect(err).toBeNull() + expect(res).toContain( + `` + + `hello` + + `` + + `
hi
` + + `` + + `` + ) + done() + }) + }) + + it('renderToString + custom serializer', done => { + const expected = `{"foo":123}` + const renderer = createRenderer({ + template: defaultTemplate, + serializer: () => expected + }) + + const context = { + state: { a: 1 } + } + + renderer.renderToString(new Vue({ + template: '
hi
' + }), context, (err, res) => { + expect(err).toBeNull() + expect(res).toContain( + `` + ) + done() + }) + }) } }) diff --git a/test/unit/features/component/component-async.spec.js b/test/unit/features/component/component-async.spec.js index 6e057b81e63..8ce085de313 100644 --- a/test/unit/features/component/component-async.spec.js +++ b/test/unit/features/component/component-async.spec.js @@ -2,6 +2,42 @@ import Vue from 'vue' import { Promise } from 'es6-promise' describe('Component async', () => { + + const oldSetTimeout = window.setTimeout; + const oldClearTimeout = window.clearTimeout; + + // will contain pending timeouts set during the test iteration + // will contain the id of the timeout as the key, and the millisecond timeout as the value + // this helps to identify the timeout that is still pending + let timeoutsPending = {}; + + beforeEach(function () { + // reset the timeouts for this iteration + timeoutsPending = {}; + + window.setTimeout = function(func, delay) { + let id = oldSetTimeout(function() { + delete timeoutsPending[id]; + func(); + }, delay); + timeoutsPending[id] = delay; + return id + }; + + window.clearTimeout = function(id) { + oldClearTimeout(id); + delete timeoutsPending[id]; + }; + }) + + afterEach(function () { + window.setTimeout = oldSetTimeout; + window.clearTimeout = oldClearTimeout; + // after the test is complete no timeouts that have been set up during the test should still be active + // compare stringified JSON for better error message containing ID and millisecond timeout + expect(JSON.stringify(timeoutsPending)).toEqual(JSON.stringify({})) + }) + it('normal', done => { const vm = new Vue({ template: '
', @@ -343,6 +379,34 @@ describe('Component async', () => { }, 50) }) + it('should not have running timeout/loading if resolved', done => { + const vm = new Vue({ + template: `
`, + components: { + test: () => ({ + component: new Promise((resolve, reject) => { + setTimeout(() => { + resolve({ template: '
hi
' }) + Promise.resolve().then(() => { + Vue.nextTick(next) + }) + }, 10) + }), + loading: { template: `
loading
` }, + delay: 30, + error: { template: `
error
` }, + timeout: 40 + }) + } + }).$mount() + + function next () { + expect(vm.$el.textContent).toBe('hi') + // the afterEach() will ensure that the timeouts for delay and timeout have been cleared + done() + } + }) + // #7107 it(`should work when resolving sync in sibling component's mounted hook`, done => { let resolveTwo diff --git a/test/unit/features/component/component-keep-alive.spec.js b/test/unit/features/component/component-keep-alive.spec.js index a2cdf6a4f09..9d30d2c7d58 100644 --- a/test/unit/features/component/component-keep-alive.spec.js +++ b/test/unit/features/component/component-keep-alive.spec.js @@ -572,6 +572,73 @@ describe('Component keep-alive', () => { }).then(done) }) + it('max=1', done => { + const spyA = jasmine.createSpy() + const spyB = jasmine.createSpy() + const spyC = jasmine.createSpy() + const spyAD = jasmine.createSpy() + const spyBD = jasmine.createSpy() + const spyCD = jasmine.createSpy() + + function assertCount (calls) { + expect([ + spyA.calls.count(), + spyAD.calls.count(), + spyB.calls.count(), + spyBD.calls.count(), + spyC.calls.count(), + spyCD.calls.count() + ]).toEqual(calls) + } + + const vm = new Vue({ + template: ` + + + + `, + data: { + n: 'aa' + }, + components: { + aa: { + template: '
a
', + created: spyA, + destroyed: spyAD + }, + bb: { + template: '
bbb
', + created: spyB, + destroyed: spyBD + }, + cc: { + template: '
ccc
', + created: spyC, + destroyed: spyCD + } + } + }).$mount() + + assertCount([1, 0, 0, 0, 0, 0]) + vm.n = 'bb' + waitForUpdate(() => { + // should prune A because max cache reached + assertCount([1, 1, 1, 0, 0, 0]) + vm.n = 'cc' + }).then(() => { + // should prune B because max cache reached + assertCount([1, 1, 1, 1, 1, 0]) + vm.n = 'bb' + }).then(() => { + // B is recreated + assertCount([1, 1, 2, 1, 1, 1]) + vm.n = 'aa' + }).then(() => { + // B is destroyed and A recreated + assertCount([2, 1, 2, 2, 1, 1]) + }).then(done) + }) + it('should warn unknown component inside', () => { new Vue({ template: `` @@ -1182,5 +1249,37 @@ describe('Component keep-alive', () => { }).then(done) } }) + + // #10083 + it('should not attach event handler repeatedly', done => { + const vm = new Vue({ + template: ` + + + + `, + data: { showBtn: true, n: 0 }, + methods: { + add () { + this.n++ + } + }, + components: { + btn: { template: '' } + } + }).$mount() + + const btn = vm.$el + expect(vm.n).toBe(0) + btn.click() + expect(vm.n).toBe(1) + vm.showBtn = false + waitForUpdate(() => { + vm.showBtn = true + }).then(() => { + btn.click() + expect(vm.n).toBe(2) + }).then(done) + }) } }) diff --git a/test/unit/features/component/component-scoped-slot.spec.js b/test/unit/features/component/component-scoped-slot.spec.js index 4fab0127ea7..a77b055ccd6 100644 --- a/test/unit/features/component/component-scoped-slot.spec.js +++ b/test/unit/features/component/component-scoped-slot.spec.js @@ -395,11 +395,9 @@ describe('Component scoped slot', () => { return { msg: 'hello' } }, render (h) { - return h('div', [ - this.$scopedSlots.item({ - text: this.msg - }) - ]) + return h('div', this.$scopedSlots.item({ + text: this.msg + })) } } } @@ -425,14 +423,54 @@ describe('Component scoped slot', () => { return { msg: 'hello' } }, render (h) { + return h('div', this.$scopedSlots.default({ msg: this.msg })) + } + } + } + }).$mount() + expect(vm.$el.innerHTML).toBe('hello') + }) + + it('render function usage (default, as root)', () => { + const vm = new Vue({ + render (h) { + return h('test', [ + props => h('span', [props.msg]) + ]) + }, + components: { + test: { + data () { + return { msg: 'hello' } + }, + render (h) { + const res = this.$scopedSlots.default({ msg: this.msg }) + // all scoped slots should be normalized into arrays + expect(Array.isArray(res)).toBe(true) + return res + } + } + } + }).$mount() + expect(vm.$el.outerHTML).toBe('hello') + }) + + // new in 2.6, unifying all slots as functions + it('non-scoped slots should also be available on $scopedSlots', () => { + const vm = new Vue({ + template: `before
{{ scope.msg }}
after
`, + components: { + foo: { + render(h) { return h('div', [ - this.$scopedSlots.default({ msg: this.msg }) + this.$scopedSlots.default(), + this.$scopedSlots.bar({ msg: 'hi' }) ]) } } } }).$mount() - expect(vm.$el.innerHTML).toBe('hello') + expect(vm.$el.innerHTML).toBe(`before after
hi
`) }) // #4779 @@ -593,4 +631,722 @@ describe('Component scoped slot', () => { expect(vm.$el.innerHTML).toBe('

hello

') }).then(done) }) + + // #9422 + // the behavior of the new syntax is slightly different. + it('scoped slot v-if using slot-scope value', () => { + const Child = { + template: '
', + } + const vm = new Vue({ + components: { Child }, + template: ` + + + + ` + }).$mount() + expect(vm.$el.textContent).toMatch(`foo foo`) + }) + + // 2.6 new slot syntax + describe('v-slot syntax', () => { + const Foo = { + render(h) { + return h('div', [ + this.$scopedSlots.default && this.$scopedSlots.default('from foo default'), + this.$scopedSlots.one && this.$scopedSlots.one('from foo one'), + this.$scopedSlots.two && this.$scopedSlots.two('from foo two') + ]) + } + } + + const Bar = { + render(h) { + return this.$scopedSlots.default && this.$scopedSlots.default('from bar') + } + } + + const Baz = { + render(h) { + return this.$scopedSlots.default && this.$scopedSlots.default('from baz') + } + } + + const toNamed = (syntax, name) => syntax[0] === '#' + ? `#${name}` // shorthand + : `${syntax}:${name}` // full syntax + + function runSuite(syntax) { + it('default slot', () => { + const vm = new Vue({ + template: `{{ foo }}
{{ foo }}
`, + components: { Foo } + }).$mount() + expect(vm.$el.innerHTML).toBe(`from foo default
from foo default
`) + }) + + it('nested default slots', () => { + const vm = new Vue({ + template: ` + + + + {{ foo }} | {{ bar }} | {{ baz }} + + + + `, + components: { Foo, Bar, Baz } + }).$mount() + expect(vm.$el.innerHTML.trim()).toBe(`from foo default | from bar | from baz`) + }) + + it('named slots', () => { + const vm = new Vue({ + template: ` + + + + + + `, + components: { Foo } + }).$mount() + expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo default from foo one from foo two`) + }) + + it('nested + named + default slots', () => { + const vm = new Vue({ + template: ` + + + + + `, + components: { Foo, Bar, Baz } + }).$mount() + expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from bar from foo two from baz`) + }) + + it('should warn v-slot usage on non-component elements', () => { + const vm = new Vue({ + template: `
` + }).$mount() + expect(`v-slot can only be used on components or