mirror of https://github.com/vuejs/core.git
Merge branch 'main' into feat/reactiveity-transform-shorthands
This commit is contained in:
commit
3a8cc1f211
|
@ -7,6 +7,7 @@ module.exports = {
|
||||||
sourceType: 'module'
|
sourceType: 'module'
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
'no-debugger': 'error',
|
||||||
'no-unused-vars': [
|
'no-unused-vars': [
|
||||||
'error',
|
'error',
|
||||||
// we are only using this rule to check for unused arguments since TS
|
// we are only using this rule to check for unused arguments since TS
|
||||||
|
@ -16,10 +17,11 @@ module.exports = {
|
||||||
// most of the codebase are expected to be env agnostic
|
// most of the codebase are expected to be env agnostic
|
||||||
'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals],
|
'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals],
|
||||||
// since we target ES2015 for baseline support, we need to forbid object
|
// since we target ES2015 for baseline support, we need to forbid object
|
||||||
// rest spread usage (both assign and destructure)
|
// rest spread usage in destructure as it compiles into a verbose helper.
|
||||||
|
// TS now compiles assignment spread into Object.assign() calls so that
|
||||||
|
// is allowed.
|
||||||
'no-restricted-syntax': [
|
'no-restricted-syntax': [
|
||||||
'error',
|
'error',
|
||||||
'ObjectExpression > SpreadElement',
|
|
||||||
'ObjectPattern > RestElement',
|
'ObjectPattern > RestElement',
|
||||||
'AwaitExpression'
|
'AwaitExpression'
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
name: "\U0001F41E Bug report"
|
||||||
|
description: Create a report to help us improve
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
**Before You Start...**
|
||||||
|
|
||||||
|
This form is only for submitting bug reports. If you have a usage question
|
||||||
|
or are unsure if this is really a bug, make sure to:
|
||||||
|
|
||||||
|
- Read the [docs](https://vuejs.org/)
|
||||||
|
- Ask on [Discord Chat](https://chat.vuejs.org/)
|
||||||
|
- Ask on [GitHub Discussions](https://github.com/vuejs/core/discussions)
|
||||||
|
- Look for / ask questions on [Stack Overflow](https://stackoverflow.com/questions/ask?tags=vue.js)
|
||||||
|
|
||||||
|
Also try to search for your issue - it may have already been answered or even fixed in the development branch.
|
||||||
|
However, if you find that an old, closed issue still persists in the latest version,
|
||||||
|
you should open a new issue using the form below instead of commenting on the old issue.
|
||||||
|
- type: input
|
||||||
|
id: reproduction-link
|
||||||
|
attributes:
|
||||||
|
label: Link to minimal reproduction
|
||||||
|
description: |
|
||||||
|
The easiest way to provide a reproduction is by showing the bug in [The SFC Playground](https://sfc.vuejs.org/).
|
||||||
|
If it cannot be reproduced in the playground and requires a proper build setup, try [StackBlitz](https://vite.new/vue).
|
||||||
|
If neither of these are suitable, you can always provide a GitHub reporistory.
|
||||||
|
|
||||||
|
The reproduction should be **minimal** - i.e. it should contain only the bare minimum amount of code needed
|
||||||
|
to show the bug. See [Bug Reproduction Guidelines](https://github.com/vuejs/core/blob/main/.github/bug-repro-guidelines.md) for more details.
|
||||||
|
|
||||||
|
Please do not just fill in a random link. The issue will be closed if no valid reproduction is provided.
|
||||||
|
placeholder: Reproduction Link
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps-to-reproduce
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: |
|
||||||
|
What do we need to do after opening your repro in order to make the bug happen? Clear and concise reproduction instructions are important for us to be able to triage your issue in a timely manner. Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
|
||||||
|
placeholder: Steps to reproduce
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: What is expected?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: actually-happening
|
||||||
|
attributes:
|
||||||
|
label: What is actually happening?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: system-info
|
||||||
|
attributes:
|
||||||
|
label: System Info
|
||||||
|
description: Output of `npx envinfo --system --npmPackages vue --binaries --browsers`
|
||||||
|
render: shell
|
||||||
|
placeholder: System, Binaries, Browsers
|
||||||
|
- type: textarea
|
||||||
|
id: additional-comments
|
||||||
|
attributes:
|
||||||
|
label: Any additional comments?
|
||||||
|
description: e.g. some background/context of how you ran into this bug.
|
|
@ -1,8 +1,11 @@
|
||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Create new issue
|
- name: Discord Chat
|
||||||
url: https://new-issue.vuejs.org/?repo=vuejs/core
|
url: https://chat.vuejs.org
|
||||||
about: Please use the following link to create a new issue.
|
about: Ask questions and discuss with other Vue users in real time.
|
||||||
|
- name: Questions & Discussions
|
||||||
|
url: https://github.com/vuejs/core/discussions
|
||||||
|
about: Use GitHub discussions for message-board style questions and discussions.
|
||||||
- name: Patreon
|
- name: Patreon
|
||||||
url: https://www.patreon.com/evanyou
|
url: https://www.patreon.com/evanyou
|
||||||
about: Love Vue.js? Please consider supporting us via Patreon.
|
about: Love Vue.js? Please consider supporting us via Patreon.
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
name: "\U0001F680 New feature proposal"
|
||||||
|
description: Suggest an idea for this project
|
||||||
|
labels: [":sparkles: feature request"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
**Before You Start...**
|
||||||
|
|
||||||
|
This form is only for submitting feature requests. If you have a usage question
|
||||||
|
or are unsure if this is really a bug, make sure to:
|
||||||
|
|
||||||
|
- Read the [docs](https://vuejs.org/)
|
||||||
|
- Ask on [Discord Chat](https://chat.vuejs.org/)
|
||||||
|
- Ask on [GitHub Discussions](https://github.com/vuejs/core/discussions)
|
||||||
|
- Look for / ask questions on [Stack Overflow](https://stackoverflow.com/questions/ask?tags=vue.js)
|
||||||
|
|
||||||
|
Also try to search for your issue - another user may have already requested something similar!
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: problem-description
|
||||||
|
attributes:
|
||||||
|
label: What problem does this feature solve?
|
||||||
|
description: |
|
||||||
|
Explain your use case, context, and rationale behind this feature request. More importantly, what is the **end user experience** you are trying to build that led to the need for this feature?
|
||||||
|
|
||||||
|
An important design goal of Vue is keeping the API surface small and straightforward. In general, we only consider adding new features that solve a problem that cannot be easily dealt with using existing APIs (i.e. not just an alternative way of doing things that can already be done). The problem should also be common enough to justify the addition.
|
||||||
|
placeholder: Problem description
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: proposed-API
|
||||||
|
attributes:
|
||||||
|
label: What does the proposed API look like?
|
||||||
|
description: |
|
||||||
|
Describe how you propose to solve the problem and provide code samples of how the API would work once implemented. Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format your code blocks.
|
||||||
|
placeholder: Steps to reproduce
|
||||||
|
validations:
|
||||||
|
required: true
|
|
@ -0,0 +1,29 @@
|
||||||
|
## About Bug Reproductions
|
||||||
|
|
||||||
|
A bug reproduction is a piece of code that can run and demonstrate how a bug can happen.
|
||||||
|
|
||||||
|
### Text is not enough
|
||||||
|
|
||||||
|
It's impossible to fix a bug from mere text descriptions. First, it's very difficult to precisely describe a technical problem while keeping it easy to follow; Second, the real cause may very well be something that you forgot to even mention. A reproduction is the only way that can reliably help us understand what is going on, so please provide one.
|
||||||
|
|
||||||
|
### A repro must be runnable
|
||||||
|
|
||||||
|
Screenshots or videos are NOT reproductions! They only show that the bug exists, but do not provide enough information on why it happens. Only runnable code provides the most complete context and allows us to properly debug the scenario. That said, in some cases videos/gifs can help explain interaction issues that are hard to describe in text.
|
||||||
|
|
||||||
|
### A repro should be minimal
|
||||||
|
|
||||||
|
Some users would give us a link to a real project and hope we can help them figure out what is wrong. We generally do not accept such requests because:
|
||||||
|
|
||||||
|
You are already familiar with your codebase, but we are not. It is extremely time-consuming to hunt a bug in a big and unfamiliar codebase.
|
||||||
|
|
||||||
|
The problematic behavior may very well be caused by your code rather than by a bug in Vue.
|
||||||
|
|
||||||
|
A minimal reproduction means it demonstrates the bug, and the bug only. It should only contain the bare minimum amount of code that can reliably cause the bug. Try your best to get rid of anything that aren't directly related to the problem.
|
||||||
|
|
||||||
|
### How to create a repro
|
||||||
|
|
||||||
|
For Vue 3 core reproductions, try reproducing it in [The SFC Playground](https://sfc.vuejs.org/).
|
||||||
|
|
||||||
|
If it cannot be reproduced in the playground and requires a proper build setup, try [StackBlitz](https://vite.new/vue).
|
||||||
|
|
||||||
|
If neither of these are suitable, you can always provide a GitHub repository.
|
|
@ -17,7 +17,7 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
|
||||||
|
|
||||||
## Pull Request Guidelines
|
## Pull Request Guidelines
|
||||||
|
|
||||||
- Checkout a topic branch from a base branch, e.g. `master`, and merge back against that branch.
|
- Checkout a topic branch from a base branch, e.g. `main`, and merge back against that branch.
|
||||||
|
|
||||||
- If adding a new feature:
|
- If adding a new feature:
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
You will need [Node.js](https://nodejs.org) **version 16+**, and [PNPM](https://pnpm.io).
|
You will need [Node.js](https://nodejs.org) **version 16+**, and [PNPM](https://pnpm.io) **version 7+**.
|
||||||
|
|
||||||
We also recommend installing [ni](https://github.com/antfu/ni) to help switching between repos using different package managers. `ni` also provides the handy `nr` command which running npm scripts easier.
|
We also recommend installing [ni](https://github.com/antfu/ni) to help switching between repos using different package managers. `ni` also provides the handy `nr` command which running npm scripts easier.
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
unit-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -15,7 +15,7 @@ jobs:
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2.0.1
|
uses: pnpm/action-setup@v2.0.1
|
||||||
with:
|
with:
|
||||||
version: 6.15.1
|
version: 7.0.1
|
||||||
|
|
||||||
- name: Set node version to 16
|
- name: Set node version to 16
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
|
@ -26,9 +26,9 @@ jobs:
|
||||||
- run: pnpm install
|
- run: pnpm install
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: pnpm run test
|
run: pnpm run test-unit
|
||||||
|
|
||||||
test-dts:
|
e2e-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -36,7 +36,7 @@ jobs:
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2.0.1
|
uses: pnpm/action-setup@v2.0.1
|
||||||
with:
|
with:
|
||||||
version: 6.15.1
|
version: 7.0.1
|
||||||
|
|
||||||
- name: Set node version to 16
|
- name: Set node version to 16
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
|
@ -46,6 +46,30 @@ jobs:
|
||||||
|
|
||||||
- run: pnpm install
|
- run: pnpm install
|
||||||
|
|
||||||
|
- name: Run e2e tests
|
||||||
|
run: pnpm run test-e2e
|
||||||
|
|
||||||
|
lint-and-test-dts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v2.0.1
|
||||||
|
with:
|
||||||
|
version: 7.0.1
|
||||||
|
|
||||||
|
- name: Set node version to 16
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- run: pnpm install
|
||||||
|
|
||||||
|
- name: Run eslint
|
||||||
|
run: pnpm run lint
|
||||||
|
|
||||||
- name: Run type declaration tests
|
- name: Run type declaration tests
|
||||||
run: pnpm run test-dts
|
run: pnpm run test-dts
|
||||||
|
|
||||||
|
@ -59,7 +83,7 @@ jobs:
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2.0.1
|
uses: pnpm/action-setup@v2.0.1
|
||||||
with:
|
with:
|
||||||
version: 6.15.1
|
version: 7.0.1
|
||||||
|
|
||||||
- name: Set node version to 16
|
- name: Set node version to 16
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
|
|
12
package.json
12
package.json
|
@ -9,7 +9,7 @@
|
||||||
"size-baseline": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler && cd packages/size-check && vite build && node brotli",
|
"size-baseline": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler && cd packages/size-check && vite build && node brotli",
|
||||||
"lint": "eslint --ext .ts packages/*/src/**.ts",
|
"lint": "eslint --ext .ts packages/*/src/**.ts",
|
||||||
"format": "prettier --write --parser typescript \"packages/**/*.ts?(x)\"",
|
"format": "prettier --write --parser typescript \"packages/**/*.ts?(x)\"",
|
||||||
"test": "run-s \"test-unit -- {@}\" \"test-e2e -- {@}\" --",
|
"test": "run-s \"test-unit {@}\" \"test-e2e {@}\"",
|
||||||
"test-unit": "jest --filter ./scripts/filter-unit.js",
|
"test-unit": "jest --filter ./scripts/filter-unit.js",
|
||||||
"test-e2e": "node scripts/build.js vue -f global -d && jest --filter ./scripts/filter-e2e.js --runInBand",
|
"test-e2e": "node scripts/build.js vue -f global -d && jest --filter ./scripts/filter-e2e.js --runInBand",
|
||||||
"test-dts": "node scripts/build.js shared reactivity runtime-core runtime-dom -dt -f esm-bundler && npm run test-dts-only",
|
"test-dts": "node scripts/build.js shared reactivity runtime-core runtime-dom -dt -f esm-bundler && npm run test-dts-only",
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
"release": "node scripts/release.js",
|
"release": "node scripts/release.js",
|
||||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||||
"dev-compiler": "run-p \"dev template-explorer\" serve",
|
"dev-compiler": "run-p \"dev template-explorer\" serve",
|
||||||
"dev-sfc": "run-p \"dev compiler-sfc -- -f esm-browser\" \"dev vue -- -if esm-bundler-runtime \" serve-sfc-playground",
|
"dev-sfc": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if esm-bundler-runtime \" serve-sfc-playground",
|
||||||
"serve-sfc-playground": "vite packages/sfc-playground --host",
|
"serve-sfc-playground": "vite packages/sfc-playground --host",
|
||||||
"serve": "serve",
|
"serve": "serve",
|
||||||
"open": "open http://localhost:5000/packages/template-explorer/local.html",
|
"open": "open http://localhost:5000/packages/template-explorer/local.html",
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
"@types/jest": "^27.0.1",
|
"@types/jest": "^27.0.1",
|
||||||
"@types/node": "^16.4.7",
|
"@types/node": "^16.4.7",
|
||||||
"@types/puppeteer": "^5.0.0",
|
"@types/puppeteer": "^5.0.0",
|
||||||
"@typescript-eslint/parser": "^4.1.1",
|
"@typescript-eslint/parser": "^5.23.0",
|
||||||
"@vue/reactivity": "workspace:*",
|
"@vue/reactivity": "workspace:*",
|
||||||
"@vue/runtime-core": "workspace:*",
|
"@vue/runtime-core": "workspace:*",
|
||||||
"@vue/runtime-dom": "workspace:*",
|
"@vue/runtime-dom": "workspace:*",
|
||||||
|
@ -89,9 +89,9 @@
|
||||||
"serve": "^12.0.0",
|
"serve": "^12.0.0",
|
||||||
"todomvc-app-css": "^2.3.0",
|
"todomvc-app-css": "^2.3.0",
|
||||||
"ts-jest": "^27.0.5",
|
"ts-jest": "^27.0.5",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^4.2.2",
|
"typescript": "^4.6.4",
|
||||||
"vite": "^2.9.0",
|
"vite": "^2.9.8",
|
||||||
"vue": "workspace:*",
|
"vue": "workspace:*",
|
||||||
"yorkie": "^2.0.0"
|
"yorkie": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ return function render(_ctx, _cache) {
|
||||||
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
|
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
|
||||||
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
|
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
|
||||||
_createTextVNode(\\"no\\")
|
_createTextVNode(\\"no\\")
|
||||||
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)),
|
], 64 /* STABLE_FRAGMENT */)),
|
||||||
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (value, index) => {
|
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (value, index) => {
|
||||||
return (_openBlock(), _createElementBlock(\\"div\\", null, [
|
return (_openBlock(), _createElementBlock(\\"div\\", null, [
|
||||||
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
|
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
|
||||||
|
@ -40,7 +40,7 @@ return function render(_ctx, _cache) {
|
||||||
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
|
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
|
||||||
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
|
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
|
||||||
_createTextVNode(\\"no\\")
|
_createTextVNode(\\"no\\")
|
||||||
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)),
|
], 64 /* STABLE_FRAGMENT */)),
|
||||||
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
|
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
|
||||||
return (_openBlock(), _createElementBlock(\\"div\\", null, [
|
return (_openBlock(), _createElementBlock(\\"div\\", null, [
|
||||||
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
|
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
|
||||||
|
@ -63,7 +63,7 @@ export function render(_ctx, _cache) {
|
||||||
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
|
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
|
||||||
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
|
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
|
||||||
_createTextVNode(\\"no\\")
|
_createTextVNode(\\"no\\")
|
||||||
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)),
|
], 64 /* STABLE_FRAGMENT */)),
|
||||||
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
|
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
|
||||||
return (_openBlock(), _createElementBlock(\\"div\\", null, [
|
return (_openBlock(), _createElementBlock(\\"div\\", null, [
|
||||||
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
|
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
|
||||||
|
|
|
@ -1037,7 +1037,7 @@ describe('compiler: parse', () => {
|
||||||
offset: 0
|
offset: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ns: 0,
|
ns: Namespaces.HTML,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
loc: {
|
loc: {
|
||||||
|
@ -1054,7 +1054,7 @@ describe('compiler: parse', () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
name: 'class',
|
name: 'class',
|
||||||
type: 6,
|
type: NodeTypes.ATTRIBUTE,
|
||||||
value: {
|
value: {
|
||||||
content: 'c',
|
content: 'c',
|
||||||
loc: {
|
loc: {
|
||||||
|
@ -1070,13 +1070,13 @@ describe('compiler: parse', () => {
|
||||||
offset: 11
|
offset: 11
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
type: 2
|
type: NodeTypes.TEXT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
tagType: 0,
|
tagType: ElementTypes.ELEMENT,
|
||||||
type: 1
|
type: NodeTypes.ELEMENT
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2023,7 +2023,7 @@ foo
|
||||||
isPreTag: tag => tag === 'pre'
|
isPreTag: tag => tag === 'pre'
|
||||||
})
|
})
|
||||||
const elementAfterPre = ast.children[1] as ElementNode
|
const elementAfterPre = ast.children[1] as ElementNode
|
||||||
// should not affect the <span> and condense its whitepsace inside
|
// should not affect the <span> and condense its whitespace inside
|
||||||
expect((elementAfterPre.children[0] as TextNode).content).toBe(` foo bar`)
|
expect((elementAfterPre.children[0] as TextNode).content).toBe(` foo bar`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ return function render(_ctx, _cache) {
|
||||||
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }))
|
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }))
|
||||||
: orNot
|
: orNot
|
||||||
? (_openBlock(), _createElementBlock(\\"p\\", { key: 1 }))
|
? (_openBlock(), _createElementBlock(\\"p\\", { key: 1 }))
|
||||||
: (_openBlock(), _createElementBlock(_Fragment, { key: 2 }, [\\"fine\\"], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
|
: (_openBlock(), _createElementBlock(_Fragment, { key: 2 }, [\\"fine\\"], 64 /* STABLE_FRAGMENT */))
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -80,7 +80,7 @@ describe('compiler: element transform', () => {
|
||||||
expect(root.components).toContain(`Foo`)
|
expect(root.components).toContain(`Foo`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('resolve implcitly self-referencing component', () => {
|
test('resolve implicitly self-referencing component', () => {
|
||||||
const { root } = parseWithElementTransform(`<Example/>`, {
|
const { root } = parseWithElementTransform(`<Example/>`, {
|
||||||
filename: `/foo/bar/Example.vue?vue&type=template`
|
filename: `/foo/bar/Example.vue?vue&type=template`
|
||||||
})
|
})
|
||||||
|
@ -807,6 +807,37 @@ describe('compiler: element transform', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(':style with array literal', () => {
|
||||||
|
const { node, root } = parseWithElementTransform(
|
||||||
|
`<div :style="[{ color: 'red' }]" />`,
|
||||||
|
{
|
||||||
|
nodeTransforms: [transformExpression, transformStyle, transformElement],
|
||||||
|
directiveTransforms: {
|
||||||
|
bind: transformBind
|
||||||
|
},
|
||||||
|
prefixIdentifiers: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(root.helpers).toContain(NORMALIZE_STYLE)
|
||||||
|
expect(node.props).toMatchObject({
|
||||||
|
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_PROPERTY,
|
||||||
|
key: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `style`,
|
||||||
|
isStatic: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: NORMALIZE_STYLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test(`props merging: class`, () => {
|
test(`props merging: class`, () => {
|
||||||
const { node, root } = parseWithElementTransform(
|
const { node, root } = parseWithElementTransform(
|
||||||
`<div class="foo" :class="{ bar: isBar }" />`,
|
`<div class="foo" :class="{ bar: isBar }" />`,
|
||||||
|
|
|
@ -259,6 +259,7 @@ export interface IfBranchNode extends Node {
|
||||||
condition: ExpressionNode | undefined // else
|
condition: ExpressionNode | undefined // else
|
||||||
children: TemplateChildNode[]
|
children: TemplateChildNode[]
|
||||||
userKey?: AttributeNode | DirectiveNode
|
userKey?: AttributeNode | DirectiveNode
|
||||||
|
isTemplateIf?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ForNode extends Node {
|
export interface ForNode extends Node {
|
||||||
|
|
|
@ -58,6 +58,8 @@ import { ImportItem } from './transform'
|
||||||
|
|
||||||
const PURE_ANNOTATION = `/*#__PURE__*/`
|
const PURE_ANNOTATION = `/*#__PURE__*/`
|
||||||
|
|
||||||
|
const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
|
||||||
|
|
||||||
type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
|
type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
|
||||||
|
|
||||||
export interface CodegenResult {
|
export interface CodegenResult {
|
||||||
|
@ -247,11 +249,7 @@ export function generate(
|
||||||
// function mode const declarations should be inside with block
|
// function mode const declarations should be inside with block
|
||||||
// also they should be renamed to avoid collision with user properties
|
// also they should be renamed to avoid collision with user properties
|
||||||
if (hasHelpers) {
|
if (hasHelpers) {
|
||||||
push(
|
push(`const { ${ast.helpers.map(aliasHelper).join(', ')} } = _Vue`)
|
||||||
`const { ${ast.helpers
|
|
||||||
.map(s => `${helperNameMap[s]}: _${helperNameMap[s]}`)
|
|
||||||
.join(', ')} } = _Vue`
|
|
||||||
)
|
|
||||||
push(`\n`)
|
push(`\n`)
|
||||||
newline()
|
newline()
|
||||||
}
|
}
|
||||||
|
@ -328,7 +326,6 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
|
||||||
!__BROWSER__ && ssr
|
!__BROWSER__ && ssr
|
||||||
? `require(${JSON.stringify(runtimeModuleName)})`
|
? `require(${JSON.stringify(runtimeModuleName)})`
|
||||||
: runtimeGlobalName
|
: runtimeGlobalName
|
||||||
const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
|
|
||||||
// Generate const declaration for helpers
|
// Generate const declaration for helpers
|
||||||
// In prefix mode, we place the const declaration at top so it's done
|
// In prefix mode, we place the const declaration at top so it's done
|
||||||
// only once; But if we not prefixing, we place the declaration inside the
|
// only once; But if we not prefixing, we place the declaration inside the
|
||||||
|
|
|
@ -59,6 +59,7 @@ export {
|
||||||
PropsExpression
|
PropsExpression
|
||||||
} from './transforms/transformElement'
|
} from './transforms/transformElement'
|
||||||
export { processSlotOutlet } from './transforms/transformSlotOutlet'
|
export { processSlotOutlet } from './transforms/transformSlotOutlet'
|
||||||
|
export { getConstantType } from './transforms/hoistStatic'
|
||||||
export { generateCodeFrame } from '@vue/shared'
|
export { generateCodeFrame } from '@vue/shared'
|
||||||
|
|
||||||
// v2 compat only
|
// v2 compat only
|
||||||
|
|
|
@ -97,6 +97,10 @@ export const enum BindingTypes {
|
||||||
* template expressions.
|
* template expressions.
|
||||||
*/
|
*/
|
||||||
SETUP_CONST = 'setup-const',
|
SETUP_CONST = 'setup-const',
|
||||||
|
/**
|
||||||
|
* a const binding that does not need `unref()`, but may be mutated.
|
||||||
|
*/
|
||||||
|
SETUP_REACTIVE_CONST = 'setup-reactive-const',
|
||||||
/**
|
/**
|
||||||
* a const binding that may be a ref.
|
* a const binding that may be a ref.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -352,7 +352,9 @@ function resolveSetupReference(name: string, context: TransformContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fromConst = checkType(BindingTypes.SETUP_CONST)
|
const fromConst =
|
||||||
|
checkType(BindingTypes.SETUP_CONST) ||
|
||||||
|
checkType(BindingTypes.SETUP_REACTIVE_CONST)
|
||||||
if (fromConst) {
|
if (fromConst) {
|
||||||
return context.inline
|
return context.inline
|
||||||
? // in inline mode, const setup bindings (e.g. imports) can be used as-is
|
? // in inline mode, const setup bindings (e.g. imports) can be used as-is
|
||||||
|
@ -765,10 +767,11 @@ export function buildProps(
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
styleProp &&
|
styleProp &&
|
||||||
!isStaticExp(styleProp.value) &&
|
|
||||||
// the static style is compiled into an object,
|
// the static style is compiled into an object,
|
||||||
// so use `hasStyleBinding` to ensure that it is a dynamic style binding
|
// so use `hasStyleBinding` to ensure that it is a dynamic style binding
|
||||||
(hasStyleBinding ||
|
(hasStyleBinding ||
|
||||||
|
(styleProp.value.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||||
|
styleProp.value.content.trim()[0] === `[`) ||
|
||||||
// v-bind:style and style both exist,
|
// v-bind:style and style both exist,
|
||||||
// v-bind:style with static literal object
|
// v-bind:style with static literal object
|
||||||
styleProp.value.type === NodeTypes.JS_ARRAY_EXPRESSION)
|
styleProp.value.type === NodeTypes.JS_ARRAY_EXPRESSION)
|
||||||
|
|
|
@ -24,7 +24,13 @@ import {
|
||||||
walkIdentifiers
|
walkIdentifiers
|
||||||
} from '../babelUtils'
|
} from '../babelUtils'
|
||||||
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
|
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
|
||||||
import { isGloballyWhitelisted, makeMap, hasOwn, isString } from '@vue/shared'
|
import {
|
||||||
|
isGloballyWhitelisted,
|
||||||
|
makeMap,
|
||||||
|
hasOwn,
|
||||||
|
isString,
|
||||||
|
genPropsAccessExp
|
||||||
|
} from '@vue/shared'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import {
|
import {
|
||||||
Node,
|
Node,
|
||||||
|
@ -122,7 +128,11 @@ export function processExpression(
|
||||||
const isDestructureAssignment =
|
const isDestructureAssignment =
|
||||||
parent && isInDestructureAssignment(parent, parentStack)
|
parent && isInDestructureAssignment(parent, parentStack)
|
||||||
|
|
||||||
if (type === BindingTypes.SETUP_CONST || localVars[raw]) {
|
if (
|
||||||
|
type === BindingTypes.SETUP_CONST ||
|
||||||
|
type === BindingTypes.SETUP_REACTIVE_CONST ||
|
||||||
|
localVars[raw]
|
||||||
|
) {
|
||||||
return raw
|
return raw
|
||||||
} else if (type === BindingTypes.SETUP_REF) {
|
} else if (type === BindingTypes.SETUP_REF) {
|
||||||
return `${raw}.value`
|
return `${raw}.value`
|
||||||
|
@ -181,17 +191,17 @@ export function processExpression(
|
||||||
} else if (type === BindingTypes.PROPS) {
|
} else if (type === BindingTypes.PROPS) {
|
||||||
// use __props which is generated by compileScript so in ts mode
|
// use __props which is generated by compileScript so in ts mode
|
||||||
// it gets correct type
|
// it gets correct type
|
||||||
return `__props.${raw}`
|
return genPropsAccessExp(raw)
|
||||||
} else if (type === BindingTypes.PROPS_ALIASED) {
|
} else if (type === BindingTypes.PROPS_ALIASED) {
|
||||||
// prop with a different local alias (from defineProps() destructure)
|
// prop with a different local alias (from defineProps() destructure)
|
||||||
return `__props.${bindingMetadata.__propsAliases![raw]}`
|
return genPropsAccessExp(bindingMetadata.__propsAliases![raw])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (type && type.startsWith('setup')) {
|
if (type && type.startsWith('setup')) {
|
||||||
// setup bindings in non-inline mode
|
// setup bindings in non-inline mode
|
||||||
return `$setup.${raw}`
|
return `$setup.${raw}`
|
||||||
} else if (type === BindingTypes.PROPS_ALIASED) {
|
} else if (type === BindingTypes.PROPS_ALIASED) {
|
||||||
return `$props.${bindingMetadata.__propsAliases![raw]}`
|
return `$props['${bindingMetadata.__propsAliases![raw]}']`
|
||||||
} else if (type) {
|
} else if (type) {
|
||||||
return `$${type}.${raw}`
|
return `$${type}.${raw}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,15 +209,14 @@ export function processIf(
|
||||||
}
|
}
|
||||||
|
|
||||||
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
||||||
|
const isTemplateIf = node.tagType === ElementTypes.TEMPLATE
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.IF_BRANCH,
|
type: NodeTypes.IF_BRANCH,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
condition: dir.name === 'else' ? undefined : dir.exp,
|
condition: dir.name === 'else' ? undefined : dir.exp,
|
||||||
children:
|
children: isTemplateIf && !findDir(node, 'for') ? node.children : [node],
|
||||||
node.tagType === ElementTypes.TEMPLATE && !findDir(node, 'for')
|
userKey: findProp(node, `key`),
|
||||||
? node.children
|
isTemplateIf
|
||||||
: [node],
|
|
||||||
userKey: findProp(node, `key`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,6 +273,7 @@ function createChildrenCodegenNode(
|
||||||
// the rest being comments
|
// the rest being comments
|
||||||
if (
|
if (
|
||||||
__DEV__ &&
|
__DEV__ &&
|
||||||
|
!branch.isTemplateIf &&
|
||||||
children.filter(c => c.type !== NodeTypes.COMMENT).length === 1
|
children.filter(c => c.type !== NodeTypes.COMMENT).length === 1
|
||||||
) {
|
) {
|
||||||
patchFlag |= PatchFlags.DEV_ROOT_FRAGMENT
|
patchFlag |= PatchFlags.DEV_ROOT_FRAGMENT
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
import { compile } from '../../src'
|
||||||
|
|
||||||
|
describe('Transition multi children warnings', () => {
|
||||||
|
function checkWarning(
|
||||||
|
template: string,
|
||||||
|
shouldWarn: boolean,
|
||||||
|
message = `<Transition> expects exactly one child element or component.`
|
||||||
|
) {
|
||||||
|
const spy = jest.fn()
|
||||||
|
compile(template.trim(), {
|
||||||
|
hoistStatic: true,
|
||||||
|
transformHoist: null,
|
||||||
|
onError: err => {
|
||||||
|
spy(err.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (shouldWarn) expect(spy).toHaveBeenCalledWith(message)
|
||||||
|
else expect(spy).not.toHaveBeenCalled()
|
||||||
|
}
|
||||||
|
|
||||||
|
test('warns if multiple children', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div>hey</div>
|
||||||
|
<div>hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('warns with v-for', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div v-for="i in items">hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('warns with multiple v-if + v-for', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div v-if="a" v-for="i in items">hey</div>
|
||||||
|
<div v-else v-for="i in items">hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('warns with template v-if', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<template v-if="ok"></template>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('warns with multiple templates', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<template v-if="a"></template>
|
||||||
|
<template v-else></template>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('warns if multiple children with v-if', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div v-if="one">hey</div>
|
||||||
|
<div v-if="other">hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not warn with regular element', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div>hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not warn with one single v-if', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div v-if="a">hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not warn with v-if v-else-if v-else', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div v-if="a">hey</div>
|
||||||
|
<div v-else-if="b">hey</div>
|
||||||
|
<div v-else>hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not warn with v-if v-else', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<div v-if="a">hey</div>
|
||||||
|
<div v-else>hey</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('inject persisted when child has v-show', () => {
|
||||||
|
expect(
|
||||||
|
compile(`
|
||||||
|
<transition>
|
||||||
|
<div v-show="ok" />
|
||||||
|
</transition>
|
||||||
|
`).code
|
||||||
|
).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('the v-if/else-if/else branches in Transition should ignore comments', () => {
|
||||||
|
expect(
|
||||||
|
compile(`
|
||||||
|
<transition>
|
||||||
|
<div v-if="a">hey</div>
|
||||||
|
<!-- this should be ignored -->
|
||||||
|
<div v-else-if="b">hey</div>
|
||||||
|
<!-- this should be ignored -->
|
||||||
|
<div v-else>
|
||||||
|
<p v-if="c"/>
|
||||||
|
<!-- this should not be ignored -->
|
||||||
|
<p v-else/>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
`).code
|
||||||
|
).toMatchSnapshot()
|
||||||
|
})
|
|
@ -1,6 +1,25 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`the v-if/else-if/else branchs in Transition should ignore comments 1`] = `
|
exports[`inject persisted when child has v-show 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
with (_ctx) {
|
||||||
|
const { vShow: _vShow, createElementVNode: _createElementVNode, withDirectives: _withDirectives, Transition: _Transition, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_Transition, { persisted: \\"\\" }, {
|
||||||
|
default: _withCtx(() => [
|
||||||
|
_withDirectives(_createElementVNode(\\"div\\", null, null, 512 /* NEED_PATCH */), [
|
||||||
|
[_vShow, ok]
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
_: 1 /* STABLE */
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`the v-if/else-if/else branches in Transition should ignore comments 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
return function render(_ctx, _cache) {
|
|
@ -32,3 +32,23 @@ return function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
|
return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`stringify static html stringify v-html 1`] = `
|
||||||
|
"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
|
||||||
|
|
||||||
|
const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"<pre data-type=\\\\\\"js\\\\\\"><code><span>show-it </span></code></pre><div class><span class>1</span><span class>2</span></div>\\", 2)
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
return _hoisted_1
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`stringify static html stringify v-text 1`] = `
|
||||||
|
"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
|
||||||
|
|
||||||
|
const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"<pre data-type=\\\\\\"js\\\\\\"><code><span>show-it </span></code></pre><div class><span class>1</span><span class>2</span></div>\\", 2)
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
return _hoisted_1
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
|
@ -433,4 +433,25 @@ describe('stringify static html', () => {
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #5439
|
||||||
|
test('stringify v-html', () => {
|
||||||
|
const { code } = compileWithStringify(`
|
||||||
|
<pre data-type="js"><code v-html="'<span>show-it </span>'"></code></pre>
|
||||||
|
<div class>
|
||||||
|
<span class>1</span><span class>2</span>
|
||||||
|
</div>`)
|
||||||
|
expect(code).toMatch(`<code><span>show-it </span></code>`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('stringify v-text', () => {
|
||||||
|
const { code } = compileWithStringify(`
|
||||||
|
<pre data-type="js"><code v-text="'<span>show-it </span>'"></code></pre>
|
||||||
|
<div class>
|
||||||
|
<span class>1</span><span class>2</span>
|
||||||
|
</div>`)
|
||||||
|
expect(code).toMatch(`<code><span>show-it </span></code>`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
import { compile } from '../../src'
|
|
||||||
|
|
||||||
describe('compiler warnings', () => {
|
|
||||||
describe('Transition', () => {
|
|
||||||
function checkWarning(
|
|
||||||
template: string,
|
|
||||||
shouldWarn: boolean,
|
|
||||||
message = `<Transition> expects exactly one child element or component.`
|
|
||||||
) {
|
|
||||||
const spy = jest.fn()
|
|
||||||
compile(template.trim(), {
|
|
||||||
hoistStatic: true,
|
|
||||||
transformHoist: null,
|
|
||||||
onError: err => {
|
|
||||||
spy(err.message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (shouldWarn) expect(spy).toHaveBeenCalledWith(message)
|
|
||||||
else expect(spy).not.toHaveBeenCalled()
|
|
||||||
}
|
|
||||||
|
|
||||||
test('warns if multiple children', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div>hey</div>
|
|
||||||
<div>hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('warns with v-for', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div v-for="i in items">hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('warns with multiple v-if + v-for', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div v-if="a" v-for="i in items">hey</div>
|
|
||||||
<div v-else v-for="i in items">hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('warns with template v-if', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<template v-if="ok"></template>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('warns with multiple templates', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<template v-if="a"></template>
|
|
||||||
<template v-else></template>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('warns if multiple children with v-if', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div v-if="one">hey</div>
|
|
||||||
<div v-if="other">hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('does not warn with regular element', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div>hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('does not warn with one single v-if', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div v-if="a">hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('does not warn with v-if v-else-if v-else', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div v-if="a">hey</div>
|
|
||||||
<div v-else-if="b">hey</div>
|
|
||||||
<div v-else>hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('does not warn with v-if v-else', () => {
|
|
||||||
checkWarning(
|
|
||||||
`
|
|
||||||
<transition>
|
|
||||||
<div v-if="a">hey</div>
|
|
||||||
<div v-else>hey</div>
|
|
||||||
</transition>
|
|
||||||
`,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('the v-if/else-if/else branchs in Transition should ignore comments', () => {
|
|
||||||
expect(
|
|
||||||
compile(`
|
|
||||||
<transition>
|
|
||||||
<div v-if="a">hey</div>
|
|
||||||
<!-- this should be ignored -->
|
|
||||||
<div v-else-if="b">hey</div>
|
|
||||||
<!-- this should be ignored -->
|
|
||||||
<div v-else>
|
|
||||||
<p v-if="c"/>
|
|
||||||
<!-- this should not be ignored -->
|
|
||||||
<p v-else/>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
`).code
|
|
||||||
).toMatchSnapshot()
|
|
||||||
})
|
|
|
@ -16,7 +16,7 @@ import { transformVText } from './transforms/vText'
|
||||||
import { transformModel } from './transforms/vModel'
|
import { transformModel } from './transforms/vModel'
|
||||||
import { transformOn } from './transforms/vOn'
|
import { transformOn } from './transforms/vOn'
|
||||||
import { transformShow } from './transforms/vShow'
|
import { transformShow } from './transforms/vShow'
|
||||||
import { warnTransitionChildren } from './transforms/warnTransitionChildren'
|
import { transformTransition } from './transforms/Transition'
|
||||||
import { stringifyStatic } from './transforms/stringifyStatic'
|
import { stringifyStatic } from './transforms/stringifyStatic'
|
||||||
import { ignoreSideEffectTags } from './transforms/ignoreSideEffectTags'
|
import { ignoreSideEffectTags } from './transforms/ignoreSideEffectTags'
|
||||||
import { extend } from '@vue/shared'
|
import { extend } from '@vue/shared'
|
||||||
|
@ -25,7 +25,7 @@ export { parserOptions }
|
||||||
|
|
||||||
export const DOMNodeTransforms: NodeTransform[] = [
|
export const DOMNodeTransforms: NodeTransform[] = [
|
||||||
transformStyle,
|
transformStyle,
|
||||||
...(__DEV__ ? [warnTransitionChildren] : [])
|
...(__DEV__ ? [transformTransition] : [])
|
||||||
]
|
]
|
||||||
|
|
||||||
export const DOMDirectiveTransforms: Record<string, DirectiveTransform> = {
|
export const DOMDirectiveTransforms: Record<string, DirectiveTransform> = {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
import { TRANSITION } from '../runtimeHelpers'
|
import { TRANSITION } from '../runtimeHelpers'
|
||||||
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
|
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
|
||||||
|
|
||||||
export const warnTransitionChildren: NodeTransform = (node, context) => {
|
export const transformTransition: NodeTransform = (node, context) => {
|
||||||
if (
|
if (
|
||||||
node.type === NodeTypes.ELEMENT &&
|
node.type === NodeTypes.ELEMENT &&
|
||||||
node.tagType === ElementTypes.COMPONENT
|
node.tagType === ElementTypes.COMPONENT
|
||||||
|
@ -16,7 +16,12 @@ export const warnTransitionChildren: NodeTransform = (node, context) => {
|
||||||
const component = context.isBuiltInComponent(node.tag)
|
const component = context.isBuiltInComponent(node.tag)
|
||||||
if (component === TRANSITION) {
|
if (component === TRANSITION) {
|
||||||
return () => {
|
return () => {
|
||||||
if (node.children.length && hasMultipleChildren(node)) {
|
if (!node.children.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// warn multiple transition children
|
||||||
|
if (hasMultipleChildren(node)) {
|
||||||
context.onError(
|
context.onError(
|
||||||
createDOMCompilerError(
|
createDOMCompilerError(
|
||||||
DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN,
|
DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN,
|
||||||
|
@ -28,6 +33,22 @@ export const warnTransitionChildren: NodeTransform = (node, context) => {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if it's s single child w/ v-show
|
||||||
|
// if yes, inject "persisted: true" to the transition props
|
||||||
|
const child = node.children[0]
|
||||||
|
if (child.type === NodeTypes.ELEMENT) {
|
||||||
|
for (const p of child.props) {
|
||||||
|
if (p.type === NodeTypes.DIRECTIVE && p.name === 'show') {
|
||||||
|
node.props.push({
|
||||||
|
type: NodeTypes.ATTRIBUTE,
|
||||||
|
name: 'persisted',
|
||||||
|
value: undefined,
|
||||||
|
loc: node.loc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -279,6 +279,7 @@ function stringifyElement(
|
||||||
context: TransformContext
|
context: TransformContext
|
||||||
): string {
|
): string {
|
||||||
let res = `<${node.tag}`
|
let res = `<${node.tag}`
|
||||||
|
let innerHTML = ''
|
||||||
for (let i = 0; i < node.props.length; i++) {
|
for (let i = 0; i < node.props.length; i++) {
|
||||||
const p = node.props[i]
|
const p = node.props[i]
|
||||||
if (p.type === NodeTypes.ATTRIBUTE) {
|
if (p.type === NodeTypes.ATTRIBUTE) {
|
||||||
|
@ -286,28 +287,38 @@ function stringifyElement(
|
||||||
if (p.value) {
|
if (p.value) {
|
||||||
res += `="${escapeHtml(p.value.content)}"`
|
res += `="${escapeHtml(p.value.content)}"`
|
||||||
}
|
}
|
||||||
} else if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind') {
|
} else if (p.type === NodeTypes.DIRECTIVE) {
|
||||||
const exp = p.exp as SimpleExpressionNode
|
if (p.name === 'bind') {
|
||||||
if (exp.content[0] === '_') {
|
const exp = p.exp as SimpleExpressionNode
|
||||||
// internally generated string constant references
|
if (exp.content[0] === '_') {
|
||||||
// e.g. imported URL strings via compiler-sfc transformAssetUrl plugin
|
// internally generated string constant references
|
||||||
res += ` ${(p.arg as SimpleExpressionNode).content}="__VUE_EXP_START__${
|
// e.g. imported URL strings via compiler-sfc transformAssetUrl plugin
|
||||||
exp.content
|
res += ` ${
|
||||||
}__VUE_EXP_END__"`
|
(p.arg as SimpleExpressionNode).content
|
||||||
continue
|
}="__VUE_EXP_START__${exp.content}__VUE_EXP_END__"`
|
||||||
}
|
continue
|
||||||
// constant v-bind, e.g. :foo="1"
|
|
||||||
let evaluated = evaluateConstant(exp)
|
|
||||||
if (evaluated != null) {
|
|
||||||
const arg = p.arg && (p.arg as SimpleExpressionNode).content
|
|
||||||
if (arg === 'class') {
|
|
||||||
evaluated = normalizeClass(evaluated)
|
|
||||||
} else if (arg === 'style') {
|
|
||||||
evaluated = stringifyStyle(normalizeStyle(evaluated))
|
|
||||||
}
|
}
|
||||||
res += ` ${(p.arg as SimpleExpressionNode).content}="${escapeHtml(
|
// constant v-bind, e.g. :foo="1"
|
||||||
evaluated
|
let evaluated = evaluateConstant(exp)
|
||||||
)}"`
|
if (evaluated != null) {
|
||||||
|
const arg = p.arg && (p.arg as SimpleExpressionNode).content
|
||||||
|
if (arg === 'class') {
|
||||||
|
evaluated = normalizeClass(evaluated)
|
||||||
|
} else if (arg === 'style') {
|
||||||
|
evaluated = stringifyStyle(normalizeStyle(evaluated))
|
||||||
|
}
|
||||||
|
res += ` ${(p.arg as SimpleExpressionNode).content}="${escapeHtml(
|
||||||
|
evaluated
|
||||||
|
)}"`
|
||||||
|
}
|
||||||
|
} else if (p.name === 'html') {
|
||||||
|
// #5439 v-html with constant value
|
||||||
|
// not sure why would anyone do this but it can happen
|
||||||
|
innerHTML = evaluateConstant(p.exp as SimpleExpressionNode)
|
||||||
|
} else if (p.name === 'text') {
|
||||||
|
innerHTML = escapeHtml(
|
||||||
|
toDisplayString(evaluateConstant(p.exp as SimpleExpressionNode))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,8 +326,12 @@ function stringifyElement(
|
||||||
res += ` ${context.scopeId}`
|
res += ` ${context.scopeId}`
|
||||||
}
|
}
|
||||||
res += `>`
|
res += `>`
|
||||||
for (let i = 0; i < node.children.length; i++) {
|
if (innerHTML) {
|
||||||
res += stringifyNode(node.children[i], context)
|
res += innerHTML
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
|
res += stringifyNode(node.children[i], context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!isVoidTag(node.tag)) {
|
if (!isVoidTag(node.tag)) {
|
||||||
res += `</${node.tag}>`
|
res += `</${node.tag}>`
|
||||||
|
@ -330,7 +345,7 @@ function stringifyElement(
|
||||||
// here, e.g. `{{ 1 }}` or `{{ 'foo' }}`
|
// here, e.g. `{{ 1 }}` or `{{ 'foo' }}`
|
||||||
// in addition, constant exps bail on presence of parens so you can't even
|
// in addition, constant exps bail on presence of parens so you can't even
|
||||||
// run JSFuck in here. But we mark it unsafe for security review purposes.
|
// run JSFuck in here. But we mark it unsafe for security review purposes.
|
||||||
// (see compiler-core/src/transformExpressions)
|
// (see compiler-core/src/transforms/transformExpression)
|
||||||
function evaluateConstant(exp: ExpressionNode): string {
|
function evaluateConstant(exp: ExpressionNode): string {
|
||||||
if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||||
return new Function(`return ${exp.content}`)()
|
return new Function(`return ${exp.content}`)()
|
||||||
|
|
|
@ -3,7 +3,8 @@ import {
|
||||||
createObjectProperty,
|
createObjectProperty,
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
TO_DISPLAY_STRING,
|
TO_DISPLAY_STRING,
|
||||||
createCallExpression
|
createCallExpression,
|
||||||
|
getConstantType
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
|
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
|
||||||
|
|
||||||
|
@ -25,11 +26,13 @@ export const transformVText: DirectiveTransform = (dir, node, context) => {
|
||||||
createObjectProperty(
|
createObjectProperty(
|
||||||
createSimpleExpression(`textContent`, true),
|
createSimpleExpression(`textContent`, true),
|
||||||
exp
|
exp
|
||||||
? createCallExpression(
|
? getConstantType(exp, context) > 0
|
||||||
context.helperString(TO_DISPLAY_STRING),
|
? exp
|
||||||
[exp],
|
: createCallExpression(
|
||||||
loc
|
context.helperString(TO_DISPLAY_STRING),
|
||||||
)
|
[exp],
|
||||||
|
loc
|
||||||
|
)
|
||||||
: createSimpleExpression('', true)
|
: createSimpleExpression('', true)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,63 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`SFC analyze <script> bindings auto name inference basic 1`] = `
|
||||||
|
"export default {
|
||||||
|
name: 'FooBar',
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
const a = 1
|
||||||
|
return { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC analyze <script> bindings auto name inference do not overwrite manual name (call) 1`] = `
|
||||||
|
"import { defineComponent } from 'vue'
|
||||||
|
const __default__ = defineComponent({
|
||||||
|
name: 'Baz'
|
||||||
|
})
|
||||||
|
|
||||||
|
export default /*#__PURE__*/Object.assign(__default__, {
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
const a = 1
|
||||||
|
return { a, defineComponent }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC analyze <script> bindings auto name inference do not overwrite manual name (object) 1`] = `
|
||||||
|
"const __default__ = {
|
||||||
|
name: 'Baz'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default /*#__PURE__*/Object.assign(__default__, {
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
const a = 1
|
||||||
|
return { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> <script> after <script setup> the script content not end with \`\\n\` 1`] = `
|
||||||
|
"const n = 1
|
||||||
|
import { x } from './x'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
|
||||||
|
|
||||||
|
return { n, x }
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`SFC compile <script setup> <script> and <script setup> co-usage script first 1`] = `
|
exports[`SFC compile <script setup> <script> and <script setup> co-usage script first 1`] = `
|
||||||
"import { x } from './x'
|
"import { x } from './x'
|
||||||
|
|
||||||
|
@ -22,7 +80,8 @@ return { n, x }
|
||||||
exports[`SFC compile <script setup> <script> and <script setup> co-usage script setup first 1`] = `
|
exports[`SFC compile <script setup> <script> and <script setup> co-usage script setup first 1`] = `
|
||||||
"export const n = 1
|
"export const n = 1
|
||||||
const __default__ = {}
|
const __default__ = {}
|
||||||
import { x } from './x'
|
|
||||||
|
import { x } from './x'
|
||||||
|
|
||||||
export default /*#__PURE__*/Object.assign(__default__, {
|
export default /*#__PURE__*/Object.assign(__default__, {
|
||||||
setup(__props, { expose }) {
|
setup(__props, { expose }) {
|
||||||
|
@ -42,7 +101,8 @@ exports[`SFC compile <script setup> <script> and <script setup> co-usage script
|
||||||
const __default__ = {
|
const __default__ = {
|
||||||
name: \\"test\\"
|
name: \\"test\\"
|
||||||
}
|
}
|
||||||
import { x } from './x'
|
|
||||||
|
import { x } from './x'
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
...__default__,
|
...__default__,
|
||||||
|
@ -63,6 +123,7 @@ exports[`SFC compile <script setup> <script> and <script setup> co-usage script
|
||||||
|
|
||||||
|
|
||||||
const __default__ = def
|
const __default__ = def
|
||||||
|
|
||||||
import { x } from './x'
|
import { x } from './x'
|
||||||
|
|
||||||
export default /*#__PURE__*/Object.assign(__default__, {
|
export default /*#__PURE__*/Object.assign(__default__, {
|
||||||
|
@ -136,6 +197,173 @@ return { }
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> async/await detection multiple \`if for\` nested statements 1`] = `
|
||||||
|
"import { withAsyncContext as _withAsyncContext } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
|
||||||
|
let __temp, __restore
|
||||||
|
if (ok) {
|
||||||
|
for (let a of [1,2,3]) {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => a)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (let a of [1,2,3]) {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => a)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
;(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => a)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> async/await detection multiple \`if while\` nested statements 1`] = `
|
||||||
|
"import { withAsyncContext as _withAsyncContext } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
|
||||||
|
let __temp, __restore
|
||||||
|
if (ok) {
|
||||||
|
while (d) {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 5)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
while (d) {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 5)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
;(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 6)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
if (c) {
|
||||||
|
let f = 10
|
||||||
|
10 + (
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 7)),
|
||||||
|
__temp = await __temp,
|
||||||
|
__restore(),
|
||||||
|
__temp
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 8)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
;(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 9)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> async/await detection multiple \`if\` nested statements 1`] = `
|
||||||
|
"import { withAsyncContext as _withAsyncContext } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
|
||||||
|
let __temp, __restore
|
||||||
|
if (ok) {
|
||||||
|
let a = 'foo'
|
||||||
|
;(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 0)),
|
||||||
|
__temp = await __temp,
|
||||||
|
__restore(),
|
||||||
|
__temp
|
||||||
|
) + (
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 1)),
|
||||||
|
__temp = await __temp,
|
||||||
|
__restore(),
|
||||||
|
__temp
|
||||||
|
)
|
||||||
|
;(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 2)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
} else if (a) {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 10)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
if (b) {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 0)),
|
||||||
|
__temp = await __temp,
|
||||||
|
__restore(),
|
||||||
|
__temp
|
||||||
|
) + (
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 1)),
|
||||||
|
__temp = await __temp,
|
||||||
|
__restore(),
|
||||||
|
__temp
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let a = 'foo'
|
||||||
|
;(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 2)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (b) {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 3)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
;(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 4)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
([__temp,__restore] = _withAsyncContext(() => 5)),
|
||||||
|
await __temp,
|
||||||
|
__restore()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`SFC compile <script setup> async/await detection nested await 1`] = `
|
exports[`SFC compile <script setup> async/await detection nested await 1`] = `
|
||||||
"import { withAsyncContext as _withAsyncContext } from 'vue'
|
"import { withAsyncContext as _withAsyncContext } from 'vue'
|
||||||
|
|
||||||
|
@ -492,6 +720,23 @@ return { props, a, emit }
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> dev mode import usage check TS annotations 1`] = `
|
||||||
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
import { Foo, Bar, Baz } from './x'
|
||||||
|
|
||||||
|
export default /*#__PURE__*/_defineComponent({
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
|
||||||
|
const a = 1
|
||||||
|
function b() {}
|
||||||
|
|
||||||
|
return { a, b, Baz }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`SFC compile <script setup> dev mode import usage check attribute expressions 1`] = `
|
exports[`SFC compile <script setup> dev mode import usage check attribute expressions 1`] = `
|
||||||
"import { defineComponent as _defineComponent } from 'vue'
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
import { bar, baz } from './x'
|
import { bar, baz } from './x'
|
||||||
|
@ -717,6 +962,7 @@ exports[`SFC compile <script setup> inlineTemplate mode avoid unref() when neces
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import Foo, { bar } from './Foo.vue'
|
import Foo, { bar } from './Foo.vue'
|
||||||
import other from './util'
|
import other from './util'
|
||||||
|
import * as tree from './tree'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setup(__props) {
|
setup(__props) {
|
||||||
|
@ -735,7 +981,8 @@ return (_ctx, _cache) => {
|
||||||
]),
|
]),
|
||||||
_: 1 /* STABLE */
|
_: 1 /* STABLE */
|
||||||
}),
|
}),
|
||||||
_createElementVNode(\\"div\\", { onClick: fn }, _toDisplayString(count.value) + \\" \\" + _toDisplayString(constant) + \\" \\" + _toDisplayString(_unref(maybe)) + \\" \\" + _toDisplayString(_unref(lett)) + \\" \\" + _toDisplayString(_unref(other)), 1 /* TEXT */)
|
_createElementVNode(\\"div\\", { onClick: fn }, _toDisplayString(count.value) + \\" \\" + _toDisplayString(constant) + \\" \\" + _toDisplayString(_unref(maybe)) + \\" \\" + _toDisplayString(_unref(lett)) + \\" \\" + _toDisplayString(_unref(other)), 1 /* TEXT */),
|
||||||
|
_createTextVNode(\\" \\" + _toDisplayString(tree.foo()), 1 /* TEXT */)
|
||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1006,7 +1253,8 @@ exports[`SFC compile <script setup> should expose top level declarations 1`] = `
|
||||||
const bb = 2
|
const bb = 2
|
||||||
function cc() {}
|
function cc() {}
|
||||||
class dd {}
|
class dd {}
|
||||||
import { x } from './x'
|
|
||||||
|
import { x } from './x'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setup(__props, { expose }) {
|
setup(__props, { expose }) {
|
||||||
|
|
|
@ -65,7 +65,7 @@ exports[`sfc props transform default values w/ runtime declaration 1`] = `
|
||||||
export default {
|
export default {
|
||||||
props: _mergeDefaults(['foo', 'bar'], {
|
props: _mergeDefaults(['foo', 'bar'], {
|
||||||
foo: 1,
|
foo: 1,
|
||||||
bar: () => {}
|
bar: () => ({})
|
||||||
}),
|
}),
|
||||||
setup(__props) {
|
setup(__props) {
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ exports[`sfc props transform default values w/ type declaration 1`] = `
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
props: {
|
props: {
|
||||||
foo: { type: Number, required: false, default: 1 },
|
foo: { type: Number, required: false, default: 1 },
|
||||||
bar: { type: Object, required: false, default: () => {} }
|
bar: { type: Object, required: false, default: () => ({}) }
|
||||||
},
|
},
|
||||||
setup(__props: any) {
|
setup(__props: any) {
|
||||||
|
|
||||||
|
@ -101,11 +101,11 @@ exports[`sfc props transform default values w/ type declaration, prod mode 1`] =
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
props: {
|
props: {
|
||||||
foo: { default: 1 },
|
foo: { default: 1 },
|
||||||
bar: { default: () => {} },
|
bar: { default: () => ({}) },
|
||||||
baz: null,
|
baz: null,
|
||||||
boola: { type: Boolean },
|
boola: { type: Boolean },
|
||||||
boolb: { type: [Boolean, Number] },
|
boolb: { type: [Boolean, Number] },
|
||||||
func: { type: Function, default: () => () => {} }
|
func: { type: Function, default: () => (() => {}) }
|
||||||
},
|
},
|
||||||
setup(__props: any) {
|
setup(__props: any) {
|
||||||
|
|
||||||
|
@ -134,6 +134,25 @@ return () => {}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`sfc props transform non-identifier prop names 1`] = `
|
||||||
|
"import { toDisplayString as _toDisplayString } from \\"vue\\"
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: { 'foo.bar': Function },
|
||||||
|
setup(__props) {
|
||||||
|
|
||||||
|
|
||||||
|
let x = __props[\\"foo.bar\\"]
|
||||||
|
|
||||||
|
return (_ctx, _cache) => {
|
||||||
|
return _toDisplayString(__props[\\"foo.bar\\"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`sfc props transform rest spread 1`] = `
|
exports[`sfc props transform rest spread 1`] = `
|
||||||
"import { createPropsRestProxy as _createPropsRestProxy } from 'vue'
|
"import { createPropsRestProxy as _createPropsRestProxy } from 'vue'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,51 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`should not hoist srcset URLs in SSR mode 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode } from \\"vue\\"
|
||||||
|
import { ssrRenderAttr as _ssrRenderAttr, ssrRenderComponent as _ssrRenderComponent } from \\"vue/server-renderer\\"
|
||||||
|
import _imports_0 from './img/foo.svg'
|
||||||
|
import _imports_1 from './img/bar.svg'
|
||||||
|
|
||||||
|
|
||||||
|
export function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||||
|
const _component_router_link = _resolveComponent(\\"router-link\\")
|
||||||
|
|
||||||
|
_push(\`<!--[--><picture><source\${
|
||||||
|
_ssrRenderAttr(\\"srcset\\", _imports_0)
|
||||||
|
}><img\${
|
||||||
|
_ssrRenderAttr(\\"src\\", _imports_0)
|
||||||
|
}></picture>\`)
|
||||||
|
_push(_ssrRenderComponent(_component_router_link, null, {
|
||||||
|
default: _withCtx((_, _push, _parent, _scopeId) => {
|
||||||
|
if (_push) {
|
||||||
|
_push(\`<picture\${
|
||||||
|
_scopeId
|
||||||
|
}><source\${
|
||||||
|
_ssrRenderAttr(\\"srcset\\", _imports_1)
|
||||||
|
}\${
|
||||||
|
_scopeId
|
||||||
|
}><img\${
|
||||||
|
_ssrRenderAttr(\\"src\\", _imports_1)
|
||||||
|
}\${
|
||||||
|
_scopeId
|
||||||
|
}></picture>\`)
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
_createVNode(\\"picture\\", null, [
|
||||||
|
_createVNode(\\"source\\", {
|
||||||
|
srcset: _imports_1
|
||||||
|
}),
|
||||||
|
_createVNode(\\"img\\", { src: _imports_1 })
|
||||||
|
])
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_: 1 /* STABLE */
|
||||||
|
}, _parent))
|
||||||
|
_push(\`<!--]-->\`)
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`source map 1`] = `
|
exports[`source map 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"mappings": ";;;wBACE,oBAA8B;IAAzB,oBAAmB,4BAAbA,WAAM",
|
"mappings": ";;;wBACE,oBAA8B;IAAzB,oBAAmB,4BAAbA,WAAM",
|
||||||
|
|
|
@ -38,11 +38,13 @@ import _imports_0 from '@svg/file.svg'
|
||||||
|
|
||||||
|
|
||||||
const _hoisted_1 = _imports_0 + '#fragment'
|
const _hoisted_1 = _imports_0 + '#fragment'
|
||||||
|
const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"use\\", { href: _hoisted_1 }, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_3 = /*#__PURE__*/_createElementVNode(\\"use\\", { href: _hoisted_1 }, null, -1 /* HOISTED */)
|
||||||
|
|
||||||
export function render(_ctx, _cache) {
|
export function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||||
_createElementVNode(\\"use\\", { href: _hoisted_1 }),
|
_hoisted_2,
|
||||||
_createElementVNode(\\"use\\", { href: _hoisted_1 })
|
_hoisted_3
|
||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`compiler sfc: transform srcset srcset w/ explicit base option 1`] = `
|
||||||
|
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
||||||
|
import _imports_0 from '@/logo.png'
|
||||||
|
|
||||||
|
|
||||||
|
const _hoisted_1 = _imports_0 + ', ' + _imports_0 + ' 2x'
|
||||||
|
const _hoisted_2 = _imports_0 + ' 1x, ' + \\"/foo/logo.png\\" + ' 2x'
|
||||||
|
const _hoisted_3 = /*#__PURE__*/_createElementVNode(\\"img\\", { srcset: _hoisted_1 }, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_4 = /*#__PURE__*/_createElementVNode(\\"img\\", { srcset: _hoisted_2 }, null, -1 /* HOISTED */)
|
||||||
|
|
||||||
|
export function render(_ctx, _cache) {
|
||||||
|
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||||
|
_hoisted_3,
|
||||||
|
_hoisted_4
|
||||||
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler sfc: transform srcset transform srcset 1`] = `
|
exports[`compiler sfc: transform srcset transform srcset 1`] = `
|
||||||
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
||||||
import _imports_0 from './logo.png'
|
import _imports_0 from './logo.png'
|
||||||
|
@ -13,57 +31,69 @@ const _hoisted_5 = _imports_0 + ' 2x, ' + _imports_0
|
||||||
const _hoisted_6 = _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
const _hoisted_6 = _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
||||||
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
||||||
const _hoisted_8 = \\"/logo.png\\" + ', ' + _imports_0 + ' 2x'
|
const _hoisted_8 = \\"/logo.png\\" + ', ' + _imports_0 + ' 2x'
|
||||||
|
const _hoisted_9 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_10 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_1
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_11 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_2
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_12 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_3
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_13 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_4
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_14 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_5
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_15 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_6
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_16 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_7
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_17 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: \\"/logo.png, /logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_18 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"https://example.com/logo.png\\",
|
||||||
|
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_19 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: _hoisted_8
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_20 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"data:image/png;base64,i\\",
|
||||||
|
srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
|
||||||
export function render(_ctx, _cache) {
|
export function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_9,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_10,
|
||||||
srcset: \\"\\"
|
_hoisted_11,
|
||||||
}),
|
_hoisted_12,
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_13,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_14,
|
||||||
srcset: _hoisted_1
|
_hoisted_15,
|
||||||
}),
|
_hoisted_16,
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_17,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_18,
|
||||||
srcset: _hoisted_2
|
_hoisted_19,
|
||||||
}),
|
_hoisted_20
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_3
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_4
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_5
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_6
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_7
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"/logo.png\\",
|
|
||||||
srcset: \\"/logo.png, /logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"https://example.com/logo.png\\",
|
|
||||||
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"/logo.png\\",
|
|
||||||
srcset: _hoisted_8
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"data:image/png;base64,i\\",
|
|
||||||
srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
|
|
||||||
})
|
|
||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -71,56 +101,69 @@ export function render(_ctx, _cache) {
|
||||||
exports[`compiler sfc: transform srcset transform srcset w/ base 1`] = `
|
exports[`compiler sfc: transform srcset transform srcset w/ base 1`] = `
|
||||||
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
||||||
|
|
||||||
|
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"/foo/logo.png\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_3 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"/foo/logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_4 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"/foo/logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_5 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"/foo/logo.png, /foo/logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_6 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"/foo/logo.png 2x, /foo/logo.png\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_7 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"/foo/logo.png 2x, /foo/logo.png 3x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_8 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"/foo/logo.png, /foo/logo.png 2x, /foo/logo.png 3x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_9 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: \\"/logo.png, /logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_10 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"https://example.com/logo.png\\",
|
||||||
|
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_11 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: \\"/logo.png, /foo/logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_12 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"data:image/png;base64,i\\",
|
||||||
|
srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
|
||||||
export function render(_ctx, _cache) {
|
export function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_1,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_2,
|
||||||
srcset: \\"\\"
|
_hoisted_3,
|
||||||
}),
|
_hoisted_4,
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_5,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_6,
|
||||||
srcset: \\"/foo/logo.png\\"
|
_hoisted_7,
|
||||||
}),
|
_hoisted_8,
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_9,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_10,
|
||||||
srcset: \\"/foo/logo.png 2x\\"
|
_hoisted_11,
|
||||||
}),
|
_hoisted_12
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: \\"/foo/logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: \\"/foo/logo.png, /foo/logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: \\"/foo/logo.png 2x, /foo/logo.png\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: \\"/foo/logo.png 2x, /foo/logo.png 3x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: \\"/foo/logo.png, /foo/logo.png 2x, /foo/logo.png 3x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"/logo.png\\",
|
|
||||||
srcset: \\"/logo.png, /logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"https://example.com/logo.png\\",
|
|
||||||
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"/logo.png\\",
|
|
||||||
srcset: \\"/logo.png, /foo/logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"data:image/png;base64,i\\",
|
|
||||||
srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
|
|
||||||
})
|
|
||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -140,57 +183,69 @@ const _hoisted_6 = _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
||||||
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
||||||
const _hoisted_8 = _imports_1 + ', ' + _imports_1 + ' 2x'
|
const _hoisted_8 = _imports_1 + ', ' + _imports_1 + ' 2x'
|
||||||
const _hoisted_9 = _imports_1 + ', ' + _imports_0 + ' 2x'
|
const _hoisted_9 = _imports_1 + ', ' + _imports_0 + ' 2x'
|
||||||
|
const _hoisted_10 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: \\"\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_11 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_1
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_12 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_2
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_13 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_3
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_14 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_4
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_15 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_5
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_16 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_6
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_17 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"./logo.png\\",
|
||||||
|
srcset: _hoisted_7
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_18 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: _hoisted_8
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_19 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"https://example.com/logo.png\\",
|
||||||
|
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_20 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: _hoisted_9
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
const _hoisted_21 = /*#__PURE__*/_createElementVNode(\\"img\\", {
|
||||||
|
src: \\"data:image/png;base64,i\\",
|
||||||
|
srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
|
||||||
|
}, null, -1 /* HOISTED */)
|
||||||
|
|
||||||
export function render(_ctx, _cache) {
|
export function render(_ctx, _cache) {
|
||||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_10,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_11,
|
||||||
srcset: \\"\\"
|
_hoisted_12,
|
||||||
}),
|
_hoisted_13,
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_14,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_15,
|
||||||
srcset: _hoisted_1
|
_hoisted_16,
|
||||||
}),
|
_hoisted_17,
|
||||||
_createElementVNode(\\"img\\", {
|
_hoisted_18,
|
||||||
src: \\"./logo.png\\",
|
_hoisted_19,
|
||||||
srcset: _hoisted_2
|
_hoisted_20,
|
||||||
}),
|
_hoisted_21
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_3
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_4
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_5
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_6
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"./logo.png\\",
|
|
||||||
srcset: _hoisted_7
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"/logo.png\\",
|
|
||||||
srcset: _hoisted_8
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"https://example.com/logo.png\\",
|
|
||||||
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"/logo.png\\",
|
|
||||||
srcset: _hoisted_9
|
|
||||||
}),
|
|
||||||
_createElementVNode(\\"img\\", {
|
|
||||||
src: \\"data:image/png;base64,i\\",
|
|
||||||
srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
|
|
||||||
})
|
|
||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -64,11 +64,11 @@ const bar = 1
|
||||||
`)
|
`)
|
||||||
// should generate working code
|
// should generate working code
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
// should anayze bindings
|
// should analyze bindings
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
foo: BindingTypes.PROPS,
|
foo: BindingTypes.PROPS,
|
||||||
bar: BindingTypes.SETUP_CONST,
|
bar: BindingTypes.SETUP_CONST,
|
||||||
props: BindingTypes.SETUP_CONST
|
props: BindingTypes.SETUP_REACTIVE_CONST
|
||||||
})
|
})
|
||||||
|
|
||||||
// should remove defineOptions import and call
|
// should remove defineOptions import and call
|
||||||
|
@ -168,6 +168,16 @@ defineExpose({ foo: 123 })
|
||||||
expect(content).toMatch(/\bexpose\(\{ foo: 123 \}\)/)
|
expect(content).toMatch(/\bexpose\(\{ foo: 123 \}\)/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('<script> after <script setup> the script content not end with `\\n`',() => {
|
||||||
|
const { content } = compile(`
|
||||||
|
<script setup>
|
||||||
|
import { x } from './x'
|
||||||
|
</script>
|
||||||
|
<script>const n = 1</script>
|
||||||
|
`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
describe('<script> and <script setup> co-usage', () => {
|
describe('<script> and <script setup> co-usage', () => {
|
||||||
test('script first', () => {
|
test('script first', () => {
|
||||||
const { content } = compile(`
|
const { content } = compile(`
|
||||||
|
@ -403,7 +413,7 @@ defineExpose({ foo: 123 })
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
})
|
})
|
||||||
|
|
||||||
// #4340 interpolations in tempalte strings
|
// #4340 interpolations in template strings
|
||||||
test('js template string interpolations', () => {
|
test('js template string interpolations', () => {
|
||||||
const { content } = compile(`
|
const { content } = compile(`
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -432,6 +442,23 @@ defineExpose({ foo: 123 })
|
||||||
expect(content).toMatch(`return { FooBaz, Last }`)
|
expect(content).toMatch(`return { FooBaz, Last }`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('TS annotations', () => {
|
||||||
|
const { content } = compile(`
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Foo, Bar, Baz } from './x'
|
||||||
|
const a = 1
|
||||||
|
function b() {}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
{{ a as Foo }}
|
||||||
|
{{ b<Bar>() }}
|
||||||
|
{{ Baz }}
|
||||||
|
</template>
|
||||||
|
`)
|
||||||
|
expect(content).toMatch(`return { a, b, Baz }`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('inlineTemplate mode', () => {
|
describe('inlineTemplate mode', () => {
|
||||||
|
@ -502,6 +529,7 @@ defineExpose({ foo: 123 })
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import Foo, { bar } from './Foo.vue'
|
import Foo, { bar } from './Foo.vue'
|
||||||
import other from './util'
|
import other from './util'
|
||||||
|
import * as tree from './tree'
|
||||||
const count = ref(0)
|
const count = ref(0)
|
||||||
const constant = {}
|
const constant = {}
|
||||||
const maybe = foo()
|
const maybe = foo()
|
||||||
|
@ -511,6 +539,7 @@ defineExpose({ foo: 123 })
|
||||||
<template>
|
<template>
|
||||||
<Foo>{{ bar }}</Foo>
|
<Foo>{{ bar }}</Foo>
|
||||||
<div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
|
<div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
|
||||||
|
{{ tree.foo() }}
|
||||||
</template>
|
</template>
|
||||||
`,
|
`,
|
||||||
{ inlineTemplate: true }
|
{ inlineTemplate: true }
|
||||||
|
@ -529,6 +558,8 @@ defineExpose({ foo: 123 })
|
||||||
expect(content).toMatch(`unref(maybe)`)
|
expect(content).toMatch(`unref(maybe)`)
|
||||||
// should unref() on let bindings
|
// should unref() on let bindings
|
||||||
expect(content).toMatch(`unref(lett)`)
|
expect(content).toMatch(`unref(lett)`)
|
||||||
|
// no need to unref namespace import (this also preserves tree-shaking)
|
||||||
|
expect(content).toMatch(`tree.foo()`)
|
||||||
// no need to unref function declarations
|
// no need to unref function declarations
|
||||||
expect(content).toMatch(`{ onClick: fn }`)
|
expect(content).toMatch(`{ onClick: fn }`)
|
||||||
// no need to mark constant fns in patch flag
|
// no need to mark constant fns in patch flag
|
||||||
|
@ -1164,6 +1195,59 @@ const emit = defineEmits(['a', 'b'])
|
||||||
assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
|
assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('multiple `if` nested statements', () => {
|
||||||
|
assertAwaitDetection(`if (ok) {
|
||||||
|
let a = 'foo'
|
||||||
|
await 0 + await 1
|
||||||
|
await 2
|
||||||
|
} else if (a) {
|
||||||
|
await 10
|
||||||
|
if (b) {
|
||||||
|
await 0 + await 1
|
||||||
|
} else {
|
||||||
|
let a = 'foo'
|
||||||
|
await 2
|
||||||
|
}
|
||||||
|
if (b) {
|
||||||
|
await 3
|
||||||
|
await 4
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await 5
|
||||||
|
}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('multiple `if while` nested statements', () => {
|
||||||
|
assertAwaitDetection(`if (ok) {
|
||||||
|
while (d) {
|
||||||
|
await 5
|
||||||
|
}
|
||||||
|
while (d) {
|
||||||
|
await 5
|
||||||
|
await 6
|
||||||
|
if (c) {
|
||||||
|
let f = 10
|
||||||
|
10 + await 7
|
||||||
|
} else {
|
||||||
|
await 8
|
||||||
|
await 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('multiple `if for` nested statements', () => {
|
||||||
|
assertAwaitDetection(`if (ok) {
|
||||||
|
for (let a of [1,2,3]) {
|
||||||
|
await a
|
||||||
|
}
|
||||||
|
for (let a of [1,2,3]) {
|
||||||
|
await a
|
||||||
|
await a
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
})
|
||||||
|
|
||||||
test('should ignore await inside functions', () => {
|
test('should ignore await inside functions', () => {
|
||||||
// function declaration
|
// function declaration
|
||||||
assertAwaitDetection(`async function foo() { await bar }`, false)
|
assertAwaitDetection(`async function foo() { await bar }`, false)
|
||||||
|
@ -1550,4 +1634,59 @@ describe('SFC analyze <script> bindings', () => {
|
||||||
foo: BindingTypes.PROPS
|
foo: BindingTypes.PROPS
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('auto name inference', () => {
|
||||||
|
test('basic', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`<script setup>const a = 1</script>
|
||||||
|
<template>{{ a }}</template>`,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
filename: 'FooBar.vue'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(content).toMatch(`export default {
|
||||||
|
name: 'FooBar'`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('do not overwrite manual name (object)', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`<script>
|
||||||
|
export default {
|
||||||
|
name: 'Baz'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup>const a = 1</script>
|
||||||
|
<template>{{ a }}</template>`,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
filename: 'FooBar.vue'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(content).not.toMatch(`name: 'FooBar'`)
|
||||||
|
expect(content).toMatch(`name: 'Baz'`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('do not overwrite manual name (call)', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Baz'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<script setup>const a = 1</script>
|
||||||
|
<template>{{ a }}</template>`,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
filename: 'FooBar.vue'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(content).not.toMatch(`name: 'FooBar'`)
|
||||||
|
expect(content).toMatch(`name: 'Baz'`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -59,7 +59,7 @@ describe('sfc props transform', () => {
|
||||||
// function
|
// function
|
||||||
expect(content).toMatch(`props: _mergeDefaults(['foo', 'bar'], {
|
expect(content).toMatch(`props: _mergeDefaults(['foo', 'bar'], {
|
||||||
foo: 1,
|
foo: 1,
|
||||||
bar: () => {}
|
bar: () => ({})
|
||||||
})`)
|
})`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
})
|
})
|
||||||
|
@ -74,7 +74,7 @@ describe('sfc props transform', () => {
|
||||||
// function
|
// function
|
||||||
expect(content).toMatch(`props: {
|
expect(content).toMatch(`props: {
|
||||||
foo: { type: Number, required: false, default: 1 },
|
foo: { type: Number, required: false, default: 1 },
|
||||||
bar: { type: Object, required: false, default: () => {} }
|
bar: { type: Object, required: false, default: () => ({}) }
|
||||||
}`)
|
}`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
})
|
})
|
||||||
|
@ -92,11 +92,11 @@ describe('sfc props transform', () => {
|
||||||
// function
|
// function
|
||||||
expect(content).toMatch(`props: {
|
expect(content).toMatch(`props: {
|
||||||
foo: { default: 1 },
|
foo: { default: 1 },
|
||||||
bar: { default: () => {} },
|
bar: { default: () => ({}) },
|
||||||
baz: null,
|
baz: null,
|
||||||
boola: { type: Boolean },
|
boola: { type: Boolean },
|
||||||
boolb: { type: [Boolean, Number] },
|
boolb: { type: [Boolean, Number] },
|
||||||
func: { type: Function, default: () => () => {} }
|
func: { type: Function, default: () => (() => {}) }
|
||||||
}`)
|
}`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
})
|
})
|
||||||
|
@ -127,6 +127,28 @@ describe('sfc props transform', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #5425
|
||||||
|
test('non-identifier prop names', () => {
|
||||||
|
const { content, bindings } = compile(`
|
||||||
|
<script setup>
|
||||||
|
const { 'foo.bar': fooBar } = defineProps({ 'foo.bar': Function })
|
||||||
|
let x = fooBar
|
||||||
|
</script>
|
||||||
|
<template>{{ fooBar }}</template>
|
||||||
|
`)
|
||||||
|
expect(content).toMatch(`x = __props["foo.bar"]`)
|
||||||
|
expect(content).toMatch(`toDisplayString(__props["foo.bar"])`)
|
||||||
|
assertCode(content)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
x: BindingTypes.SETUP_LET,
|
||||||
|
'foo.bar': BindingTypes.PROPS,
|
||||||
|
fooBar: BindingTypes.PROPS_ALIASED,
|
||||||
|
__propsAliases: {
|
||||||
|
fooBar: 'foo.bar'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('rest spread', () => {
|
test('rest spread', () => {
|
||||||
const { content, bindings } = compile(`
|
const { content, bindings } = compile(`
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -141,7 +163,7 @@ describe('sfc props transform', () => {
|
||||||
foo: BindingTypes.PROPS,
|
foo: BindingTypes.PROPS,
|
||||||
bar: BindingTypes.PROPS,
|
bar: BindingTypes.PROPS,
|
||||||
baz: BindingTypes.PROPS,
|
baz: BindingTypes.PROPS,
|
||||||
rest: BindingTypes.SETUP_CONST
|
rest: BindingTypes.SETUP_REACTIVE_CONST
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -153,3 +153,24 @@ test('should generate the correct imports expression', () => {
|
||||||
expect(code).toMatch(`_ssrRenderAttr(\"src\", _imports_1)`)
|
expect(code).toMatch(`_ssrRenderAttr(\"src\", _imports_1)`)
|
||||||
expect(code).toMatch(`_createVNode(\"img\", { src: _imports_1 })`)
|
expect(code).toMatch(`_createVNode(\"img\", { src: _imports_1 })`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #3874
|
||||||
|
test('should not hoist srcset URLs in SSR mode', () => {
|
||||||
|
const { code } = compile({
|
||||||
|
filename: 'example.vue',
|
||||||
|
source: `
|
||||||
|
<picture>
|
||||||
|
<source srcset="./img/foo.svg"/>
|
||||||
|
<img src="./img/foo.svg"/>
|
||||||
|
</picture>
|
||||||
|
<router-link>
|
||||||
|
<picture>
|
||||||
|
<source srcset="./img/bar.svg"/>
|
||||||
|
<img src="./img/bar.svg"/>
|
||||||
|
</picture>
|
||||||
|
</router-link>
|
||||||
|
`,
|
||||||
|
ssr: true
|
||||||
|
})
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
|
@ -54,9 +54,12 @@ describe('compiler sfc: transform asset url', () => {
|
||||||
test('support uri fragment', () => {
|
test('support uri fragment', () => {
|
||||||
const result = compileWithAssetUrls(
|
const result = compileWithAssetUrls(
|
||||||
'<use href="~@svg/file.svg#fragment"></use>' +
|
'<use href="~@svg/file.svg#fragment"></use>' +
|
||||||
'<use href="~@svg/file.svg#fragment"></use>'
|
'<use href="~@svg/file.svg#fragment"></use>',
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
hoistStatic: true
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(result.code).toMatchSnapshot()
|
expect(result.code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ function compileWithSrcset(
|
||||||
? createSrcsetTransformWithOptions(normalizeOptions(options))
|
? createSrcsetTransformWithOptions(normalizeOptions(options))
|
||||||
: transformSrcset
|
: transformSrcset
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
|
hoistStatic: true,
|
||||||
nodeTransforms: [srcsetTransform, transformElement],
|
nodeTransforms: [srcsetTransform, transformElement],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
bind: transformBind
|
bind: transformBind
|
||||||
|
@ -85,4 +86,16 @@ describe('compiler sfc: transform srcset', () => {
|
||||||
expect(code).toMatch(`_createStaticVNode`)
|
expect(code).toMatch(`_createStaticVNode`)
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('srcset w/ explicit base option', () => {
|
||||||
|
const code = compileWithSrcset(
|
||||||
|
`
|
||||||
|
<img srcset="@/logo.png, @/logo.png 2x"/>
|
||||||
|
<img srcset="@/logo.png 1x, ./logo.png 2x"/>
|
||||||
|
`,
|
||||||
|
{ base: '/foo/' },
|
||||||
|
{ hoistStatic: true }
|
||||||
|
).code
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
import { parse, SFCScriptCompileOptions, compileScript } from '../src'
|
import {
|
||||||
|
parse,
|
||||||
|
SFCScriptCompileOptions,
|
||||||
|
compileScript,
|
||||||
|
SFCParseOptions
|
||||||
|
} from '../src'
|
||||||
import { parse as babelParse } from '@babel/parser'
|
import { parse as babelParse } from '@babel/parser'
|
||||||
|
|
||||||
export const mockId = 'xxxxxxxx'
|
export const mockId = 'xxxxxxxx'
|
||||||
|
|
||||||
export function compileSFCScript(
|
export function compileSFCScript(
|
||||||
src: string,
|
src: string,
|
||||||
options?: Partial<SFCScriptCompileOptions>
|
options?: Partial<SFCScriptCompileOptions>,
|
||||||
|
parseOptions?: SFCParseOptions
|
||||||
) {
|
) {
|
||||||
const { descriptor } = parse(src)
|
const { descriptor } = parse(src, parseOptions)
|
||||||
return compileScript(descriptor, {
|
return compileScript(descriptor, {
|
||||||
...options,
|
...options,
|
||||||
id: mockId
|
id: mockId
|
||||||
|
|
|
@ -11,15 +11,14 @@ import {
|
||||||
isFunctionType,
|
isFunctionType,
|
||||||
walkIdentifiers
|
walkIdentifiers
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { SFCDescriptor, SFCScriptBlock } from './parse'
|
import { DEFAULT_FILENAME, SFCDescriptor, SFCScriptBlock } from './parse'
|
||||||
import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
|
|
||||||
import {
|
import {
|
||||||
camelize,
|
parse as _parse,
|
||||||
capitalize,
|
parseExpression,
|
||||||
generateCodeFrame,
|
ParserOptions,
|
||||||
isObject,
|
ParserPlugin
|
||||||
makeMap
|
} from '@babel/parser'
|
||||||
} from '@vue/shared'
|
import { camelize, capitalize, generateCodeFrame, isObject,makeMap } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
Node,
|
Node,
|
||||||
Declaration,
|
Declaration,
|
||||||
|
@ -188,6 +187,12 @@ export function compileScript(
|
||||||
const plugins: ParserPlugin[] = []
|
const plugins: ParserPlugin[] = []
|
||||||
if (!isTS || scriptLang === 'tsx' || scriptSetupLang === 'tsx') {
|
if (!isTS || scriptLang === 'tsx' || scriptSetupLang === 'tsx') {
|
||||||
plugins.push('jsx')
|
plugins.push('jsx')
|
||||||
|
} else {
|
||||||
|
// If don't match the case of adding jsx, should remove the jsx from the babelParserPlugins
|
||||||
|
if (options.babelParserPlugins)
|
||||||
|
options.babelParserPlugins = options.babelParserPlugins.filter(
|
||||||
|
n => n !== 'jsx'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (options.babelParserPlugins) plugins.push(...options.babelParserPlugins)
|
if (options.babelParserPlugins) plugins.push(...options.babelParserPlugins)
|
||||||
if (isTS) plugins.push('typescript', 'decorators-legacy')
|
if (isTS) plugins.push('typescript', 'decorators-legacy')
|
||||||
|
@ -279,6 +284,8 @@ export function compileScript(
|
||||||
let hasDefinePropsCall = false
|
let hasDefinePropsCall = false
|
||||||
let hasDefineEmitCall = false
|
let hasDefineEmitCall = false
|
||||||
let hasDefineExposeCall = false
|
let hasDefineExposeCall = false
|
||||||
|
let hasDefaultExportName = false
|
||||||
|
let hasDefaultExportRender = false
|
||||||
let propsRuntimeDecl: Node | undefined
|
let propsRuntimeDecl: Node | undefined
|
||||||
let propsRuntimeDefaults: ObjectExpression | undefined
|
let propsRuntimeDefaults: ObjectExpression | undefined
|
||||||
let propsDestructureDecl: Node | undefined
|
let propsDestructureDecl: Node | undefined
|
||||||
|
@ -356,14 +363,23 @@ export function compileScript(
|
||||||
local: string,
|
local: string,
|
||||||
imported: string | false,
|
imported: string | false,
|
||||||
isType: boolean,
|
isType: boolean,
|
||||||
isFromSetup: boolean
|
isFromSetup: boolean,
|
||||||
|
needTemplateUsageCheck: boolean
|
||||||
) {
|
) {
|
||||||
if (source === 'vue' && imported) {
|
if (source === 'vue' && imported) {
|
||||||
userImportAlias[imported] = local
|
userImportAlias[imported] = local
|
||||||
}
|
}
|
||||||
|
|
||||||
let isUsedInTemplate = true
|
// template usage check is only needed in non-inline mode, so we can skip
|
||||||
if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
|
// the work if inlineTemplate is true.
|
||||||
|
let isUsedInTemplate = needTemplateUsageCheck
|
||||||
|
if (
|
||||||
|
needTemplateUsageCheck &&
|
||||||
|
isTS &&
|
||||||
|
sfc.template &&
|
||||||
|
!sfc.template.src &&
|
||||||
|
!sfc.template.lang
|
||||||
|
) {
|
||||||
isUsedInTemplate = isImportUsed(local, sfc)
|
isUsedInTemplate = isImportUsed(local, sfc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +441,11 @@ export function compileScript(
|
||||||
prop.key
|
prop.key
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const propKey = (prop.key as Identifier).name
|
|
||||||
|
const propKey = prop.key.type === 'StringLiteral'
|
||||||
|
? prop.key.value
|
||||||
|
: (prop.key as Identifier).name
|
||||||
|
|
||||||
if (prop.value.type === 'AssignmentPattern') {
|
if (prop.value.type === 'AssignmentPattern') {
|
||||||
// default value { foo = 123 }
|
// default value { foo = 123 }
|
||||||
const { left, right } = prop.value
|
const { left, right } = prop.value
|
||||||
|
@ -751,7 +771,7 @@ export function compileScript(
|
||||||
destructured.default.end!
|
destructured.default.end!
|
||||||
)
|
)
|
||||||
const isLiteral = destructured.default.type.endsWith('Literal')
|
const isLiteral = destructured.default.type.endsWith('Literal')
|
||||||
return isLiteral ? value : `() => ${value}`
|
return isLiteral ? value : `() => (${value})`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,12 +841,48 @@ export function compileScript(
|
||||||
node.importKind === 'type' ||
|
node.importKind === 'type' ||
|
||||||
(specifier.type === 'ImportSpecifier' &&
|
(specifier.type === 'ImportSpecifier' &&
|
||||||
specifier.importKind === 'type'),
|
specifier.importKind === 'type'),
|
||||||
false
|
false,
|
||||||
|
!options.inlineTemplate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (node.type === 'ExportDefaultDeclaration') {
|
} else if (node.type === 'ExportDefaultDeclaration') {
|
||||||
// export default
|
// export default
|
||||||
defaultExport = node
|
defaultExport = node
|
||||||
|
|
||||||
|
// check if user has manually specified `name` or 'render` option in
|
||||||
|
// export default
|
||||||
|
// if has name, skip name inference
|
||||||
|
// if has render and no template, generate return object instead of
|
||||||
|
// empty render function (#4980)
|
||||||
|
let optionProperties
|
||||||
|
if (defaultExport.declaration.type === 'ObjectExpression') {
|
||||||
|
optionProperties = defaultExport.declaration.properties
|
||||||
|
} else if (
|
||||||
|
defaultExport.declaration.type === 'CallExpression' &&
|
||||||
|
defaultExport.declaration.arguments[0].type === 'ObjectExpression'
|
||||||
|
) {
|
||||||
|
optionProperties = defaultExport.declaration.arguments[0].properties
|
||||||
|
}
|
||||||
|
if (optionProperties) {
|
||||||
|
for (const s of optionProperties) {
|
||||||
|
if (
|
||||||
|
s.type === 'ObjectProperty' &&
|
||||||
|
s.key.type === 'Identifier' &&
|
||||||
|
s.key.name === 'name'
|
||||||
|
) {
|
||||||
|
hasDefaultExportName = true
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(s.type === 'ObjectMethod' || s.type === 'ObjectProperty') &&
|
||||||
|
s.key.type === 'Identifier' &&
|
||||||
|
s.key.name === 'render'
|
||||||
|
) {
|
||||||
|
// TODO warn when we provide a better way to do it?
|
||||||
|
hasDefaultExportRender = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// export default { ... } --> const __default__ = { ... }
|
// export default { ... } --> const __default__ = { ... }
|
||||||
const start = node.start! + scriptStartOffset!
|
const start = node.start! + scriptStartOffset!
|
||||||
const end = node.declaration.start! + scriptStartOffset!
|
const end = node.declaration.start! + scriptStartOffset!
|
||||||
|
@ -896,6 +952,10 @@ export function compileScript(
|
||||||
// we need to move the block up so that `const __default__` is
|
// we need to move the block up so that `const __default__` is
|
||||||
// declared before being used in the actual component definition
|
// declared before being used in the actual component definition
|
||||||
if (scriptStartOffset! > startOffset) {
|
if (scriptStartOffset! > startOffset) {
|
||||||
|
// if content doesn't end with newline, add one
|
||||||
|
if (!/\n$/.test(script.content.trim())) {
|
||||||
|
s.appendLeft(scriptEndOffset!, `\n`)
|
||||||
|
}
|
||||||
s.move(scriptStartOffset!, scriptEndOffset!, 0)
|
s.move(scriptStartOffset!, scriptEndOffset!, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -969,10 +1029,13 @@ export function compileScript(
|
||||||
for (let i = 0; i < node.specifiers.length; i++) {
|
for (let i = 0; i < node.specifiers.length; i++) {
|
||||||
const specifier = node.specifiers[i]
|
const specifier = node.specifiers[i]
|
||||||
const local = specifier.local.name
|
const local = specifier.local.name
|
||||||
const imported =
|
let imported =
|
||||||
specifier.type === 'ImportSpecifier' &&
|
specifier.type === 'ImportSpecifier' &&
|
||||||
specifier.imported.type === 'Identifier' &&
|
specifier.imported.type === 'Identifier' &&
|
||||||
specifier.imported.name
|
specifier.imported.name
|
||||||
|
if (specifier.type === 'ImportNamespaceSpecifier') {
|
||||||
|
imported = '*'
|
||||||
|
}
|
||||||
const source = node.source.value
|
const source = node.source.value
|
||||||
const existing = userImports[local]
|
const existing = userImports[local]
|
||||||
if (
|
if (
|
||||||
|
@ -1000,7 +1063,8 @@ export function compileScript(
|
||||||
node.importKind === 'type' ||
|
node.importKind === 'type' ||
|
||||||
(specifier.type === 'ImportSpecifier' &&
|
(specifier.type === 'ImportSpecifier' &&
|
||||||
specifier.importKind === 'type'),
|
specifier.importKind === 'type'),
|
||||||
true
|
true,
|
||||||
|
!options.inlineTemplate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1076,15 +1140,28 @@ export function compileScript(
|
||||||
(node.type === 'VariableDeclaration' && !node.declare) ||
|
(node.type === 'VariableDeclaration' && !node.declare) ||
|
||||||
node.type.endsWith('Statement')
|
node.type.endsWith('Statement')
|
||||||
) {
|
) {
|
||||||
|
const scope: Statement[][] = [scriptSetupAst.body]
|
||||||
;(walk as any)(node, {
|
;(walk as any)(node, {
|
||||||
enter(child: Node, parent: Node) {
|
enter(child: Node, parent: Node) {
|
||||||
if (isFunctionType(child)) {
|
if (isFunctionType(child)) {
|
||||||
this.skip()
|
this.skip()
|
||||||
}
|
}
|
||||||
|
if (child.type === 'BlockStatement') {
|
||||||
|
scope.push(child.body)
|
||||||
|
}
|
||||||
if (child.type === 'AwaitExpression') {
|
if (child.type === 'AwaitExpression') {
|
||||||
hasAwait = true
|
hasAwait = true
|
||||||
const needsSemi = scriptSetupAst.body.some(n => {
|
// if the await expression is an expression statement and
|
||||||
return n.type === 'ExpressionStatement' && n.start === child.start
|
// - is in the root scope
|
||||||
|
// - or is not the first statement in a nested block scope
|
||||||
|
// then it needs a semicolon before the generated code.
|
||||||
|
const currentScope = scope[scope.length - 1]
|
||||||
|
const needsSemi = currentScope.some((n, i) => {
|
||||||
|
return (
|
||||||
|
(scope.length === 1 || i > 0) &&
|
||||||
|
n.type === 'ExpressionStatement' &&
|
||||||
|
n.start === child.start
|
||||||
|
)
|
||||||
})
|
})
|
||||||
processAwait(
|
processAwait(
|
||||||
child,
|
child,
|
||||||
|
@ -1092,6 +1169,9 @@ export function compileScript(
|
||||||
parent.type === 'ExpressionStatement'
|
parent.type === 'ExpressionStatement'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
exit(node: Node) {
|
||||||
|
if (node.type === 'BlockStatement') scope.pop()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1161,7 +1241,7 @@ export function compileScript(
|
||||||
checkInvalidScopeReference(propsRuntimeDecl, DEFINE_PROPS)
|
checkInvalidScopeReference(propsRuntimeDecl, DEFINE_PROPS)
|
||||||
checkInvalidScopeReference(propsRuntimeDefaults, DEFINE_PROPS)
|
checkInvalidScopeReference(propsRuntimeDefaults, DEFINE_PROPS)
|
||||||
checkInvalidScopeReference(propsDestructureDecl, DEFINE_PROPS)
|
checkInvalidScopeReference(propsDestructureDecl, DEFINE_PROPS)
|
||||||
checkInvalidScopeReference(emitsRuntimeDecl, DEFINE_PROPS)
|
checkInvalidScopeReference(emitsRuntimeDecl, DEFINE_EMITS)
|
||||||
|
|
||||||
// 6. remove non-script content
|
// 6. remove non-script content
|
||||||
if (script) {
|
if (script) {
|
||||||
|
@ -1197,7 +1277,8 @@ export function compileScript(
|
||||||
// props aliases
|
// props aliases
|
||||||
if (propsDestructureDecl) {
|
if (propsDestructureDecl) {
|
||||||
if (propsDestructureRestId) {
|
if (propsDestructureRestId) {
|
||||||
bindingMetadata[propsDestructureRestId] = BindingTypes.SETUP_CONST
|
bindingMetadata[propsDestructureRestId] =
|
||||||
|
BindingTypes.SETUP_REACTIVE_CONST
|
||||||
}
|
}
|
||||||
for (const key in propsDestructuredBindings) {
|
for (const key in propsDestructuredBindings) {
|
||||||
const { local } = propsDestructuredBindings[key]
|
const { local } = propsDestructuredBindings[key]
|
||||||
|
@ -1213,7 +1294,9 @@ export function compileScript(
|
||||||
)) {
|
)) {
|
||||||
if (isType) continue
|
if (isType) continue
|
||||||
bindingMetadata[key] =
|
bindingMetadata[key] =
|
||||||
(imported === 'default' && source.endsWith('.vue')) || source === 'vue'
|
imported === '*' ||
|
||||||
|
(imported === 'default' && source.endsWith('.vue')) ||
|
||||||
|
source === 'vue'
|
||||||
? BindingTypes.SETUP_CONST
|
? BindingTypes.SETUP_CONST
|
||||||
: BindingTypes.SETUP_MAYBE_REF
|
: BindingTypes.SETUP_MAYBE_REF
|
||||||
}
|
}
|
||||||
|
@ -1292,7 +1375,21 @@ export function compileScript(
|
||||||
|
|
||||||
// 10. generate return statement
|
// 10. generate return statement
|
||||||
let returned
|
let returned
|
||||||
if (options.inlineTemplate) {
|
if (!options.inlineTemplate || (!sfc.template && hasDefaultExportRender)) {
|
||||||
|
// non-inline mode, or has manual render in normal <script>
|
||||||
|
// return bindings from script and script setup
|
||||||
|
const allBindings: Record<string, any> = {
|
||||||
|
...scriptBindings,
|
||||||
|
...setupBindings
|
||||||
|
}
|
||||||
|
for (const key in userImports) {
|
||||||
|
if (!userImports[key].isType && userImports[key].isUsedInTemplate) {
|
||||||
|
allBindings[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returned = `{ ${Object.keys(allBindings).join(', ')} }`
|
||||||
|
} else {
|
||||||
|
// inline mode
|
||||||
if (sfc.template && !sfc.template.src) {
|
if (sfc.template && !sfc.template.src) {
|
||||||
if (options.templateOptions && options.templateOptions.ssr) {
|
if (options.templateOptions && options.templateOptions.ssr) {
|
||||||
hasInlinedSsrRenderFn = true
|
hasInlinedSsrRenderFn = true
|
||||||
|
@ -1350,18 +1447,6 @@ export function compileScript(
|
||||||
} else {
|
} else {
|
||||||
returned = `() => {}`
|
returned = `() => {}`
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// return bindings from script and script setup
|
|
||||||
const allBindings: Record<string, any> = {
|
|
||||||
...scriptBindings,
|
|
||||||
...setupBindings
|
|
||||||
}
|
|
||||||
for (const key in userImports) {
|
|
||||||
if (!userImports[key].isType && userImports[key].isUsedInTemplate) {
|
|
||||||
allBindings[key] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
returned = `{ ${Object.keys(allBindings).join(', ')} }`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.inlineTemplate && !__TEST__) {
|
if (!options.inlineTemplate && !__TEST__) {
|
||||||
|
@ -1380,6 +1465,12 @@ export function compileScript(
|
||||||
|
|
||||||
// 11. finalize default export
|
// 11. finalize default export
|
||||||
let runtimeOptions = ``
|
let runtimeOptions = ``
|
||||||
|
if (!hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
|
||||||
|
const match = filename.match(/([^/\\]+)\.\w+$/)
|
||||||
|
if (match) {
|
||||||
|
runtimeOptions += `\n name: '${match[1]}',`
|
||||||
|
}
|
||||||
|
}
|
||||||
if (hasInlinedSsrRenderFn) {
|
if (hasInlinedSsrRenderFn) {
|
||||||
runtimeOptions += `\n __ssrInlineRender: true,`
|
runtimeOptions += `\n __ssrInlineRender: true,`
|
||||||
}
|
}
|
||||||
|
@ -1509,14 +1600,18 @@ function walkDeclaration(
|
||||||
const userReactiveBinding = userImportAlias['reactive'] || 'reactive'
|
const userReactiveBinding = userImportAlias['reactive'] || 'reactive'
|
||||||
if (isCallOf(init, userReactiveBinding)) {
|
if (isCallOf(init, userReactiveBinding)) {
|
||||||
// treat reactive() calls as let since it's meant to be mutable
|
// treat reactive() calls as let since it's meant to be mutable
|
||||||
bindingType = BindingTypes.SETUP_LET
|
bindingType = isConst
|
||||||
|
? BindingTypes.SETUP_REACTIVE_CONST
|
||||||
|
: BindingTypes.SETUP_LET
|
||||||
} else if (
|
} else if (
|
||||||
// if a declaration is a const literal, we can mark it so that
|
// if a declaration is a const literal, we can mark it so that
|
||||||
// the generated render fn code doesn't need to unref() it
|
// the generated render fn code doesn't need to unref() it
|
||||||
isDefineCall ||
|
isDefineCall ||
|
||||||
(isConst && canNeverBeRef(init!, userReactiveBinding))
|
(isConst && canNeverBeRef(init!, userReactiveBinding))
|
||||||
) {
|
) {
|
||||||
bindingType = BindingTypes.SETUP_CONST
|
bindingType = isCallOf(init, DEFINE_PROPS)
|
||||||
|
? BindingTypes.SETUP_REACTIVE_CONST
|
||||||
|
: BindingTypes.SETUP_CONST
|
||||||
} else if (isConst) {
|
} else if (isConst) {
|
||||||
if (isCallOf(init, userImportAlias['ref'] || 'ref')) {
|
if (isCallOf(init, userImportAlias['ref'] || 'ref')) {
|
||||||
bindingType = BindingTypes.SETUP_REF
|
bindingType = BindingTypes.SETUP_REF
|
||||||
|
@ -2011,14 +2106,14 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
||||||
code += `,v${capitalize(camelize(prop.name))}`
|
code += `,v${capitalize(camelize(prop.name))}`
|
||||||
}
|
}
|
||||||
if (prop.exp) {
|
if (prop.exp) {
|
||||||
code += `,${stripStrings(
|
code += `,${processExp(
|
||||||
(prop.exp as SimpleExpressionNode).content
|
(prop.exp as SimpleExpressionNode).content
|
||||||
)}`
|
)}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (node.type === NodeTypes.INTERPOLATION) {
|
} else if (node.type === NodeTypes.INTERPOLATION) {
|
||||||
code += `,${stripStrings(
|
code += `,${processExp(
|
||||||
(node.content as SimpleExpressionNode).content
|
(node.content as SimpleExpressionNode).content
|
||||||
)}`
|
)}`
|
||||||
}
|
}
|
||||||
|
@ -2031,6 +2126,19 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processExp(exp: string) {
|
||||||
|
if (/ as \w|<.*>/.test(exp)) {
|
||||||
|
let ret = ''
|
||||||
|
// has potential type cast or generic arguments that uses types
|
||||||
|
const ast = parseExpression(exp, { plugins: ['typescript'] })
|
||||||
|
walkIdentifiers(ast, node => {
|
||||||
|
ret += `,` + node.name
|
||||||
|
})
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
return stripStrings(exp)
|
||||||
|
}
|
||||||
|
|
||||||
function stripStrings(exp: string) {
|
function stripStrings(exp: string) {
|
||||||
return exp
|
return exp
|
||||||
.replace(/'[^']*'|"[^"]*"/g, '')
|
.replace(/'[^']*'|"[^"]*"/g, '')
|
||||||
|
|
|
@ -33,7 +33,7 @@ function genVarName(id: string, raw: string, isProd: boolean): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function noramlizeExpression(exp: string) {
|
function normalizeExpression(exp: string) {
|
||||||
exp = exp.trim()
|
exp = exp.trim()
|
||||||
if (
|
if (
|
||||||
(exp[0] === `'` && exp[exp.length - 1] === `'`) ||
|
(exp[0] === `'` && exp[exp.length - 1] === `'`) ||
|
||||||
|
@ -51,7 +51,7 @@ export function parseCssVars(sfc: SFCDescriptor): string[] {
|
||||||
// ignore v-bind() in comments /* ... */
|
// ignore v-bind() in comments /* ... */
|
||||||
const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '')
|
const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '')
|
||||||
while ((match = cssVarRE.exec(content))) {
|
while ((match = cssVarRE.exec(content))) {
|
||||||
const variable = noramlizeExpression(match[1])
|
const variable = normalizeExpression(match[1])
|
||||||
if (!vars.includes(variable)) {
|
if (!vars.includes(variable)) {
|
||||||
vars.push(variable)
|
vars.push(variable)
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
|
||||||
// rewrite CSS variables
|
// rewrite CSS variables
|
||||||
if (cssVarRE.test(decl.value)) {
|
if (cssVarRE.test(decl.value)) {
|
||||||
decl.value = decl.value.replace(cssVarRE, (_, $1) => {
|
decl.value = decl.value.replace(cssVarRE, (_, $1) => {
|
||||||
return `var(--${genVarName(id, noramlizeExpression($1), isProd)})`
|
return `var(--${genVarName(id, normalizeExpression($1), isProd)})`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,12 @@ export {
|
||||||
// Types
|
// Types
|
||||||
export {
|
export {
|
||||||
SFCParseOptions,
|
SFCParseOptions,
|
||||||
|
SFCParseResult,
|
||||||
SFCDescriptor,
|
SFCDescriptor,
|
||||||
SFCBlock,
|
SFCBlock,
|
||||||
SFCTemplateBlock,
|
SFCTemplateBlock,
|
||||||
SFCScriptBlock,
|
SFCScriptBlock,
|
||||||
SFCStyleBlock,
|
SFCStyleBlock
|
||||||
SFCParseResult
|
|
||||||
} from './parse'
|
} from './parse'
|
||||||
export {
|
export {
|
||||||
TemplateCompiler,
|
TemplateCompiler,
|
||||||
|
|
|
@ -13,6 +13,8 @@ import { parseCssVars } from './cssVars'
|
||||||
import { createCache } from './cache'
|
import { createCache } from './cache'
|
||||||
import { hmrShouldReload, ImportBinding } from './compileScript'
|
import { hmrShouldReload, ImportBinding } from './compileScript'
|
||||||
|
|
||||||
|
export const DEFAULT_FILENAME = 'anonymous.vue'
|
||||||
|
|
||||||
export interface SFCParseOptions {
|
export interface SFCParseOptions {
|
||||||
filename?: string
|
filename?: string
|
||||||
sourceMap?: boolean
|
sourceMap?: boolean
|
||||||
|
@ -95,7 +97,7 @@ export function parse(
|
||||||
source: string,
|
source: string,
|
||||||
{
|
{
|
||||||
sourceMap = true,
|
sourceMap = true,
|
||||||
filename = 'anonymous.vue',
|
filename = DEFAULT_FILENAME,
|
||||||
sourceRoot = '',
|
sourceRoot = '',
|
||||||
pad = false,
|
pad = false,
|
||||||
ignoreEmpty = true,
|
ignoreEmpty = true,
|
||||||
|
|
|
@ -176,6 +176,17 @@ function getImportsExpressionExp(
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashExp = `${name} + '${hash}'`
|
const hashExp = `${name} + '${hash}'`
|
||||||
|
const finalExp = createSimpleExpression(
|
||||||
|
hashExp,
|
||||||
|
false,
|
||||||
|
loc,
|
||||||
|
ConstantTypes.CAN_STRINGIFY
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!context.hoistStatic) {
|
||||||
|
return finalExp
|
||||||
|
}
|
||||||
|
|
||||||
const existingHoistIndex = context.hoists.findIndex(h => {
|
const existingHoistIndex = context.hoists.findIndex(h => {
|
||||||
return (
|
return (
|
||||||
h &&
|
h &&
|
||||||
|
@ -192,9 +203,7 @@ function getImportsExpressionExp(
|
||||||
ConstantTypes.CAN_STRINGIFY
|
ConstantTypes.CAN_STRINGIFY
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return context.hoist(
|
return context.hoist(finalExp)
|
||||||
createSimpleExpression(hashExp, false, loc, ConstantTypes.CAN_STRINGIFY)
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
|
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
ConstantTypes,
|
ConstantTypes,
|
||||||
createCompoundExpression,
|
createCompoundExpression,
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
|
ExpressionNode,
|
||||||
NodeTransform,
|
NodeTransform,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
SimpleExpressionNode
|
SimpleExpressionNode
|
||||||
|
@ -68,40 +69,45 @@ export const transformSrcset: NodeTransform = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasQualifiedUrl = imageCandidates.some(({ url }) => {
|
const shouldProcessUrl = (url: string) => {
|
||||||
return (
|
return (
|
||||||
!isExternalUrl(url) &&
|
!isExternalUrl(url) &&
|
||||||
!isDataUrl(url) &&
|
!isDataUrl(url) &&
|
||||||
(options.includeAbsolute || isRelativeUrl(url))
|
(options.includeAbsolute || isRelativeUrl(url))
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
// When srcset does not contain any qualified URLs, skip transforming
|
// When srcset does not contain any qualified URLs, skip transforming
|
||||||
if (!hasQualifiedUrl) {
|
if (!imageCandidates.some(({ url }) => shouldProcessUrl(url))) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.base) {
|
if (options.base) {
|
||||||
const base = options.base
|
const base = options.base
|
||||||
const set: string[] = []
|
const set: string[] = []
|
||||||
imageCandidates.forEach(({ url, descriptor }) => {
|
let needImportTransform = false
|
||||||
|
|
||||||
|
imageCandidates.forEach(candidate => {
|
||||||
|
let { url, descriptor } = candidate
|
||||||
descriptor = descriptor ? ` ${descriptor}` : ``
|
descriptor = descriptor ? ` ${descriptor}` : ``
|
||||||
if (isRelativeUrl(url)) {
|
if (url[0] === '.') {
|
||||||
set.push((path.posix || path).join(base, url) + descriptor)
|
candidate.url = (path.posix || path).join(base, url)
|
||||||
|
set.push(candidate.url + descriptor)
|
||||||
|
} else if (shouldProcessUrl(url)) {
|
||||||
|
needImportTransform = true
|
||||||
} else {
|
} else {
|
||||||
set.push(url + descriptor)
|
set.push(url + descriptor)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
attr.value.content = set.join(', ')
|
|
||||||
return
|
if (!needImportTransform) {
|
||||||
|
attr.value.content = set.join(', ')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const compoundExpression = createCompoundExpression([], attr.loc)
|
const compoundExpression = createCompoundExpression([], attr.loc)
|
||||||
imageCandidates.forEach(({ url, descriptor }, index) => {
|
imageCandidates.forEach(({ url, descriptor }, index) => {
|
||||||
if (
|
if (shouldProcessUrl(url)) {
|
||||||
!isExternalUrl(url) &&
|
|
||||||
!isDataUrl(url) &&
|
|
||||||
(options.includeAbsolute || isRelativeUrl(url))
|
|
||||||
) {
|
|
||||||
const { path } = parseUrl(url)
|
const { path } = parseUrl(url)
|
||||||
let exp: SimpleExpressionNode
|
let exp: SimpleExpressionNode
|
||||||
if (path) {
|
if (path) {
|
||||||
|
@ -145,14 +151,17 @@ export const transformSrcset: NodeTransform = (
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const hoisted = context.hoist(compoundExpression)
|
let exp: ExpressionNode = compoundExpression
|
||||||
hoisted.constType = ConstantTypes.CAN_STRINGIFY
|
if (context.hoistStatic) {
|
||||||
|
exp = context.hoist(compoundExpression)
|
||||||
|
exp.constType = ConstantTypes.CAN_STRINGIFY
|
||||||
|
}
|
||||||
|
|
||||||
node.props[index] = {
|
node.props[index] = {
|
||||||
type: NodeTypes.DIRECTIVE,
|
type: NodeTypes.DIRECTIVE,
|
||||||
name: 'bind',
|
name: 'bind',
|
||||||
arg: createSimpleExpression('srcset', true, attr.loc),
|
arg: createSimpleExpression('srcset', true, attr.loc),
|
||||||
exp: hoisted,
|
exp,
|
||||||
modifiers: [],
|
modifiers: [],
|
||||||
loc: attr.loc
|
loc: attr.loc
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { compile } from '../src'
|
import { compile } from '../src'
|
||||||
|
import { ssrHelpers, SSR_RENDER_SLOT_INNER } from '../src/runtimeHelpers'
|
||||||
|
|
||||||
describe('ssr: <slot>', () => {
|
describe('ssr: <slot>', () => {
|
||||||
test('basic', () => {
|
test('basic', () => {
|
||||||
|
@ -114,4 +115,16 @@ describe('ssr: <slot>', () => {
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('inside transition', () => {
|
||||||
|
const { code } = compile(`<transition><slot/></transition>`)
|
||||||
|
expect(code).toMatch(ssrHelpers[SSR_RENDER_SLOT_INNER])
|
||||||
|
expect(code).toMatchInlineSnapshot(`
|
||||||
|
"const { ssrRenderSlotInner: _ssrRenderSlotInner } = require(\\"vue/server-renderer\\")
|
||||||
|
|
||||||
|
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||||
|
_ssrRenderSlotInner(_ctx.$slots, \\"default\\", {}, null, _push, _parent)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,7 @@ export const SSR_INTERPOLATE = Symbol(`ssrInterpolate`)
|
||||||
export const SSR_RENDER_VNODE = Symbol(`ssrRenderVNode`)
|
export const SSR_RENDER_VNODE = Symbol(`ssrRenderVNode`)
|
||||||
export const SSR_RENDER_COMPONENT = Symbol(`ssrRenderComponent`)
|
export const SSR_RENDER_COMPONENT = Symbol(`ssrRenderComponent`)
|
||||||
export const SSR_RENDER_SLOT = Symbol(`ssrRenderSlot`)
|
export const SSR_RENDER_SLOT = Symbol(`ssrRenderSlot`)
|
||||||
|
export const SSR_RENDER_SLOT_INNER = Symbol(`ssrRenderSlotInner`)
|
||||||
export const SSR_RENDER_CLASS = Symbol(`ssrRenderClass`)
|
export const SSR_RENDER_CLASS = Symbol(`ssrRenderClass`)
|
||||||
export const SSR_RENDER_STYLE = Symbol(`ssrRenderStyle`)
|
export const SSR_RENDER_STYLE = Symbol(`ssrRenderStyle`)
|
||||||
export const SSR_RENDER_ATTRS = Symbol(`ssrRenderAttrs`)
|
export const SSR_RENDER_ATTRS = Symbol(`ssrRenderAttrs`)
|
||||||
|
@ -24,6 +25,7 @@ export const ssrHelpers = {
|
||||||
[SSR_RENDER_VNODE]: `ssrRenderVNode`,
|
[SSR_RENDER_VNODE]: `ssrRenderVNode`,
|
||||||
[SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
|
[SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
|
||||||
[SSR_RENDER_SLOT]: `ssrRenderSlot`,
|
[SSR_RENDER_SLOT]: `ssrRenderSlot`,
|
||||||
|
[SSR_RENDER_SLOT_INNER]: `ssrRenderSlotInner`,
|
||||||
[SSR_RENDER_CLASS]: `ssrRenderClass`,
|
[SSR_RENDER_CLASS]: `ssrRenderClass`,
|
||||||
[SSR_RENDER_STYLE]: `ssrRenderStyle`,
|
[SSR_RENDER_STYLE]: `ssrRenderStyle`,
|
||||||
[SSR_RENDER_ATTRS]: `ssrRenderAttrs`,
|
[SSR_RENDER_ATTRS]: `ssrRenderAttrs`,
|
||||||
|
|
|
@ -4,9 +4,13 @@ import {
|
||||||
processSlotOutlet,
|
processSlotOutlet,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
SlotOutletNode,
|
SlotOutletNode,
|
||||||
createFunctionExpression
|
createFunctionExpression,
|
||||||
|
NodeTypes,
|
||||||
|
ElementTypes,
|
||||||
|
resolveComponentType,
|
||||||
|
TRANSITION
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { SSR_RENDER_SLOT } from '../runtimeHelpers'
|
import { SSR_RENDER_SLOT, SSR_RENDER_SLOT_INNER } from '../runtimeHelpers'
|
||||||
import {
|
import {
|
||||||
SSRTransformContext,
|
SSRTransformContext,
|
||||||
processChildrenAsStatement
|
processChildrenAsStatement
|
||||||
|
@ -31,10 +35,24 @@ export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
|
||||||
args.push(`"${context.scopeId}-s"`)
|
args.push(`"${context.scopeId}-s"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
node.ssrCodegenNode = createCallExpression(
|
let method = SSR_RENDER_SLOT
|
||||||
context.helper(SSR_RENDER_SLOT),
|
|
||||||
args
|
// #3989
|
||||||
)
|
// check if this is a single slot inside a transition wrapper - since
|
||||||
|
// transition will unwrap the slot fragment into a single vnode at runtime,
|
||||||
|
// we need to avoid rendering the slot as a fragment.
|
||||||
|
const parent = context.parent
|
||||||
|
if (
|
||||||
|
parent &&
|
||||||
|
parent.type === NodeTypes.ELEMENT &&
|
||||||
|
parent.tagType === ElementTypes.COMPONENT &&
|
||||||
|
resolveComponentType(parent, context, true) === TRANSITION &&
|
||||||
|
parent.children.filter(c => c.type === NodeTypes.ELEMENT).length === 1
|
||||||
|
) {
|
||||||
|
method = SSR_RENDER_SLOT_INNER
|
||||||
|
}
|
||||||
|
|
||||||
|
node.ssrCodegenNode = createCallExpression(context.helper(method), args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ exports[`object destructure 1`] = `
|
||||||
d = _toRef(__$temp_1, 'd', 1),
|
d = _toRef(__$temp_1, 'd', 1),
|
||||||
f = _toRef(__$temp_1, 'e', 2),
|
f = _toRef(__$temp_1, 'e', 2),
|
||||||
h = _toRef(__$temp_1, g)
|
h = _toRef(__$temp_1, g)
|
||||||
let __$temp_2 = (useSomthing(() => 1)),
|
let __$temp_2 = (useSomething(() => 1)),
|
||||||
foo = _toRef(__$temp_2, 'foo');
|
foo = _toRef(__$temp_2, 'foo');
|
||||||
console.log(n.value, a.value, c.value, d.value, f.value, h.value, foo.value)
|
console.log(n.value, a.value, c.value, d.value, f.value, h.value, foo.value)
|
||||||
"
|
"
|
||||||
|
|
|
@ -240,7 +240,7 @@ test('should not rewrite scope variable', () => {
|
||||||
test('object destructure', () => {
|
test('object destructure', () => {
|
||||||
const { code, rootRefs } = transform(`
|
const { code, rootRefs } = transform(`
|
||||||
let n = $ref(1), { a, b: c, d = 1, e: f = 2, [g]: h } = $(useFoo())
|
let n = $ref(1), { a, b: c, d = 1, e: f = 2, [g]: h } = $(useFoo())
|
||||||
let { foo } = $(useSomthing(() => 1));
|
let { foo } = $(useSomething(() => 1));
|
||||||
console.log(n, a, c, d, f, h, foo)
|
console.log(n, a, c, d, f, h, foo)
|
||||||
`)
|
`)
|
||||||
expect(code).toMatch(`a = _toRef(__$temp_1, 'a')`)
|
expect(code).toMatch(`a = _toRef(__$temp_1, 'a')`)
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
walkFunctionParams
|
walkFunctionParams
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { parse, ParserPlugin } from '@babel/parser'
|
import { parse, ParserPlugin } from '@babel/parser'
|
||||||
import { hasOwn, isArray, isString } from '@vue/shared'
|
import { hasOwn, isArray, isString, genPropsAccessExp } from '@vue/shared'
|
||||||
|
|
||||||
const CONVERT_SYMBOL = '$'
|
const CONVERT_SYMBOL = '$'
|
||||||
const ESCAPE_SYMBOL = '$$'
|
const ESCAPE_SYMBOL = '$$'
|
||||||
|
@ -525,10 +525,10 @@ export function createReactivityTransformer(
|
||||||
`: __props_${propsLocalToPublicMap[id.name]}`
|
`: __props_${propsLocalToPublicMap[id.name]}`
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// { prop } -> { prop: __prop.prop }
|
// { prop } -> { prop: __props.prop }
|
||||||
s.appendLeft(
|
s.appendLeft(
|
||||||
id.end! + offset,
|
id.end! + offset,
|
||||||
`: __props.${propsLocalToPublicMap[id.name]}`
|
`: ${genPropsAccessExp(propsLocalToPublicMap[id.name])}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -539,7 +539,8 @@ export function createReactivityTransformer(
|
||||||
} else {
|
} else {
|
||||||
if (isProp) {
|
if (isProp) {
|
||||||
if (escapeScope) {
|
if (escapeScope) {
|
||||||
// x --> __props_x
|
// prop binding in $$()
|
||||||
|
// { prop } -> { prop: __props_prop }
|
||||||
registerEscapedPropBinding(id)
|
registerEscapedPropBinding(id)
|
||||||
s.overwrite(
|
s.overwrite(
|
||||||
id.start! + offset,
|
id.start! + offset,
|
||||||
|
@ -551,7 +552,7 @@ export function createReactivityTransformer(
|
||||||
s.overwrite(
|
s.overwrite(
|
||||||
id.start! + offset,
|
id.start! + offset,
|
||||||
id.end! + offset,
|
id.end! + offset,
|
||||||
`__props.${propsLocalToPublicMap[id.name]}`
|
genPropsAccessExp(propsLocalToPublicMap[id.name])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -66,22 +66,22 @@ describe('reactivity/effect', () => {
|
||||||
|
|
||||||
it('should observe delete operations', () => {
|
it('should observe delete operations', () => {
|
||||||
let dummy
|
let dummy
|
||||||
const obj = reactive({ prop: 'value' })
|
const obj = reactive<{
|
||||||
|
prop?: string
|
||||||
|
}>({ prop: 'value' })
|
||||||
effect(() => (dummy = obj.prop))
|
effect(() => (dummy = obj.prop))
|
||||||
|
|
||||||
expect(dummy).toBe('value')
|
expect(dummy).toBe('value')
|
||||||
// @ts-ignore
|
|
||||||
delete obj.prop
|
delete obj.prop
|
||||||
expect(dummy).toBe(undefined)
|
expect(dummy).toBe(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should observe has operations', () => {
|
it('should observe has operations', () => {
|
||||||
let dummy
|
let dummy
|
||||||
const obj = reactive<{ prop: string | number }>({ prop: 'value' })
|
const obj = reactive<{ prop?: string | number }>({ prop: 'value' })
|
||||||
effect(() => (dummy = 'prop' in obj))
|
effect(() => (dummy = 'prop' in obj))
|
||||||
|
|
||||||
expect(dummy).toBe(true)
|
expect(dummy).toBe(true)
|
||||||
// @ts-ignore
|
|
||||||
delete obj.prop
|
delete obj.prop
|
||||||
expect(dummy).toBe(false)
|
expect(dummy).toBe(false)
|
||||||
obj.prop = 12
|
obj.prop = 12
|
||||||
|
@ -90,13 +90,12 @@ describe('reactivity/effect', () => {
|
||||||
|
|
||||||
it('should observe properties on the prototype chain', () => {
|
it('should observe properties on the prototype chain', () => {
|
||||||
let dummy
|
let dummy
|
||||||
const counter = reactive({ num: 0 })
|
const counter = reactive<{ num?: number }>({ num: 0 })
|
||||||
const parentCounter = reactive({ num: 2 })
|
const parentCounter = reactive({ num: 2 })
|
||||||
Object.setPrototypeOf(counter, parentCounter)
|
Object.setPrototypeOf(counter, parentCounter)
|
||||||
effect(() => (dummy = counter.num))
|
effect(() => (dummy = counter.num))
|
||||||
|
|
||||||
expect(dummy).toBe(0)
|
expect(dummy).toBe(0)
|
||||||
// @ts-ignore
|
|
||||||
delete counter.num
|
delete counter.num
|
||||||
expect(dummy).toBe(2)
|
expect(dummy).toBe(2)
|
||||||
parentCounter.num = 4
|
parentCounter.num = 4
|
||||||
|
@ -107,16 +106,14 @@ describe('reactivity/effect', () => {
|
||||||
|
|
||||||
it('should observe has operations on the prototype chain', () => {
|
it('should observe has operations on the prototype chain', () => {
|
||||||
let dummy
|
let dummy
|
||||||
const counter = reactive({ num: 0 })
|
const counter = reactive<{ num?: number }>({ num: 0 })
|
||||||
const parentCounter = reactive({ num: 2 })
|
const parentCounter = reactive<{ num?: number }>({ num: 2 })
|
||||||
Object.setPrototypeOf(counter, parentCounter)
|
Object.setPrototypeOf(counter, parentCounter)
|
||||||
effect(() => (dummy = 'num' in counter))
|
effect(() => (dummy = 'num' in counter))
|
||||||
|
|
||||||
expect(dummy).toBe(true)
|
expect(dummy).toBe(true)
|
||||||
// @ts-ignore
|
|
||||||
delete counter.num
|
delete counter.num
|
||||||
expect(dummy).toBe(true)
|
expect(dummy).toBe(true)
|
||||||
// @ts-ignore
|
|
||||||
delete parentCounter.num
|
delete parentCounter.num
|
||||||
expect(dummy).toBe(false)
|
expect(dummy).toBe(false)
|
||||||
counter.num = 3
|
counter.num = 3
|
||||||
|
@ -220,7 +217,7 @@ describe('reactivity/effect', () => {
|
||||||
it('should observe symbol keyed properties', () => {
|
it('should observe symbol keyed properties', () => {
|
||||||
const key = Symbol('symbol keyed prop')
|
const key = Symbol('symbol keyed prop')
|
||||||
let dummy, hasDummy
|
let dummy, hasDummy
|
||||||
const obj = reactive({ [key]: 'value' })
|
const obj = reactive<{ [key]?: string }>({ [key]: 'value' })
|
||||||
effect(() => (dummy = obj[key]))
|
effect(() => (dummy = obj[key]))
|
||||||
effect(() => (hasDummy = key in obj))
|
effect(() => (hasDummy = key in obj))
|
||||||
|
|
||||||
|
@ -228,7 +225,6 @@ describe('reactivity/effect', () => {
|
||||||
expect(hasDummy).toBe(true)
|
expect(hasDummy).toBe(true)
|
||||||
obj[key] = 'newValue'
|
obj[key] = 'newValue'
|
||||||
expect(dummy).toBe('newValue')
|
expect(dummy).toBe('newValue')
|
||||||
// @ts-ignore
|
|
||||||
delete obj[key]
|
delete obj[key]
|
||||||
expect(dummy).toBe(undefined)
|
expect(dummy).toBe(undefined)
|
||||||
expect(hasDummy).toBe(false)
|
expect(hasDummy).toBe(false)
|
||||||
|
@ -752,7 +748,7 @@ describe('reactivity/effect', () => {
|
||||||
const onTrigger = jest.fn((e: DebuggerEvent) => {
|
const onTrigger = jest.fn((e: DebuggerEvent) => {
|
||||||
events.push(e)
|
events.push(e)
|
||||||
})
|
})
|
||||||
const obj = reactive({ foo: 1 })
|
const obj = reactive<{ foo?: number }>({ foo: 1 })
|
||||||
const runner = effect(
|
const runner = effect(
|
||||||
() => {
|
() => {
|
||||||
dummy = obj.foo
|
dummy = obj.foo
|
||||||
|
@ -760,7 +756,7 @@ describe('reactivity/effect', () => {
|
||||||
{ onTrigger }
|
{ onTrigger }
|
||||||
)
|
)
|
||||||
|
|
||||||
obj.foo++
|
obj.foo!++
|
||||||
expect(dummy).toBe(2)
|
expect(dummy).toBe(2)
|
||||||
expect(onTrigger).toHaveBeenCalledTimes(1)
|
expect(onTrigger).toHaveBeenCalledTimes(1)
|
||||||
expect(events[0]).toEqual({
|
expect(events[0]).toEqual({
|
||||||
|
@ -772,7 +768,6 @@ describe('reactivity/effect', () => {
|
||||||
newValue: 2
|
newValue: 2
|
||||||
})
|
})
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
delete obj.foo
|
delete obj.foo
|
||||||
expect(dummy).toBeUndefined()
|
expect(dummy).toBeUndefined()
|
||||||
expect(onTrigger).toHaveBeenCalledTimes(2)
|
expect(onTrigger).toHaveBeenCalledTimes(2)
|
||||||
|
|
|
@ -120,7 +120,7 @@ describe('reactivity/effect/scope', () => {
|
||||||
counter.num = 6
|
counter.num = 6
|
||||||
expect(dummy).toBe(7)
|
expect(dummy).toBe(7)
|
||||||
|
|
||||||
// nested scope should not be stoped
|
// nested scope should not be stopped
|
||||||
expect(doubled).toBe(12)
|
expect(doubled).toBe(12)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ describe('reactivity/effect/scope', () => {
|
||||||
expect(spy).toHaveBeenCalledTimes(1)
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should derefence child scope from parent scope after stopping child scope (no memleaks)', () => {
|
it('should dereference child scope from parent scope after stopping child scope (no memleaks)', () => {
|
||||||
const parent = new EffectScope()
|
const parent = new EffectScope()
|
||||||
const child = parent.run(() => new EffectScope())!
|
const child = parent.run(() => new EffectScope())!
|
||||||
expect(parent.scopes!.includes(child)).toBe(true)
|
expect(parent.scopes!.includes(child)).toBe(true)
|
||||||
|
|
|
@ -112,14 +112,13 @@ describe('reactivity/reactive/Array', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('add non-integer prop on Array should not trigger length dependency', () => {
|
test('add non-integer prop on Array should not trigger length dependency', () => {
|
||||||
const array = new Array(3)
|
const array: any[] & { x?: string } = new Array(3)
|
||||||
const observed = reactive(array)
|
const observed = reactive(array)
|
||||||
const fn = jest.fn()
|
const fn = jest.fn()
|
||||||
effect(() => {
|
effect(() => {
|
||||||
fn(observed.length)
|
fn(observed.length)
|
||||||
})
|
})
|
||||||
expect(fn).toHaveBeenCalledTimes(1)
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
// @ts-ignore
|
|
||||||
observed.x = 'x'
|
observed.x = 'x'
|
||||||
expect(fn).toHaveBeenCalledTimes(1)
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
observed[-1] = 'x'
|
observed[-1] = 'x'
|
||||||
|
|
|
@ -68,21 +68,21 @@ describe('reactivity/readonly', () => {
|
||||||
`Set operation on key "Symbol(qux)" failed: target is readonly.`
|
`Set operation on key "Symbol(qux)" failed: target is readonly.`
|
||||||
).toHaveBeenWarnedLast()
|
).toHaveBeenWarnedLast()
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
delete wrapped.foo
|
delete wrapped.foo
|
||||||
expect(wrapped.foo).toBe(1)
|
expect(wrapped.foo).toBe(1)
|
||||||
expect(
|
expect(
|
||||||
`Delete operation on key "foo" failed: target is readonly.`
|
`Delete operation on key "foo" failed: target is readonly.`
|
||||||
).toHaveBeenWarnedLast()
|
).toHaveBeenWarnedLast()
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
delete wrapped.bar.baz
|
delete wrapped.bar.baz
|
||||||
expect(wrapped.bar.baz).toBe(2)
|
expect(wrapped.bar.baz).toBe(2)
|
||||||
expect(
|
expect(
|
||||||
`Delete operation on key "baz" failed: target is readonly.`
|
`Delete operation on key "baz" failed: target is readonly.`
|
||||||
).toHaveBeenWarnedLast()
|
).toHaveBeenWarnedLast()
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
delete wrapped[qux]
|
delete wrapped[qux]
|
||||||
expect(wrapped[qux]).toBe(3)
|
expect(wrapped[qux]).toBe(3)
|
||||||
expect(
|
expect(
|
||||||
|
@ -459,7 +459,7 @@ describe('reactivity/readonly', () => {
|
||||||
expect(
|
expect(
|
||||||
'Set operation on key "_dirty" failed: target is readonly.'
|
'Set operation on key "_dirty" failed: target is readonly.'
|
||||||
).not.toHaveBeenWarned()
|
).not.toHaveBeenWarned()
|
||||||
// @ts-expect-error - non-existant property
|
// @ts-expect-error - non-existent property
|
||||||
rC.randomProperty = true
|
rC.randomProperty = true
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -476,7 +476,7 @@ describe('reactivity/readonly', () => {
|
||||||
expect(isReadonly(rr.foo)).toBe(true)
|
expect(isReadonly(rr.foo)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('attemptingt to write plain value to a readonly ref nested in a reactive object should fail', () => {
|
test('attempting to write plain value to a readonly ref nested in a reactive object should fail', () => {
|
||||||
const r = ref(false)
|
const r = ref(false)
|
||||||
const ror = readonly(r)
|
const ror = readonly(r)
|
||||||
const obj = reactive({ ror })
|
const obj = reactive({ ror })
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('reactivity/shallowReadonly', () => {
|
||||||
|
|
||||||
test('should make root level properties readonly', () => {
|
test('should make root level properties readonly', () => {
|
||||||
const props = shallowReadonly({ n: 1 })
|
const props = shallowReadonly({ n: 1 })
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
props.n = 2
|
props.n = 2
|
||||||
expect(props.n).toBe(1)
|
expect(props.n).toBe(1)
|
||||||
expect(
|
expect(
|
||||||
|
@ -19,7 +19,7 @@ describe('reactivity/shallowReadonly', () => {
|
||||||
// to retain 2.x behavior.
|
// to retain 2.x behavior.
|
||||||
test('should NOT make nested properties readonly', () => {
|
test('should NOT make nested properties readonly', () => {
|
||||||
const props = shallowReadonly({ n: { foo: 1 } })
|
const props = shallowReadonly({ n: { foo: 1 } })
|
||||||
// @ts-ignore
|
|
||||||
props.n.foo = 2
|
props.n.foo = 2
|
||||||
expect(props.n.foo).toBe(2)
|
expect(props.n.foo).toBe(2)
|
||||||
expect(
|
expect(
|
||||||
|
|
|
@ -37,6 +37,10 @@ const isNonTrackableKeys = /*#__PURE__*/ makeMap(`__proto__,__v_isRef,__isVue`)
|
||||||
const builtInSymbols = new Set(
|
const builtInSymbols = new Set(
|
||||||
/*#__PURE__*/
|
/*#__PURE__*/
|
||||||
Object.getOwnPropertyNames(Symbol)
|
Object.getOwnPropertyNames(Symbol)
|
||||||
|
// ios10.x Object.getOwnPropertyNames(Symbol) can enumerate 'arguments' and 'caller'
|
||||||
|
// but accessing them on Symbol leads to TypeError because Symbol is a strict mode
|
||||||
|
// function
|
||||||
|
.filter(key => key !== 'arguments' && key !== 'caller')
|
||||||
.map(key => (Symbol as any)[key])
|
.map(key => (Symbol as any)[key])
|
||||||
.filter(isSymbol)
|
.filter(isSymbol)
|
||||||
)
|
)
|
||||||
|
@ -125,9 +129,8 @@ function createGetter(isReadonly = false, shallow = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRef(res)) {
|
if (isRef(res)) {
|
||||||
// ref unwrapping - does not apply for Array + integer key.
|
// ref unwrapping - skip unwrap for Array + integer key.
|
||||||
const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
|
return targetIsArray && isIntegerKey(key) ? res : res.value
|
||||||
return shouldUnwrap ? res.value : res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isObject(res)) {
|
if (isObject(res)) {
|
||||||
|
|
|
@ -26,10 +26,12 @@ function get(
|
||||||
target = (target as any)[ReactiveFlags.RAW]
|
target = (target as any)[ReactiveFlags.RAW]
|
||||||
const rawTarget = toRaw(target)
|
const rawTarget = toRaw(target)
|
||||||
const rawKey = toRaw(key)
|
const rawKey = toRaw(key)
|
||||||
if (key !== rawKey) {
|
if (!isReadonly) {
|
||||||
!isReadonly && track(rawTarget, TrackOpTypes.GET, key)
|
if (key !== rawKey) {
|
||||||
|
track(rawTarget, TrackOpTypes.GET, key)
|
||||||
|
}
|
||||||
|
track(rawTarget, TrackOpTypes.GET, rawKey)
|
||||||
}
|
}
|
||||||
!isReadonly && track(rawTarget, TrackOpTypes.GET, rawKey)
|
|
||||||
const { has } = getProto(rawTarget)
|
const { has } = getProto(rawTarget)
|
||||||
const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
|
const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
|
||||||
if (has.call(rawTarget, key)) {
|
if (has.call(rawTarget, key)) {
|
||||||
|
@ -47,10 +49,12 @@ function has(this: CollectionTypes, key: unknown, isReadonly = false): boolean {
|
||||||
const target = (this as any)[ReactiveFlags.RAW]
|
const target = (this as any)[ReactiveFlags.RAW]
|
||||||
const rawTarget = toRaw(target)
|
const rawTarget = toRaw(target)
|
||||||
const rawKey = toRaw(key)
|
const rawKey = toRaw(key)
|
||||||
if (key !== rawKey) {
|
if (!isReadonly) {
|
||||||
!isReadonly && track(rawTarget, TrackOpTypes.HAS, key)
|
if (key !== rawKey) {
|
||||||
|
track(rawTarget, TrackOpTypes.HAS, key)
|
||||||
|
}
|
||||||
|
track(rawTarget, TrackOpTypes.HAS, rawKey)
|
||||||
}
|
}
|
||||||
!isReadonly && track(rawTarget, TrackOpTypes.HAS, rawKey)
|
|
||||||
return key === rawKey
|
return key === rawKey
|
||||||
? target.has(key)
|
? target.has(key)
|
||||||
: target.has(key) || target.has(rawKey)
|
: target.has(key) || target.has(rawKey)
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class EffectScope {
|
||||||
cleanups: (() => void)[] = []
|
cleanups: (() => void)[] = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* only assinged by undetached scope
|
* only assigned by undetached scope
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
parent: EffectScope | undefined
|
parent: EffectScope | undefined
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
shallowCollectionHandlers,
|
shallowCollectionHandlers,
|
||||||
shallowReadonlyCollectionHandlers
|
shallowReadonlyCollectionHandlers
|
||||||
} from './collectionHandlers'
|
} from './collectionHandlers'
|
||||||
import { UnwrapRefSimple, Ref } from './ref'
|
import type { UnwrapRefSimple, Ref, RawSymbol } from './ref'
|
||||||
|
|
||||||
export const enum ReactiveFlags {
|
export const enum ReactiveFlags {
|
||||||
SKIP = '__v_skip',
|
SKIP = '__v_skip',
|
||||||
|
@ -204,7 +204,7 @@ function createReactiveObject(
|
||||||
if (existingProxy) {
|
if (existingProxy) {
|
||||||
return existingProxy
|
return existingProxy
|
||||||
}
|
}
|
||||||
// only a whitelist of value types can be observed.
|
// only specific value types can be observed.
|
||||||
const targetType = getTargetType(target)
|
const targetType = getTargetType(target)
|
||||||
if (targetType === TargetType.INVALID) {
|
if (targetType === TargetType.INVALID) {
|
||||||
return target
|
return target
|
||||||
|
@ -241,7 +241,9 @@ export function toRaw<T>(observed: T): T {
|
||||||
return raw ? toRaw(raw) : observed
|
return raw ? toRaw(raw) : observed
|
||||||
}
|
}
|
||||||
|
|
||||||
export function markRaw<T extends object>(value: T): T {
|
export function markRaw<T extends object>(
|
||||||
|
value: T
|
||||||
|
): T & { [RawSymbol]?: true } {
|
||||||
def(value, ReactiveFlags.SKIP, true)
|
def(value, ReactiveFlags.SKIP, true)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { CollectionTypes } from './collectionHandlers'
|
||||||
import { createDep, Dep } from './dep'
|
import { createDep, Dep } from './dep'
|
||||||
|
|
||||||
declare const RefSymbol: unique symbol
|
declare const RefSymbol: unique symbol
|
||||||
|
export declare const RawSymbol: unique symbol
|
||||||
|
|
||||||
export interface Ref<T = any> {
|
export interface Ref<T = any> {
|
||||||
value: T
|
value: T
|
||||||
|
@ -291,6 +292,7 @@ export type UnwrapRefSimple<T> = T extends
|
||||||
| BaseTypes
|
| BaseTypes
|
||||||
| Ref
|
| Ref
|
||||||
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
|
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
|
||||||
|
| { [RawSymbol]?: true }
|
||||||
? T
|
? T
|
||||||
: T extends Array<any>
|
: T extends Array<any>
|
||||||
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
|
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
|
||||||
|
|
|
@ -4,9 +4,11 @@ import {
|
||||||
Component,
|
Component,
|
||||||
ref,
|
ref,
|
||||||
nextTick,
|
nextTick,
|
||||||
Suspense
|
Suspense,
|
||||||
|
KeepAlive
|
||||||
} from '../src'
|
} from '../src'
|
||||||
import { createApp, nodeOps, serializeInner } from '@vue/runtime-test'
|
import { createApp, nodeOps, serializeInner } from '@vue/runtime-test'
|
||||||
|
import { onActivated } from '../src/components/KeepAlive'
|
||||||
|
|
||||||
const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n))
|
const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n))
|
||||||
|
|
||||||
|
@ -799,4 +801,44 @@ describe('api: defineAsyncComponent', () => {
|
||||||
expect(vnodeHooks.onVnodeBeforeUnmount).toHaveBeenCalledTimes(1)
|
expect(vnodeHooks.onVnodeBeforeUnmount).toHaveBeenCalledTimes(1)
|
||||||
expect(vnodeHooks.onVnodeUnmounted).toHaveBeenCalledTimes(1)
|
expect(vnodeHooks.onVnodeUnmounted).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('with KeepAlive', async () => {
|
||||||
|
const spy = jest.fn()
|
||||||
|
let resolve: (comp: Component) => void
|
||||||
|
|
||||||
|
const Foo = defineAsyncComponent(
|
||||||
|
() =>
|
||||||
|
new Promise(r => {
|
||||||
|
resolve = r as any
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const Bar = defineAsyncComponent(() => Promise.resolve(() => 'Bar'))
|
||||||
|
|
||||||
|
const toggle = ref(true)
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
const app = createApp({
|
||||||
|
render: () => h(KeepAlive, [toggle.value ? h(Foo) : h(Bar)])
|
||||||
|
})
|
||||||
|
|
||||||
|
app.mount(root)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
resolve!({
|
||||||
|
setup() {
|
||||||
|
onActivated(() => {
|
||||||
|
spy()
|
||||||
|
})
|
||||||
|
return () => 'Foo'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await timeout()
|
||||||
|
expect(serializeInner(root)).toBe('Foo')
|
||||||
|
expect(spy).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
toggle.value = false
|
||||||
|
await timeout()
|
||||||
|
expect(serializeInner(root)).toBe('Bar')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -30,6 +30,11 @@ describe('api: createApp', () => {
|
||||||
const root1 = nodeOps.createElement('div')
|
const root1 = nodeOps.createElement('div')
|
||||||
createApp(Comp).mount(root1)
|
createApp(Comp).mount(root1)
|
||||||
expect(serializeInner(root1)).toBe(`0`)
|
expect(serializeInner(root1)).toBe(`0`)
|
||||||
|
//#5571 mount multiple apps to the same host element
|
||||||
|
createApp(Comp).mount(root1)
|
||||||
|
expect(
|
||||||
|
`There is already an app instance mounted on the host container`
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
|
||||||
// mount with props
|
// mount with props
|
||||||
const root2 = nodeOps.createElement('div')
|
const root2 = nodeOps.createElement('div')
|
||||||
|
|
|
@ -334,7 +334,10 @@ describe('api: lifecycle hooks', () => {
|
||||||
const onTrigger = jest.fn((e: DebuggerEvent) => {
|
const onTrigger = jest.fn((e: DebuggerEvent) => {
|
||||||
events.push(e)
|
events.push(e)
|
||||||
})
|
})
|
||||||
const obj = reactive({ foo: 1, bar: 2 })
|
const obj = reactive<{
|
||||||
|
foo: number
|
||||||
|
bar?: number
|
||||||
|
}>({ foo: 1, bar: 2 })
|
||||||
|
|
||||||
const Comp = {
|
const Comp = {
|
||||||
setup() {
|
setup() {
|
||||||
|
@ -356,7 +359,6 @@ describe('api: lifecycle hooks', () => {
|
||||||
newValue: 2
|
newValue: 2
|
||||||
})
|
})
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
delete obj.bar
|
delete obj.bar
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(onTrigger).toHaveBeenCalledTimes(2)
|
expect(onTrigger).toHaveBeenCalledTimes(2)
|
||||||
|
|
|
@ -1411,7 +1411,7 @@ describe('api: options', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
render(h(Comp), root)
|
render(h(Comp), root)
|
||||||
|
|
||||||
expect('Invalid watch option: "foo"').toHaveBeenWarned()
|
expect('Invalid watch option: "foo"').toHaveBeenWarned()
|
||||||
|
|
|
@ -214,7 +214,7 @@ describe('api: watch', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('warn invalid watch source', () => {
|
it('warn invalid watch source', () => {
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
watch(1, () => {})
|
watch(1, () => {})
|
||||||
expect(`Invalid watch source`).toHaveBeenWarned()
|
expect(`Invalid watch source`).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
@ -748,7 +748,7 @@ describe('api: watch', () => {
|
||||||
() => {
|
() => {
|
||||||
dummy = count.value
|
dummy = count.value
|
||||||
},
|
},
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
{ immediate: false }
|
{ immediate: false }
|
||||||
)
|
)
|
||||||
expect(dummy).toBe(0)
|
expect(dummy).toBe(0)
|
||||||
|
@ -767,7 +767,7 @@ describe('api: watch', () => {
|
||||||
spy()
|
spy()
|
||||||
return arr
|
return arr
|
||||||
},
|
},
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
expect(spy).toHaveBeenCalledTimes(1)
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
@ -818,7 +818,7 @@ describe('api: watch', () => {
|
||||||
const onTrigger = jest.fn((e: DebuggerEvent) => {
|
const onTrigger = jest.fn((e: DebuggerEvent) => {
|
||||||
events.push(e)
|
events.push(e)
|
||||||
})
|
})
|
||||||
const obj = reactive({ foo: 1 })
|
const obj = reactive<{ foo?: number }>({ foo: 1 })
|
||||||
watchEffect(
|
watchEffect(
|
||||||
() => {
|
() => {
|
||||||
dummy = obj.foo
|
dummy = obj.foo
|
||||||
|
@ -828,7 +828,7 @@ describe('api: watch', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(dummy).toBe(1)
|
expect(dummy).toBe(1)
|
||||||
|
|
||||||
obj.foo++
|
obj.foo!++
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(dummy).toBe(2)
|
expect(dummy).toBe(2)
|
||||||
expect(onTrigger).toHaveBeenCalledTimes(1)
|
expect(onTrigger).toHaveBeenCalledTimes(1)
|
||||||
|
@ -839,7 +839,6 @@ describe('api: watch', () => {
|
||||||
newValue: 2
|
newValue: 2
|
||||||
})
|
})
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
delete obj.foo
|
delete obj.foo
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(dummy).toBeUndefined()
|
expect(dummy).toBeUndefined()
|
||||||
|
@ -893,6 +892,21 @@ describe('api: watch', () => {
|
||||||
expect(sideEffect).toBe(2)
|
expect(sideEffect).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should force trigger on triggerRef when watching multiple sources: shallow ref array', async () => {
|
||||||
|
const v = shallowRef([] as any)
|
||||||
|
const spy = jest.fn()
|
||||||
|
watch([v], () => {
|
||||||
|
spy()
|
||||||
|
})
|
||||||
|
|
||||||
|
v.value.push(1)
|
||||||
|
triggerRef(v)
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
// should trigger now
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
// #2125
|
// #2125
|
||||||
test('watchEffect should not recursively trigger itself', async () => {
|
test('watchEffect should not recursively trigger itself', async () => {
|
||||||
const spy = jest.fn()
|
const spy = jest.fn()
|
||||||
|
|
|
@ -153,7 +153,7 @@ describe('component: emit', () => {
|
||||||
emits: ['foo'],
|
emits: ['foo'],
|
||||||
render() {},
|
render() {},
|
||||||
created() {
|
created() {
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
this.$emit('bar')
|
this.$emit('bar')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -170,7 +170,7 @@ describe('component: emit', () => {
|
||||||
},
|
},
|
||||||
render() {},
|
render() {},
|
||||||
created() {
|
created() {
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
this.$emit('bar')
|
this.$emit('bar')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -186,7 +186,7 @@ describe('component: emit', () => {
|
||||||
emits: [],
|
emits: [],
|
||||||
render() {},
|
render() {},
|
||||||
created() {
|
created() {
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
this.$emit('foo')
|
this.$emit('foo')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -355,6 +355,37 @@ describe('component: emit', () => {
|
||||||
expect(fn2).toHaveBeenCalledWith('two')
|
expect(fn2).toHaveBeenCalledWith('two')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('.trim and .number modifiers should work with v-model on component', () => {
|
||||||
|
const Foo = defineComponent({
|
||||||
|
render() {},
|
||||||
|
created() {
|
||||||
|
this.$emit('update:modelValue', ' +01.2 ')
|
||||||
|
this.$emit('update:foo', ' 1 ')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const fn1 = jest.fn()
|
||||||
|
const fn2 = jest.fn()
|
||||||
|
|
||||||
|
const Comp = () =>
|
||||||
|
h(Foo, {
|
||||||
|
modelValue: null,
|
||||||
|
modelModifiers: { trim: true, number: true },
|
||||||
|
'onUpdate:modelValue': fn1,
|
||||||
|
|
||||||
|
foo: null,
|
||||||
|
fooModifiers: { trim: true, number: true },
|
||||||
|
'onUpdate:foo': fn2
|
||||||
|
})
|
||||||
|
|
||||||
|
render(h(Comp), nodeOps.createElement('div'))
|
||||||
|
|
||||||
|
expect(fn1).toHaveBeenCalledTimes(1)
|
||||||
|
expect(fn1).toHaveBeenCalledWith(1.2)
|
||||||
|
expect(fn2).toHaveBeenCalledTimes(1)
|
||||||
|
expect(fn2).toHaveBeenCalledWith(1)
|
||||||
|
})
|
||||||
|
|
||||||
test('isEmitListener', () => {
|
test('isEmitListener', () => {
|
||||||
const options = {
|
const options = {
|
||||||
click: null,
|
click: null,
|
||||||
|
|
|
@ -257,7 +257,6 @@ describe('component: proxy', () => {
|
||||||
expect(instanceProxy.isDisplayed).toBe(true)
|
expect(instanceProxy.isDisplayed).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
test('allow jest spying on proxy methods with Object.defineProperty', () => {
|
test('allow jest spying on proxy methods with Object.defineProperty', () => {
|
||||||
// #5417
|
// #5417
|
||||||
let instanceProxy: any
|
let instanceProxy: any
|
||||||
|
@ -426,7 +425,6 @@ describe('component: proxy', () => {
|
||||||
expect(instanceProxy.fromProp).toBe(false)
|
expect(instanceProxy.fromProp).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// #864
|
// #864
|
||||||
test('should not warn declared but absent props', () => {
|
test('should not warn declared but absent props', () => {
|
||||||
const Comp = {
|
const Comp = {
|
||||||
|
|
|
@ -18,7 +18,12 @@ import {
|
||||||
defineAsyncComponent,
|
defineAsyncComponent,
|
||||||
Component,
|
Component,
|
||||||
createApp,
|
createApp,
|
||||||
onActivated
|
onActivated,
|
||||||
|
onUnmounted,
|
||||||
|
onMounted,
|
||||||
|
reactive,
|
||||||
|
shallowRef,
|
||||||
|
onDeactivated
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { KeepAliveProps } from '../../src/components/KeepAlive'
|
import { KeepAliveProps } from '../../src/components/KeepAlive'
|
||||||
|
|
||||||
|
@ -903,4 +908,73 @@ describe('KeepAlive', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(handler).toHaveBeenCalledWith(err, {}, 'activated hook')
|
expect(handler).toHaveBeenCalledWith(err, {}, 'activated hook')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #3648
|
||||||
|
test('should avoid unmount later included components', async () => {
|
||||||
|
const unmountedA = jest.fn()
|
||||||
|
const mountedA = jest.fn()
|
||||||
|
const activatedA = jest.fn()
|
||||||
|
const deactivatedA = jest.fn()
|
||||||
|
const unmountedB = jest.fn()
|
||||||
|
const mountedB = jest.fn()
|
||||||
|
|
||||||
|
const A = {
|
||||||
|
name: 'A',
|
||||||
|
setup() {
|
||||||
|
onMounted(mountedA)
|
||||||
|
onUnmounted(unmountedA)
|
||||||
|
onActivated(activatedA)
|
||||||
|
onDeactivated(deactivatedA)
|
||||||
|
return () => 'A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const B = {
|
||||||
|
name: 'B',
|
||||||
|
setup() {
|
||||||
|
onMounted(mountedB)
|
||||||
|
onUnmounted(unmountedB)
|
||||||
|
return () => 'B'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const include = reactive<string[]>([])
|
||||||
|
const current = shallowRef(A)
|
||||||
|
const app = createApp({
|
||||||
|
setup() {
|
||||||
|
return () => {
|
||||||
|
return [
|
||||||
|
h(
|
||||||
|
KeepAlive,
|
||||||
|
{
|
||||||
|
include
|
||||||
|
},
|
||||||
|
h(current.value)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.mount(root)
|
||||||
|
|
||||||
|
expect(serializeInner(root)).toBe(`A`)
|
||||||
|
expect(mountedA).toHaveBeenCalledTimes(1)
|
||||||
|
expect(unmountedA).toHaveBeenCalledTimes(0)
|
||||||
|
expect(activatedA).toHaveBeenCalledTimes(0)
|
||||||
|
expect(deactivatedA).toHaveBeenCalledTimes(0)
|
||||||
|
expect(mountedB).toHaveBeenCalledTimes(0)
|
||||||
|
expect(unmountedB).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
include.push('A') // cache A
|
||||||
|
await nextTick()
|
||||||
|
current.value = B // toggle to B
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(`B`)
|
||||||
|
expect(mountedA).toHaveBeenCalledTimes(1)
|
||||||
|
expect(unmountedA).toHaveBeenCalledTimes(0)
|
||||||
|
expect(activatedA).toHaveBeenCalledTimes(0)
|
||||||
|
expect(deactivatedA).toHaveBeenCalledTimes(1)
|
||||||
|
expect(mountedB).toHaveBeenCalledTimes(1)
|
||||||
|
expect(unmountedB).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1032,7 +1032,7 @@ describe('Suspense', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(deps.length).toBe(2)
|
expect(deps.length).toBe(2)
|
||||||
|
|
||||||
// switch before two resovles
|
// switch before two resolves
|
||||||
view.value = Three
|
view.value = Three
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(deps.length).toBe(3)
|
expect(deps.length).toBe(3)
|
||||||
|
@ -1098,7 +1098,7 @@ describe('Suspense', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(deps.length).toBe(2)
|
expect(deps.length).toBe(2)
|
||||||
|
|
||||||
// switch back before two resovles
|
// switch back before two resolves
|
||||||
view.value = One
|
view.value = One
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(deps.length).toBe(2)
|
expect(deps.length).toBe(2)
|
||||||
|
|
|
@ -183,7 +183,7 @@ describe('error handling', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// unlike other lifecycle hooks, created/beforeCreate are called as part of
|
// unlike other lifecycle hooks, created/beforeCreate are called as part of
|
||||||
// the options API initiualization process instead of by the renderer.
|
// the options API initialization process instead of by the renderer.
|
||||||
test('in created/beforeCreate hook', () => {
|
test('in created/beforeCreate hook', () => {
|
||||||
const err = new Error('foo')
|
const err = new Error('foo')
|
||||||
const fn = jest.fn()
|
const fn = jest.fn()
|
||||||
|
|
|
@ -22,7 +22,9 @@ describe('renderList', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should warn when given a non-integer N', () => {
|
it('should warn when given a non-integer N', () => {
|
||||||
renderList(3.1, () => {})
|
try {
|
||||||
|
renderList(3.1, () => {})
|
||||||
|
} catch (e) {}
|
||||||
expect(
|
expect(
|
||||||
`The v-for range expect an integer value but got 3.1.`
|
`The v-for range expect an integer value but got 3.1.`
|
||||||
).toHaveBeenWarned()
|
).toHaveBeenWarned()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// since v-memo really is a compiler + runtime combo feature, we are performing
|
// since v-memo really is a compiler + runtime combo feature, we are performing
|
||||||
// more of an itegration test here.
|
// more of an integration test here.
|
||||||
import { ComponentOptions, createApp, nextTick } from 'vue'
|
import { ComponentOptions, createApp, nextTick } from 'vue'
|
||||||
|
|
||||||
describe('v-memo', () => {
|
describe('v-memo', () => {
|
||||||
|
@ -210,4 +210,17 @@ describe('v-memo', () => {
|
||||||
// should update
|
// should update
|
||||||
expect(el.innerHTML).toBe(`<div>2</div><div>2</div><div>2</div>`)
|
expect(el.innerHTML).toBe(`<div>2</div><div>2</div><div>2</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('v-memo dependency is NaN should be equal', async () => {
|
||||||
|
const [el, vm] = mount({
|
||||||
|
template: `<div v-memo="[x]">{{ y }}</div>`,
|
||||||
|
data: () => ({ x: NaN, y: 0 })
|
||||||
|
})
|
||||||
|
expect(el.innerHTML).toBe(`<div>0</div>`)
|
||||||
|
|
||||||
|
vm.y++
|
||||||
|
// should not update
|
||||||
|
await nextTick()
|
||||||
|
expect(el.innerHTML).toBe(`<div>0</div>`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -879,7 +879,7 @@ describe('renderer: optimized mode', () => {
|
||||||
// #3881
|
// #3881
|
||||||
// root cause: fragment inside a compiled slot passed to component which
|
// root cause: fragment inside a compiled slot passed to component which
|
||||||
// programmatically invokes the slot. The entire slot should de-opt but
|
// programmatically invokes the slot. The entire slot should de-opt but
|
||||||
// the fragment was incorretly put in optimized mode which causes it to skip
|
// the fragment was incorrectly put in optimized mode which causes it to skip
|
||||||
// updates for its inner components.
|
// updates for its inner components.
|
||||||
test('fragments inside programmatically invoked compiled slot should de-opt properly', async () => {
|
test('fragments inside programmatically invoked compiled slot should de-opt properly', async () => {
|
||||||
const Parent: FunctionalComponent = (_, { slots }) => slots.default!()
|
const Parent: FunctionalComponent = (_, { slots }) => slots.default!()
|
||||||
|
|
|
@ -443,10 +443,8 @@ describe('api: template refs', () => {
|
||||||
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
test('named ref in v-for', async () => {
|
test('named ref in v-for', async () => {
|
||||||
const show = ref(true);
|
const show = ref(true)
|
||||||
const list = reactive([1, 2, 3])
|
const list = reactive([1, 2, 3])
|
||||||
const listRefs = ref([])
|
const listRefs = ref([])
|
||||||
const mapRefs = () => listRefs.value.map(n => serializeInner(n))
|
const mapRefs = () => listRefs.value.map(n => serializeInner(n))
|
||||||
|
@ -495,6 +493,4 @@ describe('api: template refs', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -541,18 +541,6 @@ describe('scheduler', () => {
|
||||||
expect(count).toBe(5)
|
expect(count).toBe(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should prevent duplicate queue', async () => {
|
|
||||||
let count = 0
|
|
||||||
const job = () => {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
job.cb = true
|
|
||||||
queueJob(job)
|
|
||||||
queueJob(job)
|
|
||||||
await nextTick()
|
|
||||||
expect(count).toBe(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
// #1947 flushPostFlushCbs should handle nested calls
|
// #1947 flushPostFlushCbs should handle nested calls
|
||||||
// e.g. app.mount inside app.mount
|
// e.g. app.mount inside app.mount
|
||||||
test('flushPostFlushCbs', async () => {
|
test('flushPostFlushCbs', async () => {
|
||||||
|
|
|
@ -111,7 +111,7 @@ export function defineAsyncComponent<
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return defineComponent({
|
return defineComponent<{}>({
|
||||||
name: 'AsyncComponentWrapper',
|
name: 'AsyncComponentWrapper',
|
||||||
|
|
||||||
__asyncLoader: load,
|
__asyncLoader: load,
|
||||||
|
@ -211,7 +211,10 @@ export function defineAsyncComponent<
|
||||||
|
|
||||||
function createInnerComp(
|
function createInnerComp(
|
||||||
comp: ConcreteComponent,
|
comp: ConcreteComponent,
|
||||||
{ vnode: { ref, props, children } }: ComponentInternalInstance
|
{
|
||||||
|
vnode: { ref, props, children, shapeFlag },
|
||||||
|
parent
|
||||||
|
}: ComponentInternalInstance
|
||||||
) {
|
) {
|
||||||
const vnode = createVNode(comp, props, children)
|
const vnode = createVNode(comp, props, children)
|
||||||
// ensure inner component inherits the async wrapper's ref owner
|
// ensure inner component inherits the async wrapper's ref owner
|
||||||
|
|
|
@ -284,6 +284,14 @@ export function createAppAPI<HostElement>(
|
||||||
isSVG?: boolean
|
isSVG?: boolean
|
||||||
): any {
|
): any {
|
||||||
if (!isMounted) {
|
if (!isMounted) {
|
||||||
|
// #5571
|
||||||
|
if (__DEV__ && (rootContainer as any).__vue_app__) {
|
||||||
|
warn(
|
||||||
|
`There is already an app instance mounted on the host container.\n` +
|
||||||
|
` If you want to mount another app on the same host container,` +
|
||||||
|
` you need to unmount the previous app by calling \`app.unmount()\` first.`
|
||||||
|
)
|
||||||
|
}
|
||||||
const vnode = createVNode(
|
const vnode = createVNode(
|
||||||
rootComponent as ConcreteComponent,
|
rootComponent as ConcreteComponent,
|
||||||
rootProps
|
rootProps
|
||||||
|
@ -345,9 +353,8 @@ export function createAppAPI<HostElement>(
|
||||||
`It will be overwritten with the new value.`
|
`It will be overwritten with the new value.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// TypeScript doesn't allow symbols as index type
|
|
||||||
// https://github.com/Microsoft/TypeScript/issues/24587
|
context.provides[key as string | symbol] = value
|
||||||
context.provides[key as string] = value
|
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ import {
|
||||||
ComponentOptionsWithObjectProps,
|
ComponentOptionsWithObjectProps,
|
||||||
ComponentOptionsMixin,
|
ComponentOptionsMixin,
|
||||||
RenderFunction,
|
RenderFunction,
|
||||||
ComponentOptionsBase
|
ComponentOptionsBase,
|
||||||
|
ComponentProvideOptions
|
||||||
} from './componentOptions'
|
} from './componentOptions'
|
||||||
import {
|
import {
|
||||||
SetupContext,
|
SetupContext,
|
||||||
|
@ -40,6 +41,8 @@ export type DefineComponent<
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = {},
|
E extends EmitsOptions = {},
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
|
RawOptions extends {} = {},
|
||||||
PP = PublicProps,
|
PP = PublicProps,
|
||||||
Props = Readonly<
|
Props = Readonly<
|
||||||
PropsOrPropOptions extends ComponentPropsOptions
|
PropsOrPropOptions extends ComponentPropsOptions
|
||||||
|
@ -48,22 +51,23 @@ export type DefineComponent<
|
||||||
> &
|
> &
|
||||||
({} extends E ? {} : EmitsToProps<E>),
|
({} extends E ? {} : EmitsToProps<E>),
|
||||||
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
|
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
|
||||||
> = ComponentPublicInstanceConstructor<
|
> = RawOptions &
|
||||||
CreateComponentPublicInstance<
|
ComponentPublicInstanceConstructor<
|
||||||
Props,
|
CreateComponentPublicInstance<
|
||||||
RawBindings,
|
Props,
|
||||||
D,
|
RawBindings,
|
||||||
C,
|
D,
|
||||||
M,
|
C,
|
||||||
Mixin,
|
M,
|
||||||
Extends,
|
Mixin,
|
||||||
E,
|
Extends,
|
||||||
PP & Props,
|
E,
|
||||||
Defaults,
|
PP & Props,
|
||||||
true
|
Defaults,
|
||||||
|
true
|
||||||
|
> &
|
||||||
|
Props
|
||||||
> &
|
> &
|
||||||
Props
|
|
||||||
> &
|
|
||||||
ComponentOptionsBase<
|
ComponentOptionsBase<
|
||||||
Props,
|
Props,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
|
@ -74,7 +78,8 @@ export type DefineComponent<
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
EE,
|
EE,
|
||||||
Defaults
|
Defaults,
|
||||||
|
Provide
|
||||||
> &
|
> &
|
||||||
PP
|
PP
|
||||||
|
|
||||||
|
@ -104,20 +109,36 @@ export function defineComponent<
|
||||||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string
|
EE extends string = string,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
|
Options extends {} = {}
|
||||||
>(
|
>(
|
||||||
options: ComponentOptionsWithoutProps<
|
options: Options &
|
||||||
Props,
|
ComponentOptionsWithoutProps<
|
||||||
RawBindings,
|
Props,
|
||||||
D,
|
RawBindings,
|
||||||
C,
|
D,
|
||||||
M,
|
C,
|
||||||
Mixin,
|
M,
|
||||||
Extends,
|
Mixin,
|
||||||
E,
|
Extends,
|
||||||
EE
|
E,
|
||||||
>
|
EE,
|
||||||
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
Provide
|
||||||
|
>
|
||||||
|
): DefineComponent<
|
||||||
|
Props,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
EE,
|
||||||
|
Provide,
|
||||||
|
Options
|
||||||
|
>
|
||||||
|
|
||||||
// overload 3: object format with array props declaration
|
// overload 3: object format with array props declaration
|
||||||
// props inferred as { [key in PropNames]?: any }
|
// props inferred as { [key in PropNames]?: any }
|
||||||
|
@ -131,19 +152,23 @@ export function defineComponent<
|
||||||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = Record<string, any>,
|
E extends EmitsOptions = Record<string, any>,
|
||||||
EE extends string = string
|
EE extends string = string,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
|
Options extends {} = {}
|
||||||
>(
|
>(
|
||||||
options: ComponentOptionsWithArrayProps<
|
options: Options &
|
||||||
PropNames,
|
ComponentOptionsWithArrayProps<
|
||||||
RawBindings,
|
PropNames,
|
||||||
D,
|
RawBindings,
|
||||||
C,
|
D,
|
||||||
M,
|
C,
|
||||||
Mixin,
|
M,
|
||||||
Extends,
|
Mixin,
|
||||||
E,
|
Extends,
|
||||||
EE
|
E,
|
||||||
>
|
EE,
|
||||||
|
Provide
|
||||||
|
>
|
||||||
): DefineComponent<
|
): DefineComponent<
|
||||||
Readonly<{ [key in PropNames]?: any }>,
|
Readonly<{ [key in PropNames]?: any }>,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
|
@ -153,7 +178,9 @@ export function defineComponent<
|
||||||
Mixin,
|
Mixin,
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
EE
|
EE,
|
||||||
|
Provide,
|
||||||
|
Options
|
||||||
>
|
>
|
||||||
|
|
||||||
// overload 4: object format with object props declaration
|
// overload 4: object format with object props declaration
|
||||||
|
@ -169,20 +196,36 @@ export function defineComponent<
|
||||||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = Record<string, any>,
|
E extends EmitsOptions = Record<string, any>,
|
||||||
EE extends string = string
|
EE extends string = string,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
|
Options extends {} = {}
|
||||||
>(
|
>(
|
||||||
options: ComponentOptionsWithObjectProps<
|
options: Options &
|
||||||
PropsOptions,
|
ComponentOptionsWithObjectProps<
|
||||||
RawBindings,
|
PropsOptions,
|
||||||
D,
|
RawBindings,
|
||||||
C,
|
D,
|
||||||
M,
|
C,
|
||||||
Mixin,
|
M,
|
||||||
Extends,
|
Mixin,
|
||||||
E,
|
Extends,
|
||||||
EE
|
E,
|
||||||
>
|
EE,
|
||||||
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
Provide
|
||||||
|
>
|
||||||
|
): DefineComponent<
|
||||||
|
PropsOptions,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
EE,
|
||||||
|
Provide,
|
||||||
|
Options
|
||||||
|
>
|
||||||
|
|
||||||
// implementation, close to no-op
|
// implementation, close to no-op
|
||||||
export function defineComponent(options: unknown) {
|
export function defineComponent(options: unknown) {
|
||||||
|
|
|
@ -212,7 +212,7 @@ function doWatch(
|
||||||
deep = true
|
deep = true
|
||||||
} else if (isArray(source)) {
|
} else if (isArray(source)) {
|
||||||
isMultiSource = true
|
isMultiSource = true
|
||||||
forceTrigger = source.some(isReactive)
|
forceTrigger = source.some(s => isReactive(s) || isShallow(s))
|
||||||
getter = () =>
|
getter = () =>
|
||||||
source.map(s => {
|
source.map(s => {
|
||||||
if (isRef(s)) {
|
if (isRef(s)) {
|
||||||
|
|
|
@ -440,6 +440,15 @@ export interface ComponentInternalInstance {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
[LifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
|
[LifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For caching bound $forceUpdate on public proxy access
|
||||||
|
*/
|
||||||
|
f?: () => void
|
||||||
|
/**
|
||||||
|
* For caching bound $nextTick on public proxy access
|
||||||
|
*/
|
||||||
|
n?: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyAppContext = createAppContext()
|
const emptyAppContext = createAppContext()
|
||||||
|
@ -477,7 +486,7 @@ export function createComponentInstance(
|
||||||
accessCache: null!,
|
accessCache: null!,
|
||||||
renderCache: [],
|
renderCache: [],
|
||||||
|
|
||||||
// local resovled assets
|
// local resolved assets
|
||||||
components: null,
|
components: null,
|
||||||
directives: null,
|
directives: null,
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,8 @@ export function emit(
|
||||||
const { number, trim } = props[modifiersKey] || EMPTY_OBJ
|
const { number, trim } = props[modifiersKey] || EMPTY_OBJ
|
||||||
if (trim) {
|
if (trim) {
|
||||||
args = rawArgs.map(a => a.trim())
|
args = rawArgs.map(a => a.trim())
|
||||||
} else if (number) {
|
}
|
||||||
|
if (number) {
|
||||||
args = rawArgs.map(toNumber)
|
args = rawArgs.map(toNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,8 +117,9 @@ export interface ComponentOptionsBase<
|
||||||
Extends extends ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin,
|
||||||
E extends EmitsOptions,
|
E extends EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
Defaults = {}
|
Defaults = {},
|
||||||
> extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
|
Provide extends ComponentProvideOptions = ComponentProvideOptions
|
||||||
|
> extends LegacyOptions<Props, D, C, M, Mixin, Extends, Provide>,
|
||||||
ComponentInternalOptions,
|
ComponentInternalOptions,
|
||||||
ComponentCustomOptions {
|
ComponentCustomOptions {
|
||||||
setup?: (
|
setup?: (
|
||||||
|
@ -224,6 +225,7 @@ export type ComponentOptionsWithoutProps<
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
PE = Props & EmitsToProps<E>
|
PE = Props & EmitsToProps<E>
|
||||||
> = ComponentOptionsBase<
|
> = ComponentOptionsBase<
|
||||||
PE,
|
PE,
|
||||||
|
@ -235,7 +237,8 @@ export type ComponentOptionsWithoutProps<
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
EE,
|
EE,
|
||||||
{}
|
{},
|
||||||
|
Provide
|
||||||
> & {
|
> & {
|
||||||
props?: undefined
|
props?: undefined
|
||||||
} & ThisType<
|
} & ThisType<
|
||||||
|
@ -252,6 +255,7 @@ export type ComponentOptionsWithArrayProps<
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps<E>
|
Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps<E>
|
||||||
> = ComponentOptionsBase<
|
> = ComponentOptionsBase<
|
||||||
Props,
|
Props,
|
||||||
|
@ -263,7 +267,8 @@ export type ComponentOptionsWithArrayProps<
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
EE,
|
EE,
|
||||||
{}
|
{},
|
||||||
|
Provide
|
||||||
> & {
|
> & {
|
||||||
props: PropNames[]
|
props: PropNames[]
|
||||||
} & ThisType<
|
} & ThisType<
|
||||||
|
@ -289,6 +294,7 @@ export type ComponentOptionsWithObjectProps<
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||||
Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
|
Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
|
||||||
Defaults = ExtractDefaultPropTypes<PropsOptions>
|
Defaults = ExtractDefaultPropTypes<PropsOptions>
|
||||||
> = ComponentOptionsBase<
|
> = ComponentOptionsBase<
|
||||||
|
@ -301,7 +307,8 @@ export type ComponentOptionsWithObjectProps<
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
EE,
|
EE,
|
||||||
Defaults
|
Defaults,
|
||||||
|
Provide
|
||||||
> & {
|
> & {
|
||||||
props: PropsOptions & ThisType<void>
|
props: PropsOptions & ThisType<void>
|
||||||
} & ThisType<
|
} & ThisType<
|
||||||
|
@ -384,6 +391,10 @@ type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[]
|
||||||
|
|
||||||
type ComponentWatchOptions = Record<string, ComponentWatchOptionItem>
|
type ComponentWatchOptions = Record<string, ComponentWatchOptionItem>
|
||||||
|
|
||||||
|
export type ComponentProvideOptions = ObjectProvideOptions | Function
|
||||||
|
|
||||||
|
type ObjectProvideOptions = Record<string | symbol, unknown>
|
||||||
|
|
||||||
type ComponentInjectOptions = string[] | ObjectInjectOptions
|
type ComponentInjectOptions = string[] | ObjectInjectOptions
|
||||||
|
|
||||||
type ObjectInjectOptions = Record<
|
type ObjectInjectOptions = Record<
|
||||||
|
@ -397,7 +408,8 @@ interface LegacyOptions<
|
||||||
C extends ComputedOptions,
|
C extends ComputedOptions,
|
||||||
M extends MethodOptions,
|
M extends MethodOptions,
|
||||||
Mixin extends ComponentOptionsMixin,
|
Mixin extends ComponentOptionsMixin,
|
||||||
Extends extends ComponentOptionsMixin
|
Extends extends ComponentOptionsMixin,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions
|
||||||
> {
|
> {
|
||||||
compatConfig?: CompatConfig
|
compatConfig?: CompatConfig
|
||||||
|
|
||||||
|
@ -431,7 +443,7 @@ interface LegacyOptions<
|
||||||
computed?: C
|
computed?: C
|
||||||
methods?: M
|
methods?: M
|
||||||
watch?: ComponentWatchOptions
|
watch?: ComponentWatchOptions
|
||||||
provide?: Data | Function
|
provide?: Provide
|
||||||
inject?: ComponentInjectOptions
|
inject?: ComponentInjectOptions
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
|
@ -471,8 +483,8 @@ interface LegacyOptions<
|
||||||
*
|
*
|
||||||
* type-only, used to assist Mixin's type inference,
|
* type-only, used to assist Mixin's type inference,
|
||||||
* typescript will try to simplify the inferred `Mixin` type,
|
* typescript will try to simplify the inferred `Mixin` type,
|
||||||
* with the `__differenciator`, typescript won't be able to combine different mixins,
|
* with the `__differentiator`, typescript won't be able to combine different mixins,
|
||||||
* because the `__differenciator` will be different
|
* because the `__differentiator` will be different
|
||||||
*/
|
*/
|
||||||
__differentiator?: keyof D | keyof C | keyof M
|
__differentiator?: keyof D | keyof C | keyof M
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,8 @@ const enum BooleanFlags {
|
||||||
|
|
||||||
// extract props which defined with default from prop options
|
// extract props which defined with default from prop options
|
||||||
export type ExtractDefaultPropTypes<O> = O extends object
|
export type ExtractDefaultPropTypes<O> = O extends object
|
||||||
? { [K in DefaultKeys<O>]: InferPropType<O[K]> }
|
? // use `keyof Pick<O, DefaultKeys<O>>` instead of `DefaultKeys<O>` to support IDE features
|
||||||
|
{ [K in keyof Pick<O, DefaultKeys<O>>]: InferPropType<O[K]> }
|
||||||
: {}
|
: {}
|
||||||
|
|
||||||
type NormalizedProp =
|
type NormalizedProp =
|
||||||
|
@ -225,7 +226,7 @@ export function updateProps(
|
||||||
for (let i = 0; i < propsToUpdate.length; i++) {
|
for (let i = 0; i < propsToUpdate.length; i++) {
|
||||||
let key = propsToUpdate[i]
|
let key = propsToUpdate[i]
|
||||||
// skip if the prop key is a declared emit event listener
|
// skip if the prop key is a declared emit event listener
|
||||||
if (isEmitListener(instance.emitsOptions, key)){
|
if (isEmitListener(instance.emitsOptions, key)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// PROPS flag guarantees rawProps to be non-null
|
// PROPS flag guarantees rawProps to be non-null
|
||||||
|
|
|
@ -34,7 +34,8 @@ import {
|
||||||
OptionTypesKeys,
|
OptionTypesKeys,
|
||||||
resolveMergedOptions,
|
resolveMergedOptions,
|
||||||
shouldCacheAccess,
|
shouldCacheAccess,
|
||||||
MergedComponentOptionsOverride
|
MergedComponentOptionsOverride,
|
||||||
|
ComponentProvideOptions
|
||||||
} from './componentOptions'
|
} from './componentOptions'
|
||||||
import { EmitsOptions, EmitFn } from './componentEmits'
|
import { EmitsOptions, EmitFn } from './componentEmits'
|
||||||
import { Slots } from './componentSlots'
|
import { Slots } from './componentSlots'
|
||||||
|
@ -150,7 +151,8 @@ export type CreateComponentPublicInstance<
|
||||||
PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
|
PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
|
||||||
EnsureNonVoid<M>,
|
EnsureNonVoid<M>,
|
||||||
PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
|
PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
|
||||||
EnsureNonVoid<Defaults>
|
EnsureNonVoid<Defaults>,
|
||||||
|
Provide extends ComponentProvideOptions = ComponentProvideOptions
|
||||||
> = ComponentPublicInstance<
|
> = ComponentPublicInstance<
|
||||||
PublicP,
|
PublicP,
|
||||||
PublicB,
|
PublicB,
|
||||||
|
@ -161,7 +163,19 @@ export type CreateComponentPublicInstance<
|
||||||
PublicProps,
|
PublicProps,
|
||||||
PublicDefaults,
|
PublicDefaults,
|
||||||
MakeDefaultsOptional,
|
MakeDefaultsOptional,
|
||||||
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>
|
ComponentOptionsBase<
|
||||||
|
P,
|
||||||
|
B,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
string,
|
||||||
|
Defaults,
|
||||||
|
Provide
|
||||||
|
>
|
||||||
>
|
>
|
||||||
|
|
||||||
// public properties exposed on the proxy, which is used as the render context
|
// public properties exposed on the proxy, which is used as the render context
|
||||||
|
@ -238,8 +252,8 @@ export const publicPropertiesMap: PublicPropertiesMap =
|
||||||
$root: i => getPublicInstance(i.root),
|
$root: i => getPublicInstance(i.root),
|
||||||
$emit: i => i.emit,
|
$emit: i => i.emit,
|
||||||
$options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type),
|
$options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type),
|
||||||
$forceUpdate: i => () => queueJob(i.update),
|
$forceUpdate: i => i.f || (i.f = () => queueJob(i.update)),
|
||||||
$nextTick: i => nextTick.bind(i.proxy!),
|
$nextTick: i => i.n || (i.n = nextTick.bind(i.proxy!)),
|
||||||
$watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP)
|
$watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP)
|
||||||
} as PublicPropertiesMap)
|
} as PublicPropertiesMap)
|
||||||
|
|
||||||
|
|
|
@ -215,6 +215,8 @@ export function renderComponentRoot(
|
||||||
`The directives will not function as intended.`
|
`The directives will not function as intended.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// clone before mutating since the root may be a hoisted vnode
|
||||||
|
root = cloneVNode(root)
|
||||||
root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs
|
root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs
|
||||||
}
|
}
|
||||||
// inherit transition data
|
// inherit transition data
|
||||||
|
|
|
@ -63,6 +63,10 @@ const normalizeSlot = (
|
||||||
rawSlot: Function,
|
rawSlot: Function,
|
||||||
ctx: ComponentInternalInstance | null | undefined
|
ctx: ComponentInternalInstance | null | undefined
|
||||||
): Slot => {
|
): Slot => {
|
||||||
|
if ((rawSlot as any)._n) {
|
||||||
|
// already normalized - #5353
|
||||||
|
return rawSlot as Slot
|
||||||
|
}
|
||||||
const normalized = withCtx((...args: any[]) => {
|
const normalized = withCtx((...args: any[]) => {
|
||||||
if (__DEV__ && currentInstance) {
|
if (__DEV__ && currentInstance) {
|
||||||
warn(
|
warn(
|
||||||
|
|
|
@ -16,10 +16,12 @@ import { warn } from '../warning'
|
||||||
import { isKeepAlive } from './KeepAlive'
|
import { isKeepAlive } from './KeepAlive'
|
||||||
import { toRaw } from '@vue/reactivity'
|
import { toRaw } from '@vue/reactivity'
|
||||||
import { callWithAsyncErrorHandling, ErrorCodes } from '../errorHandling'
|
import { callWithAsyncErrorHandling, ErrorCodes } from '../errorHandling'
|
||||||
import { ShapeFlags, PatchFlags } from '@vue/shared'
|
import { ShapeFlags, PatchFlags, isArray } from '@vue/shared'
|
||||||
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
|
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
|
||||||
import { RendererElement } from '../renderer'
|
import { RendererElement } from '../renderer'
|
||||||
|
|
||||||
|
type Hook<T = () => void> = T | T[]
|
||||||
|
|
||||||
export interface BaseTransitionProps<HostElement = RendererElement> {
|
export interface BaseTransitionProps<HostElement = RendererElement> {
|
||||||
mode?: 'in-out' | 'out-in' | 'default'
|
mode?: 'in-out' | 'out-in' | 'default'
|
||||||
appear?: boolean
|
appear?: boolean
|
||||||
|
@ -34,20 +36,20 @@ export interface BaseTransitionProps<HostElement = RendererElement> {
|
||||||
// Hooks. Using camel case for easier usage in render functions & JSX.
|
// Hooks. Using camel case for easier usage in render functions & JSX.
|
||||||
// In templates these can be written as @before-enter="xxx" as prop names
|
// In templates these can be written as @before-enter="xxx" as prop names
|
||||||
// are camelized.
|
// are camelized.
|
||||||
onBeforeEnter?: (el: HostElement) => void
|
onBeforeEnter?: Hook<(el: HostElement) => void>
|
||||||
onEnter?: (el: HostElement, done: () => void) => void
|
onEnter?: Hook<(el: HostElement, done: () => void) => void>
|
||||||
onAfterEnter?: (el: HostElement) => void
|
onAfterEnter?: Hook<(el: HostElement) => void>
|
||||||
onEnterCancelled?: (el: HostElement) => void
|
onEnterCancelled?: Hook<(el: HostElement) => void>
|
||||||
// leave
|
// leave
|
||||||
onBeforeLeave?: (el: HostElement) => void
|
onBeforeLeave?: Hook<(el: HostElement) => void>
|
||||||
onLeave?: (el: HostElement, done: () => void) => void
|
onLeave?: Hook<(el: HostElement, done: () => void) => void>
|
||||||
onAfterLeave?: (el: HostElement) => void
|
onAfterLeave?: Hook<(el: HostElement) => void>
|
||||||
onLeaveCancelled?: (el: HostElement) => void // only fired in persisted mode
|
onLeaveCancelled?: Hook<(el: HostElement) => void> // only fired in persisted mode
|
||||||
// appear
|
// appear
|
||||||
onBeforeAppear?: (el: HostElement) => void
|
onBeforeAppear?: Hook<(el: HostElement) => void>
|
||||||
onAppear?: (el: HostElement, done: () => void) => void
|
onAppear?: Hook<(el: HostElement, done: () => void) => void>
|
||||||
onAfterAppear?: (el: HostElement) => void
|
onAfterAppear?: Hook<(el: HostElement) => void>
|
||||||
onAppearCancelled?: (el: HostElement) => void
|
onAppearCancelled?: Hook<(el: HostElement) => void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransitionHooks<
|
export interface TransitionHooks<
|
||||||
|
@ -69,9 +71,9 @@ export interface TransitionHooks<
|
||||||
delayedLeave?(): void
|
delayedLeave?(): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TransitionHookCaller = (
|
export type TransitionHookCaller = <T extends any[] = [el: any]>(
|
||||||
hook: ((el: any) => void) | Array<(el: any) => void> | undefined,
|
hook: Hook<(...args: T) => void> | undefined,
|
||||||
args?: any[]
|
args?: T
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
export type PendingCallback = (cancelled?: boolean) => void
|
export type PendingCallback = (cancelled?: boolean) => void
|
||||||
|
@ -331,6 +333,19 @@ export function resolveTransitionHooks(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const callAsyncHook = (
|
||||||
|
hook: Hook<(el: any, done: () => void) => void>,
|
||||||
|
args: [TransitionElement, () => void]
|
||||||
|
) => {
|
||||||
|
const done = args[1]
|
||||||
|
callHook(hook, args)
|
||||||
|
if (isArray(hook)) {
|
||||||
|
if (hook.every(hook => hook.length <= 1)) done()
|
||||||
|
} else if (hook.length <= 1) {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hooks: TransitionHooks<TransitionElement> = {
|
const hooks: TransitionHooks<TransitionElement> = {
|
||||||
mode,
|
mode,
|
||||||
persisted,
|
persisted,
|
||||||
|
@ -388,10 +403,7 @@ export function resolveTransitionHooks(
|
||||||
el._enterCb = undefined
|
el._enterCb = undefined
|
||||||
})
|
})
|
||||||
if (hook) {
|
if (hook) {
|
||||||
hook(el, done)
|
callAsyncHook(hook, [el, done])
|
||||||
if (hook.length <= 1) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
@ -423,10 +435,7 @@ export function resolveTransitionHooks(
|
||||||
})
|
})
|
||||||
leavingVNodesCache[key] = vnode
|
leavingVNodesCache[key] = vnode
|
||||||
if (onLeave) {
|
if (onLeave) {
|
||||||
onLeave(el, done)
|
callAsyncHook(onLeave, [el, done])
|
||||||
if (onLeave.length <= 1) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import { setTransitionHooks } from './BaseTransition'
|
||||||
import { ComponentRenderContext } from '../componentPublicInstance'
|
import { ComponentRenderContext } from '../componentPublicInstance'
|
||||||
import { devtoolsComponentAdded } from '../devtools'
|
import { devtoolsComponentAdded } from '../devtools'
|
||||||
import { isAsyncWrapper } from '../apiAsyncComponent'
|
import { isAsyncWrapper } from '../apiAsyncComponent'
|
||||||
|
import { isSuspense } from './Suspense'
|
||||||
|
|
||||||
type MatchPattern = string | RegExp | (string | RegExp)[]
|
type MatchPattern = string | RegExp | (string | RegExp)[]
|
||||||
|
|
||||||
|
@ -323,7 +324,7 @@ const KeepAliveImpl: ComponentOptions = {
|
||||||
vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
|
||||||
|
|
||||||
current = vnode
|
current = vnode
|
||||||
return rawVNode
|
return isSuspense(rawVNode.type) ? rawVNode : vnode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
import { App } from './apiCreateApp'
|
import { App } from './apiCreateApp'
|
||||||
import { Fragment, Text, Comment, Static } from './vnode'
|
import { Fragment, Text, Comment, Static } from './vnode'
|
||||||
import { ComponentInternalInstance } from './component'
|
import { ComponentInternalInstance } from './component'
|
||||||
|
@ -53,7 +54,6 @@ export function setDevtoolsHook(hook: DevtoolsHook, target: any) {
|
||||||
// handle late devtools injection - only do this if we are in an actual
|
// handle late devtools injection - only do this if we are in an actual
|
||||||
// browser environment to avoid the timer handle stalling test runner exit
|
// browser environment to avoid the timer handle stalling test runner exit
|
||||||
// (#4815)
|
// (#4815)
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
typeof window !== 'undefined' &&
|
typeof window !== 'undefined' &&
|
||||||
// some envs mock window but not fully
|
// some envs mock window but not fully
|
||||||
window.HTMLElement &&
|
window.HTMLElement &&
|
||||||
|
|
|
@ -67,7 +67,6 @@ export function renderList(
|
||||||
} else if (typeof source === 'number') {
|
} else if (typeof source === 'number') {
|
||||||
if (__DEV__ && !Number.isInteger(source)) {
|
if (__DEV__ && !Number.isInteger(source)) {
|
||||||
warn(`The v-for range expect an integer value but got ${source}.`)
|
warn(`The v-for range expect an integer value but got ${source}.`)
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
ret = new Array(source)
|
ret = new Array(source)
|
||||||
for (let i = 0; i < source; i++) {
|
for (let i = 0; i < source; i++) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { hasChanged } from '@vue/shared'
|
||||||
import { currentBlock, isBlockTreeEnabled, VNode } from '../vnode'
|
import { currentBlock, isBlockTreeEnabled, VNode } from '../vnode'
|
||||||
|
|
||||||
export function withMemo(
|
export function withMemo(
|
||||||
|
@ -22,8 +23,9 @@ export function isMemoSame(cached: VNode, memo: any[]) {
|
||||||
if (prev.length != memo.length) {
|
if (prev.length != memo.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < prev.length; i++) {
|
for (let i = 0; i < prev.length; i++) {
|
||||||
if (prev[i] !== memo[i]) {
|
if (hasChanged(prev[i], memo[i])) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,10 +97,15 @@ export function createHydrationFunctions(
|
||||||
isFragmentStart
|
isFragmentStart
|
||||||
)
|
)
|
||||||
|
|
||||||
const { type, ref, shapeFlag } = vnode
|
const { type, ref, shapeFlag, patchFlag } = vnode
|
||||||
const domType = node.nodeType
|
const domType = node.nodeType
|
||||||
vnode.el = node
|
vnode.el = node
|
||||||
|
|
||||||
|
if (patchFlag === PatchFlags.BAIL) {
|
||||||
|
optimized = false
|
||||||
|
vnode.dynamicChildren = null
|
||||||
|
}
|
||||||
|
|
||||||
let nextNode: Node | null = null
|
let nextNode: Node | null = null
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Text:
|
case Text:
|
||||||
|
|
|
@ -1098,6 +1098,8 @@ function baseCreateRenderer(
|
||||||
if (
|
if (
|
||||||
patchFlag > 0 &&
|
patchFlag > 0 &&
|
||||||
patchFlag & PatchFlags.STABLE_FRAGMENT &&
|
patchFlag & PatchFlags.STABLE_FRAGMENT &&
|
||||||
|
// #5523 dev root fragment may inherit directives so always force update
|
||||||
|
!(__DEV__ && patchFlag & PatchFlags.DEV_ROOT_FRAGMENT) &&
|
||||||
dynamicChildren &&
|
dynamicChildren &&
|
||||||
// #2715 the previous fragment could've been a BAILed one as a result
|
// #2715 the previous fragment could've been a BAILed one as a result
|
||||||
// of renderSlot() with no valid children
|
// of renderSlot() with no valid children
|
||||||
|
@ -1288,7 +1290,6 @@ function baseCreateRenderer(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no update needed. just copy over properties
|
// no update needed. just copy over properties
|
||||||
n2.component = n1.component
|
|
||||||
n2.el = n1.el
|
n2.el = n1.el
|
||||||
instance.vnode = n2
|
instance.vnode = n2
|
||||||
}
|
}
|
||||||
|
@ -1419,7 +1420,12 @@ function baseCreateRenderer(
|
||||||
// activated hook for keep-alive roots.
|
// activated hook for keep-alive roots.
|
||||||
// #1742 activated hook must be accessed after first render
|
// #1742 activated hook must be accessed after first render
|
||||||
// since the hook may be injected by a child keep-alive
|
// since the hook may be injected by a child keep-alive
|
||||||
if (initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
|
if (
|
||||||
|
initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
|
||||||
|
(parent &&
|
||||||
|
isAsyncWrapper(parent.vnode) &&
|
||||||
|
parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
|
||||||
|
) {
|
||||||
instance.a && queuePostRenderEffect(instance.a, parentSuspense)
|
instance.a && queuePostRenderEffect(instance.a, parentSuspense)
|
||||||
if (
|
if (
|
||||||
__COMPAT__ &&
|
__COMPAT__ &&
|
||||||
|
@ -1544,11 +1550,11 @@ function baseCreateRenderer(
|
||||||
// create reactive effect for rendering
|
// create reactive effect for rendering
|
||||||
const effect = (instance.effect = new ReactiveEffect(
|
const effect = (instance.effect = new ReactiveEffect(
|
||||||
componentUpdateFn,
|
componentUpdateFn,
|
||||||
() => queueJob(instance.update),
|
() => queueJob(update),
|
||||||
instance.scope // track it in component's effect scope
|
instance.scope // track it in component's effect scope
|
||||||
))
|
))
|
||||||
|
|
||||||
const update = (instance.update = effect.run.bind(effect) as SchedulerJob)
|
const update: SchedulerJob = (instance.update = () => effect.run())
|
||||||
update.id = instance.uid
|
update.id = instance.uid
|
||||||
// allowRecurse
|
// allowRecurse
|
||||||
// #1801, #2043 component render effects should allow recursive updates
|
// #1801, #2043 component render effects should allow recursive updates
|
||||||
|
@ -1561,7 +1567,6 @@ function baseCreateRenderer(
|
||||||
effect.onTrigger = instance.rtg
|
effect.onTrigger = instance.rtg
|
||||||
? e => invokeArrayFns(instance.rtg!, e)
|
? e => invokeArrayFns(instance.rtg!, e)
|
||||||
: void 0
|
: void 0
|
||||||
// @ts-ignore (for scheduler)
|
|
||||||
update.ownerInstance = instance
|
update.ownerInstance = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -510,6 +510,14 @@ function _createVNode(
|
||||||
if (children) {
|
if (children) {
|
||||||
normalizeChildren(cloned, children)
|
normalizeChildren(cloned, children)
|
||||||
}
|
}
|
||||||
|
if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {
|
||||||
|
if (cloned.shapeFlag & ShapeFlags.COMPONENT) {
|
||||||
|
currentBlock[currentBlock.indexOf(type)] = cloned
|
||||||
|
} else {
|
||||||
|
currentBlock.push(cloned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cloned.patchFlag |= PatchFlags.BAIL
|
||||||
return cloned
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@ describe('createApp for dom', () => {
|
||||||
|
|
||||||
// #4398
|
// #4398
|
||||||
test('should not mutate original root component options object', () => {
|
test('should not mutate original root component options object', () => {
|
||||||
|
const originalObj = {
|
||||||
const originalObj = {
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
counter: 0
|
counter: 0
|
||||||
|
@ -28,17 +27,16 @@ describe('createApp for dom', () => {
|
||||||
expect(msg).toMatch(`Component is missing template or render function`)
|
expect(msg).toMatch(`Component is missing template or render function`)
|
||||||
})
|
})
|
||||||
|
|
||||||
const Root = { ...originalObj}
|
const Root = { ...originalObj }
|
||||||
|
|
||||||
const app = createApp(Root)
|
const app = createApp(Root)
|
||||||
app.config.warnHandler = handler
|
app.config.warnHandler = handler
|
||||||
app.mount(document.createElement('div'))
|
app.mount(document.createElement('div'))
|
||||||
|
|
||||||
// ensure mount is based on a copy of Root object rather than Root object itself
|
// ensure mount is based on a copy of Root object rather than Root object itself
|
||||||
expect(app._component).not.toBe(Root)
|
expect(app._component).not.toBe(Root)
|
||||||
|
|
||||||
// ensure no mutation happened to Root object
|
// ensure no mutation happened to Root object
|
||||||
expect(originalObj).toMatchObject(Root)
|
expect(originalObj).toMatchObject(Root)
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -201,7 +201,7 @@ describe('vModel', () => {
|
||||||
it('should support modifiers', async () => {
|
it('should support modifiers', async () => {
|
||||||
const component = defineComponent({
|
const component = defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return { number: null, trim: null, lazy: null }
|
return { number: null, trim: null, lazy: null, trimNumber: null }
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return [
|
return [
|
||||||
|
@ -229,6 +229,19 @@ describe('vModel', () => {
|
||||||
trim: true
|
trim: true
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
withVModel(
|
||||||
|
h('input', {
|
||||||
|
class: 'trim-number',
|
||||||
|
'onUpdate:modelValue': (val: any) => {
|
||||||
|
this.trimNumber = val
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
this.trimNumber,
|
||||||
|
{
|
||||||
|
trim: true,
|
||||||
|
number: true
|
||||||
|
}
|
||||||
|
),
|
||||||
withVModel(
|
withVModel(
|
||||||
h('input', {
|
h('input', {
|
||||||
class: 'lazy',
|
class: 'lazy',
|
||||||
|
@ -248,6 +261,7 @@ describe('vModel', () => {
|
||||||
|
|
||||||
const number = root.querySelector('.number')
|
const number = root.querySelector('.number')
|
||||||
const trim = root.querySelector('.trim')
|
const trim = root.querySelector('.trim')
|
||||||
|
const trimNumber = root.querySelector('.trim-number')
|
||||||
const lazy = root.querySelector('.lazy')
|
const lazy = root.querySelector('.lazy')
|
||||||
const data = root._vnode.component.data
|
const data = root._vnode.component.data
|
||||||
|
|
||||||
|
@ -261,6 +275,16 @@ describe('vModel', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(data.trim).toEqual('hello, world')
|
expect(data.trim).toEqual('hello, world')
|
||||||
|
|
||||||
|
trimNumber.value = ' 1 '
|
||||||
|
triggerEvent('input', trimNumber)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.trimNumber).toEqual(1)
|
||||||
|
|
||||||
|
trimNumber.value = ' +01.2 '
|
||||||
|
triggerEvent('input', trimNumber)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.trimNumber).toEqual(1.2)
|
||||||
|
|
||||||
lazy.value = 'foo'
|
lazy.value = 'foo'
|
||||||
triggerEvent('change', lazy)
|
triggerEvent('change', lazy)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
@ -1015,7 +1039,7 @@ describe('vModel', () => {
|
||||||
bar.selected = false
|
bar.selected = false
|
||||||
data.value = new Set([{ foo: 1 }, { bar: 1 }])
|
data.value = new Set([{ foo: 1 }, { bar: 1 }])
|
||||||
await nextTick()
|
await nextTick()
|
||||||
// whithout looseEqual, here is different from Array
|
// without looseEqual, here is different from Array
|
||||||
expect(foo.selected).toEqual(false)
|
expect(foo.selected).toEqual(false)
|
||||||
expect(bar.selected).toEqual(false)
|
expect(bar.selected).toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
|
@ -205,7 +205,7 @@ describe('runtime-dom: props patching', () => {
|
||||||
test('form attribute', () => {
|
test('form attribute', () => {
|
||||||
const el = document.createElement('input')
|
const el = document.createElement('input')
|
||||||
patchProp(el, 'form', null, 'foo')
|
patchProp(el, 'form', null, 'foo')
|
||||||
// non existant element
|
// non existent element
|
||||||
expect(el.form).toBe(null)
|
expect(el.form).toBe(null)
|
||||||
expect(el.getAttribute('form')).toBe('foo')
|
expect(el.getAttribute('form')).toBe('foo')
|
||||||
// remove attribute
|
// remove attribute
|
||||||
|
|
|
@ -122,13 +122,13 @@ export function defineCustomElement(options: {
|
||||||
|
|
||||||
export function defineCustomElement(
|
export function defineCustomElement(
|
||||||
options: any,
|
options: any,
|
||||||
hydate?: RootHydrateFunction
|
hydrate?: RootHydrateFunction
|
||||||
): VueElementConstructor {
|
): VueElementConstructor {
|
||||||
const Comp = defineComponent(options as any)
|
const Comp = defineComponent(options as any)
|
||||||
class VueCustomElement extends VueElement {
|
class VueCustomElement extends VueElement {
|
||||||
static def = Comp
|
static def = Comp
|
||||||
constructor(initialProps?: Record<string, any>) {
|
constructor(initialProps?: Record<string, any>) {
|
||||||
super(Comp, initialProps, hydate)
|
super(Comp, initialProps, hydrate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,10 @@ export function resolveTransitionProps(
|
||||||
done && done()
|
done && done()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isLeaving = false
|
||||||
const finishLeave = (el: Element, done?: () => void) => {
|
const finishLeave = (el: Element, done?: () => void) => {
|
||||||
|
isLeaving = false
|
||||||
|
removeTransitionClass(el, leaveFromClass)
|
||||||
removeTransitionClass(el, leaveToClass)
|
removeTransitionClass(el, leaveToClass)
|
||||||
removeTransitionClass(el, leaveActiveClass)
|
removeTransitionClass(el, leaveActiveClass)
|
||||||
done && done()
|
done && done()
|
||||||
|
@ -221,6 +224,7 @@ export function resolveTransitionProps(
|
||||||
onEnter: makeEnterHook(false),
|
onEnter: makeEnterHook(false),
|
||||||
onAppear: makeEnterHook(true),
|
onAppear: makeEnterHook(true),
|
||||||
onLeave(el, done) {
|
onLeave(el, done) {
|
||||||
|
isLeaving = true
|
||||||
const resolve = () => finishLeave(el, done)
|
const resolve = () => finishLeave(el, done)
|
||||||
addTransitionClass(el, leaveFromClass)
|
addTransitionClass(el, leaveFromClass)
|
||||||
if (__COMPAT__ && legacyClassEnabled) {
|
if (__COMPAT__ && legacyClassEnabled) {
|
||||||
|
@ -230,6 +234,10 @@ export function resolveTransitionProps(
|
||||||
forceReflow()
|
forceReflow()
|
||||||
addTransitionClass(el, leaveActiveClass)
|
addTransitionClass(el, leaveActiveClass)
|
||||||
nextFrame(() => {
|
nextFrame(() => {
|
||||||
|
if (!isLeaving) {
|
||||||
|
// cancelled
|
||||||
|
return
|
||||||
|
}
|
||||||
removeTransitionClass(el, leaveFromClass)
|
removeTransitionClass(el, leaveFromClass)
|
||||||
if (__COMPAT__ && legacyClassEnabled) {
|
if (__COMPAT__ && legacyClassEnabled) {
|
||||||
removeTransitionClass(el, legacyLeaveFromClass)
|
removeTransitionClass(el, legacyLeaveFromClass)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue