mirror of https://github.com/vuejs/core.git
Compare commits
291 Commits
Author | SHA1 | Date |
---|---|---|
|
cdffaf6b9e | |
|
a47832e75e | |
|
e416f84d74 | |
|
1b98aafa05 | |
|
55dad625ac | |
|
47ddf98602 | |
|
e8d8f5f604 | |
|
5ba1afba09 | |
|
73055d8d95 | |
|
4aa7a4aba3 | |
|
4eb46e443f | |
|
10ebcef8c8 | |
|
f05a8d613b | |
|
762fae4b57 | |
|
e53a4ffbe0 | |
|
62f2aa11a2 | |
|
a60f2bd371 | |
|
e7381761cc | |
|
19f23b180b | |
|
d9bd436b1a | |
|
42f879fcab | |
|
d5ada3d235 | |
|
c017c7b8d3 | |
|
004f488a67 | |
|
1ee3fcf929 | |
|
c46d9f37ae | |
|
9fa787cfd2 | |
|
2c6c0794a1 | |
|
93949e6587 | |
|
949df80880 | |
|
a683c80cf4 | |
|
1d41d4de7f | |
|
5179d328d9 | |
|
d53daf1f29 | |
|
2b894746bd | |
|
64d2ba9f47 | |
|
dc18a159e8 | |
|
00734afef5 | |
|
89edc6cdcb | |
|
f44feed6fa | |
|
c69c4bb59c | |
|
1a664749d4 | |
|
013749e75e | |
|
35aeae7fa3 | |
|
d15dce3142 | |
|
163b3651d1 | |
|
f7ce5ae666 | |
|
772b0087cb | |
|
cf5a5e0edf | |
|
d37a2ac59d | |
|
80055fddfb | |
|
b9910755a5 | |
|
d0253a0b7e | |
|
1f98a9c493 | |
|
626450590d | |
|
8e3435779a | |
|
343c891224 | |
|
9c4dbbc518 | |
|
f7dad6da2f | |
|
258f78b643 | |
|
3cb4db21ef | |
|
d79aa70c61 | |
|
f556c925ac | |
|
56be3dd4db | |
|
3f27c58ffb | |
|
9196222ae1 | |
|
2206cd235a | |
|
5e37dd0095 | |
|
0b23fd2383 | |
|
8b848cbbd2 | |
|
016c472bd2 | |
|
5d166f3796 | |
|
b3ecee3da8 | |
|
e4d9e7ee52 | |
|
bfc458f7bf | |
|
d9923c3503 | |
|
a23fb59e83 | |
|
c3e3396475 | |
|
b92ae84ce5 | |
|
b782cd6c3c | |
|
4085ed9052 | |
|
4f792535e2 | |
|
c15ed52030 | |
|
9d84d64fee | |
|
8ae11226e8 | |
|
32bc647fab | |
|
4f6ef92ad9 | |
|
347c7849ad | |
|
1faca599cd | |
|
466b30f404 | |
|
f6e84af30a | |
|
5f14669d29 | |
|
25e803773a | |
|
1499135c22 | |
|
733e266cdd | |
|
c97cc4cdb0 | |
|
93d663a046 | |
|
60b7bf50e6 | |
|
40fd95ffd3 | |
|
d65b25cdda | |
|
07064f3522 | |
|
c65b8d1519 | |
|
7278d35213 | |
|
de7959ea47 | |
|
021f8f3b69 | |
|
f6f64befb8 | |
|
10e54dcc86 | |
|
4fea167b57 | |
|
fbf88b6062 | |
|
1722090022 | |
|
36509d8561 | |
|
4eba2c53bb | |
|
d2c8c19ae8 | |
|
388295b27f | |
|
fdbd026583 | |
|
636a8619f0 | |
|
d48937fb95 | |
|
fb0c3ca519 | |
|
87e0cd71c2 | |
|
4b1931cf89 | |
|
9b708cf5f7 | |
|
8bd9cdb77e | |
|
d18c248691 | |
|
2dd2feab9c | |
|
79d5d6cf4d | |
|
604d08760e | |
|
5e776ae97e | |
|
295b5ec19b | |
|
0c8dd94ef9 | |
|
efed3ebee6 | |
|
2785d70382 | |
|
cbf5821028 | |
|
4a1884f8dc | |
|
ce0554fc2c | |
|
633327e14d | |
|
4f4425e0df | |
|
d6a6ec13ce | |
|
263f63f735 | |
|
99551e387a | |
|
2ab70c202f | |
|
de0bf335cb | |
|
119f18c773 | |
|
a117a7a84b | |
|
992a05dd48 | |
|
22f359bdbe | |
|
7ecd2a22c7 | |
|
4650715197 | |
|
a63679f2ef | |
|
29216853d4 | |
|
0f12fb7ea9 | |
|
08e153c48c | |
|
f2263229ff | |
|
50b7aa17e9 | |
|
26162b4999 | |
|
c48a00d85e | |
|
82da43d167 | |
|
2e6ec39811 | |
|
c950b4c0c4 | |
|
f399dd3588 | |
|
2d6bcc4537 | |
|
4170dec5a1 | |
|
18555fa7b2 | |
|
23d2f453d1 | |
|
fbdd084acc | |
|
db57d21c17 | |
|
22dcbf3e20 | |
|
e8e842241a | |
|
5a6e98ca32 | |
|
eb618fd889 | |
|
21f8d9dba9 | |
|
3faa0a3a9e | |
|
b7085a85f1 | |
|
289f4bd94f | |
|
234b6cbb74 | |
|
6da11a7d22 | |
|
bc688434af | |
|
833f9ea1f7 | |
|
ec22b79fd4 | |
|
76bdd78f17 | |
|
f74146f40e | |
|
d07cdead2c | |
|
11c053a542 | |
|
201936f9a3 | |
|
11f76741fb | |
|
c86a08b946 | |
|
5a5406d002 | |
|
d82fb465a6 | |
|
fc4bbf95c1 | |
|
06310e82f5 | |
|
14f6917c3c | |
|
01057fc74c | |
|
fdaff720d7 | |
|
c028aeafa9 | |
|
6eb29d345a | |
|
4f8d807822 | |
|
983eb50a17 | |
|
da7ad5e3d2 | |
|
1f75d4e6df | |
|
4b479db61d | |
|
a20a4cb36a | |
|
352bc88c1b | |
|
10ab8c0e7b | |
|
d637bd6c01 | |
|
2d78539da3 | |
|
54812eacaa | |
|
1022eabaa1 | |
|
660132df6c | |
|
4aeff318bd | |
|
70b44ca835 | |
|
a49858f3ee | |
|
8bff142f99 | |
|
c4312f9c71 | |
|
2d5c5e25e9 | |
|
99009eee0e | |
|
37300fc261 | |
|
bee2f5ee62 | |
|
21932840ea | |
|
e9f3e6b546 | |
|
3656364b06 | |
|
506ed4e75f | |
|
83430a35f4 | |
|
b5ff930089 | |
|
852642729a | |
|
a0901756da | |
|
76c43c6040 | |
|
e81ecc9a9d | |
|
7d0dc7394f | |
|
394902c2e9 | |
|
664d2e553d | |
|
3a043e8c16 | |
|
6c0f4741ed | |
|
ad247b3323 | |
|
5652c5ad83 | |
|
ed01d92571 | |
|
1222437ec2 | |
|
536d600199 | |
|
657603d7b7 | |
|
2442c3b061 | |
|
5b17afa89d | |
|
828d4a4439 | |
|
a038505c65 | |
|
4e19a99461 | |
|
3b5d8d2511 | |
|
aa6879f987 | |
|
be9eed2593 | |
|
1755ac0a10 | |
|
770ea67a9c | |
|
c82b66214b | |
|
ea943afe40 | |
|
d82fa611e8 | |
|
d96883cf7d | |
|
1ae545a378 | |
|
05685a9d7c | |
|
cde2c0671b | |
|
4474c113d1 | |
|
9da1ac1565 | |
|
10a46f43c0 | |
|
d3ecde8a69 | |
|
76a8223199 | |
|
b4d35349d8 | |
|
ec917cfdb9 | |
|
f6d9926236 | |
|
7ad289e1e7 | |
|
c0418a3b8f | |
|
f1a4f67aed | |
|
e0a591e1cd | |
|
704173e242 | |
|
d9d4d4e158 | |
|
e16e9a7341 | |
|
35785f3cd7 | |
|
723f588716 | |
|
73a3666dee | |
|
f7cbea2111 | |
|
b094c72b3d | |
|
6e4de8d75e | |
|
6f85894376 | |
|
c97bb84d0b | |
|
57315ab968 | |
|
577edca8e7 | |
|
3a55c3e421 | |
|
435e4fefad | |
|
d3f5e6e531 | |
|
29de6f8b0b | |
|
2328b051f4 | |
|
4b09ab2074 | |
|
93c95dd4cd | |
|
aa9ef2386a | |
|
60c2029f77 | |
|
e2c19c20cf | |
|
ea3efa09e0 | |
|
a77b95992a |
|
@ -1,18 +1,17 @@
|
|||
{
|
||||
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
||||
extends: ['config:base', 'schedule:weekly', 'group:allNonMajor'],
|
||||
extends: ['config:recommended', 'schedule:weekly', 'group:allNonMajor'],
|
||||
labels: ['dependencies'],
|
||||
ignorePaths: ['**/__tests__/**'],
|
||||
rangeStrategy: 'bump',
|
||||
packageRules: [
|
||||
{
|
||||
depTypeList: ['peerDependencies'],
|
||||
matchDepTypes: ['peerDependencies'],
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
groupName: 'test',
|
||||
matchPackageNames: ['vitest', 'jsdom', 'puppeteer'],
|
||||
matchPackagePrefixes: ['@vitest'],
|
||||
matchPackageNames: ['vitest', 'jsdom', 'puppeteer', '@vitest{/,}**'],
|
||||
},
|
||||
{
|
||||
groupName: 'playground',
|
||||
|
@ -23,18 +22,28 @@
|
|||
},
|
||||
{
|
||||
groupName: 'compiler',
|
||||
matchPackageNames: ['magic-string'],
|
||||
matchPackagePrefixes: ['@babel', 'postcss'],
|
||||
matchPackageNames: ['magic-string', '@babel{/,}**', 'postcss{/,}**'],
|
||||
},
|
||||
{
|
||||
groupName: 'build',
|
||||
matchPackageNames: ['vite', '@swc/core'],
|
||||
matchPackagePrefixes: ['rollup', 'esbuild', '@rollup', '@vitejs'],
|
||||
matchPackageNames: [
|
||||
'vite',
|
||||
'@swc/core',
|
||||
'rollup{/,}**',
|
||||
'esbuild{/,}**',
|
||||
'@rollup{/,}**',
|
||||
'@vitejs{/,}**',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'lint',
|
||||
matchPackageNames: ['simple-git-hooks', 'lint-staged'],
|
||||
matchPackagePrefixes: ['typescript-eslint', 'eslint', 'prettier'],
|
||||
matchPackageNames: [
|
||||
'simple-git-hooks',
|
||||
'lint-staged',
|
||||
'typescript-eslint{/,}**',
|
||||
'eslint{/,}**',
|
||||
'prettier{/,}**',
|
||||
],
|
||||
},
|
||||
],
|
||||
ignoreDeps: [
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -31,4 +31,4 @@ jobs:
|
|||
- name: Run prettier
|
||||
run: pnpm run format
|
||||
|
||||
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c
|
||||
- uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef
|
||||
|
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
ref: minor
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -37,7 +37,7 @@ jobs:
|
|||
run: pnpm install
|
||||
|
||||
- name: Download Size Data
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v9
|
||||
with:
|
||||
name: size-data
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
|
@ -56,7 +56,7 @@ jobs:
|
|||
path: temp/size/base.txt
|
||||
|
||||
- name: Download Previous Size Data
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v9
|
||||
with:
|
||||
branch: ${{ steps.pr-base.outputs.content }}
|
||||
workflow: size-data.yml
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -35,7 +35,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -63,7 +63,7 @@ jobs:
|
|||
key: chromium-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -88,7 +88,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.0.0
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -104,5 +104,8 @@ jobs:
|
|||
- name: Run prettier
|
||||
run: pnpm run format-check
|
||||
|
||||
- name: Run tsc
|
||||
run: pnpm run check
|
||||
|
||||
- name: Run type declaration tests
|
||||
run: pnpm run test-dts
|
||||
|
|
|
@ -1 +1 @@
|
|||
20
|
||||
22.14.0
|
||||
|
|
|
@ -13,5 +13,6 @@
|
|||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
},
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
https://vuejs.org/funding.json
|
166
CHANGELOG.md
166
CHANGELOG.md
|
@ -1,3 +1,169 @@
|
|||
## [3.5.16](https://github.com/vuejs/core/compare/v3.5.15...v3.5.16) (2025-05-29)
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "fix(compiler-sfc): add scoping tag to trailing universal selector" (#13406) ([19f23b1](https://github.com/vuejs/core/commit/19f23b180bb679e38db95d6a10a420abeedc8e1c)), closes [#13406](https://github.com/vuejs/core/issues/13406)
|
||||
* Revert "fix(compiler-sfc): add error handling for defineModel() without variable" (#13390) ([42f879f](https://github.com/vuejs/core/commit/42f879fcab48e0e1011967a771b4ad9e8838d760)), closes [#13390](https://github.com/vuejs/core/issues/13390)
|
||||
|
||||
|
||||
|
||||
## [3.5.15](https://github.com/vuejs/core/compare/v3.5.14...v3.5.15) (2025-05-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compat:** ensure false value on input retains value attribute ([#13216](https://github.com/vuejs/core/issues/13216)) ([1a66474](https://github.com/vuejs/core/commit/1a664749d4d65a345589a6d78106ede7574cb2e1)), closes [#13205](https://github.com/vuejs/core/issues/13205)
|
||||
* **compat:** should not warn COMPILER_V_BIND_OBJECT_ORDER when using v-bind together with v-for ([#12993](https://github.com/vuejs/core/issues/12993)) ([93949e6](https://github.com/vuejs/core/commit/93949e6587ee019bccd5b8b9d76f0e1ed6ea16fc)), closes [#12992](https://github.com/vuejs/core/issues/12992)
|
||||
* **compile-sfc:** handle inline template source map in prod build ([#12701](https://github.com/vuejs/core/issues/12701)) ([89edc6c](https://github.com/vuejs/core/commit/89edc6cdcbd34ea6394927ecbfaa61dc4f871de7)), closes [#12682](https://github.com/vuejs/core/issues/12682) [vitejs/vite-plugin-vue#500](https://github.com/vitejs/vite-plugin-vue/issues/500)
|
||||
* **compiler-core:** ensure mapping is added only if node source is available ([#13285](https://github.com/vuejs/core/issues/13285)) ([d37a2ac](https://github.com/vuejs/core/commit/d37a2ac59d904ac0e3257ba552b6c04920a363f0)), closes [#13261](https://github.com/vuejs/core/issues/13261) [vitejs/vite-plugin-vue#368](https://github.com/vitejs/vite-plugin-vue/issues/368)
|
||||
* **compiler-dom:** improve HTML nesting validation to allow any child element within template tag ([#13320](https://github.com/vuejs/core/issues/13320)) ([163b365](https://github.com/vuejs/core/commit/163b3651d174321911648a164052effa9249a2aa)), closes [#13318](https://github.com/vuejs/core/issues/13318)
|
||||
* **compiler-sfc:** add error handling for defineModel() without variable assignment ([#13352](https://github.com/vuejs/core/issues/13352)) ([00734af](https://github.com/vuejs/core/commit/00734afef5f7bddbdaee52aa5359a6ef989f32d3)), closes [#13280](https://github.com/vuejs/core/issues/13280)
|
||||
* **compiler-sfc:** add scoping tag to trailing universal selector ([#12918](https://github.com/vuejs/core/issues/12918)) ([949df80](https://github.com/vuejs/core/commit/949df808809fd7cccf7718797beab0654aa68302)), closes [#12906](https://github.com/vuejs/core/issues/12906)
|
||||
* **compiler-sfc:** improve type inference for TSTypeAliasDeclaration with better runtime type detection ([#13245](https://github.com/vuejs/core/issues/13245)) ([cf5a5e0](https://github.com/vuejs/core/commit/cf5a5e0edf0efcab25c27aa2d13eba91f7372d39)), closes [#13240](https://github.com/vuejs/core/issues/13240)
|
||||
* **compiler-sfc:** simulate `allowArbitraryExtensions` on resolving type ([#13301](https://github.com/vuejs/core/issues/13301)) ([f7ce5ae](https://github.com/vuejs/core/commit/f7ce5ae666129339c006b339437c2dff6bceffe0)), closes [#13295](https://github.com/vuejs/core/issues/13295)
|
||||
* **custom-element:** allow injecting values from app context in nested elements ([#13219](https://github.com/vuejs/core/issues/13219)) ([b991075](https://github.com/vuejs/core/commit/b9910755a50c7d6c52b28c3aef20cf97810295c9)), closes [#13212](https://github.com/vuejs/core/issues/13212)
|
||||
* **custom-element:** ensure proper remount and prevent redundant slot parsing with shadowRoot false ([#13201](https://github.com/vuejs/core/issues/13201)) ([1d41d4d](https://github.com/vuejs/core/commit/1d41d4de7f64a37160c8171d0137fd8d35c346c9)), closes [#13199](https://github.com/vuejs/core/issues/13199)
|
||||
* **custom-element:** preserve appContext during update ([#12455](https://github.com/vuejs/core/issues/12455)) ([013749e](https://github.com/vuejs/core/commit/013749e75ef3b51762a86da379ea4ba4501b54ae)), closes [#12453](https://github.com/vuejs/core/issues/12453)
|
||||
* **custom-element:** properly resolve props for sync component defs ([#12855](https://github.com/vuejs/core/issues/12855)) ([a683c80](https://github.com/vuejs/core/commit/a683c80cf44ecc482f8ac9c76bf2381443c1b0bb)), closes [#12854](https://github.com/vuejs/core/issues/12854)
|
||||
* **hydration:** handle transition appear hydration edge case ([#13339](https://github.com/vuejs/core/issues/13339)) ([35aeae7](https://github.com/vuejs/core/commit/35aeae7fa3168adcf9ed95fd35495d17c8b93eeb)), closes [#13335](https://github.com/vuejs/core/issues/13335)
|
||||
* **hydration:** skip lazy hydration for patched components ([#13283](https://github.com/vuejs/core/issues/13283)) ([80055fd](https://github.com/vuejs/core/commit/80055fddfb3ca1e2a44f19c7f0ffaeba00de5140)), closes [#13255](https://github.com/vuejs/core/issues/13255)
|
||||
* **suspense:** handle edge case in patching list nodes within Suspense ([#13306](https://github.com/vuejs/core/issues/13306)) ([772b008](https://github.com/vuejs/core/commit/772b0087cb7be151c514a1d30365fb0f61a652ba)), closes [#13305](https://github.com/vuejs/core/issues/13305)
|
||||
* **teleport:** handle deferred teleport updates before and after mount ([#13350](https://github.com/vuejs/core/issues/13350)) ([d15dce3](https://github.com/vuejs/core/commit/d15dce3142474f2ef9fffed38383acdadcb26c4c)), closes [#13349](https://github.com/vuejs/core/issues/13349)
|
||||
* **types:** avoid merging component instance into `$props` in `ComponentInstance` ([#12870](https://github.com/vuejs/core/issues/12870)) ([f44feed](https://github.com/vuejs/core/commit/f44feed6fa461a9c4c724e9631c19e9e214c0a20)), closes [#12751](https://github.com/vuejs/core/issues/12751)
|
||||
* **types:** exclude `undefined` from inferred prop types with default values ([#13007](https://github.com/vuejs/core/issues/13007)) ([5179d32](https://github.com/vuejs/core/commit/5179d328d950015e7fb2a74fe1a8518fd8d2c94e)), closes [#13006](https://github.com/vuejs/core/issues/13006)
|
||||
* **watch:** update `oldValue` before running `cb` to prevent stale value ([#12296](https://github.com/vuejs/core/issues/12296)) ([c69c4bb](https://github.com/vuejs/core/commit/c69c4bb59c114f2b5e03733b55ef9ace3087b5c3)), closes [#12294](https://github.com/vuejs/core/issues/12294)
|
||||
|
||||
|
||||
|
||||
## [3.5.14](https://github.com/vuejs/core/compare/v3.5.13...v3.5.14) (2025-05-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compat:** correct deprecation message for v-bind.sync usage ([#13137](https://github.com/vuejs/core/issues/13137)) ([466b30f](https://github.com/vuejs/core/commit/466b30f4049ec89fb282624ec17d1a93472ab93f)), closes [#13133](https://github.com/vuejs/core/issues/13133)
|
||||
* **compiler-core:** remove slot cache from parent renderCache during unmounting ([#13215](https://github.com/vuejs/core/issues/13215)) ([5d166f3](https://github.com/vuejs/core/commit/5d166f3796a03a497435fc079c6a83a4e9c6cf52))
|
||||
* **compiler-sfc:** fix scope handling for props destructure in function parameters and catch clauses ([8e34357](https://github.com/vuejs/core/commit/8e3435779a667de485cf9efd78667d0ca14c5f84)), closes [#12790](https://github.com/vuejs/core/issues/12790)
|
||||
* **compiler-sfc:** treat the return value of `useTemplateRef` as a definite ref ([#13197](https://github.com/vuejs/core/issues/13197)) ([8ae1122](https://github.com/vuejs/core/commit/8ae11226e8ee938615e17c7b81dc38ae3f7cefb9))
|
||||
* **compiler:** fix spelling error in domTagConfig ([#13043](https://github.com/vuejs/core/issues/13043)) ([388295b](https://github.com/vuejs/core/commit/388295b27f3cc69eba25d325bbe60a36a3df831a))
|
||||
* **customFormatter:** properly accessing ref value during debugger ([#12948](https://github.com/vuejs/core/issues/12948)) ([fdbd026](https://github.com/vuejs/core/commit/fdbd02658301dd794fe0c84f0018d080a07fca9f))
|
||||
* **hmr/teleport:** adjust static children traversal for HMR in dev mode ([#12819](https://github.com/vuejs/core/issues/12819)) ([5e37dd0](https://github.com/vuejs/core/commit/5e37dd009562bcd8080a200c32abde2d6e4f0305)), closes [#12816](https://github.com/vuejs/core/issues/12816)
|
||||
* **hmr:** avoid hydration for hmr root reload ([#12450](https://github.com/vuejs/core/issues/12450)) ([1f98a9c](https://github.com/vuejs/core/commit/1f98a9c493d01c21befa90107f0593bc92a58932)), closes [vitejs/vite-plugin-vue#146](https://github.com/vitejs/vite-plugin-vue/issues/146) [vitejs/vite-plugin-vue#477](https://github.com/vitejs/vite-plugin-vue/issues/477)
|
||||
* **hmr:** avoid hydration for hmr updating ([#12262](https://github.com/vuejs/core/issues/12262)) ([9c4dbbc](https://github.com/vuejs/core/commit/9c4dbbc5185125835ad3e49baba303bd54676111)), closes [#7706](https://github.com/vuejs/core/issues/7706) [#8170](https://github.com/vuejs/core/issues/8170)
|
||||
* **reactivity:** ensure markRaw objects are not reactive ([#12824](https://github.com/vuejs/core/issues/12824)) ([295b5ec](https://github.com/vuejs/core/commit/295b5ec19b6a52c4a56652cc4d6e93a4ea7c14ed)), closes [#12807](https://github.com/vuejs/core/issues/12807)
|
||||
* **reactivity:** ensure multiple effectScope on() and off() calls maintains correct active scope ([22dcbf3](https://github.com/vuejs/core/commit/22dcbf3e20eb84f69c8952f6f70d9990136a4a68)), closes [#12631](https://github.com/vuejs/core/issues/12631) [#12632](https://github.com/vuejs/core/issues/12632) [#12641](https://github.com/vuejs/core/issues/12641)
|
||||
* **reactivity:** should not recompute if computed does not track reactive data ([#12341](https://github.com/vuejs/core/issues/12341)) ([0b23fd2](https://github.com/vuejs/core/commit/0b23fd23833cf085e7e112bf4435cfc9b360d072)), closes [#12337](https://github.com/vuejs/core/issues/12337)
|
||||
* **runtime-core:** stop tracking deps in setRef during unmount ([#13210](https://github.com/vuejs/core/issues/13210)) ([016c472](https://github.com/vuejs/core/commit/016c472bd2e7604b21c69dee1da8545ce26e4d2f))
|
||||
* **runtime-core:** update __vnode of static nodes when patching along the optimized path ([#13223](https://github.com/vuejs/core/issues/13223)) ([b3ecee3](https://github.com/vuejs/core/commit/b3ecee3da8ed5c55dea89ce6b4b376b2b722b018))
|
||||
* **runtime-core:** inherit comment nodes during block patch in production build ([#10748](https://github.com/vuejs/core/issues/10748)) ([6264505](https://github.com/vuejs/core/commit/626450590d81f79117b34d2a73073b1dc8f551bd)), closes [#10747](https://github.com/vuejs/core/issues/10747) [#12650](https://github.com/vuejs/core/issues/12650)
|
||||
* **runtime-core:** prevent unmounted vnode from being inserted during transition leave ([#12862](https://github.com/vuejs/core/issues/12862)) ([d6a6ec1](https://github.com/vuejs/core/commit/d6a6ec13ce521683bfb2a22932778ef7b51f8600)), closes [#12860](https://github.com/vuejs/core/issues/12860)
|
||||
* **runtime-core:** respect immutability for readonly reactive arrays in `v-for` ([#13091](https://github.com/vuejs/core/issues/13091)) ([3f27c58](https://github.com/vuejs/core/commit/3f27c58ffbd4309df369bc89493fdc284dc540bb)), closes [#13087](https://github.com/vuejs/core/issues/13087)
|
||||
* **runtime-dom:** always treat autocorrect as attribute ([#13001](https://github.com/vuejs/core/issues/13001)) ([1499135](https://github.com/vuejs/core/commit/1499135c227236e037bb746beeb777941b0b58ff)), closes [#5705](https://github.com/vuejs/core/issues/5705)
|
||||
* **slots:** properly warn if slot invoked in setup ([#12195](https://github.com/vuejs/core/issues/12195)) ([9196222](https://github.com/vuejs/core/commit/9196222ae1d63b52b35ac5fbf5e71494587ccf05)), closes [#12194](https://github.com/vuejs/core/issues/12194)
|
||||
* **ssr:** properly init slots during ssr rendering ([#12441](https://github.com/vuejs/core/issues/12441)) ([2206cd2](https://github.com/vuejs/core/commit/2206cd235a1627c540e795e378b7564a55b47313)), closes [#12438](https://github.com/vuejs/core/issues/12438)
|
||||
* **transition:** fix KeepAlive with transition out-in mode behavior in production ([#12468](https://github.com/vuejs/core/issues/12468)) ([343c891](https://github.com/vuejs/core/commit/343c89122448719bd6ed6bd9de986dfb2721d6bf)), closes [#12465](https://github.com/vuejs/core/issues/12465)
|
||||
* **TransitionGroup:** reset prevChildren to prevent memory leak ([#13183](https://github.com/vuejs/core/issues/13183)) ([8b848cb](https://github.com/vuejs/core/commit/8b848cbbd2af337d23e19e202f9ab433f8580855)), closes [#13181](https://github.com/vuejs/core/issues/13181)
|
||||
* **types:** allow return any for Options API lifecycle hooks ([#5914](https://github.com/vuejs/core/issues/5914)) ([06310e8](https://github.com/vuejs/core/commit/06310e82f5bed62d1b9733dcb18cd8d6edc988de))
|
||||
* **types:** the directive's modifiers should be optional ([#12605](https://github.com/vuejs/core/issues/12605)) ([10e54dc](https://github.com/vuejs/core/commit/10e54dcc86a7967f3196d96200bcbd1d3d42082f))
|
||||
* **typos:** fix comments referencing transformElement.ts ([#12551](https://github.com/vuejs/core/issues/12551))[ci-skip] ([11c053a](https://github.com/vuejs/core/commit/11c053a5429ad0d27a0e2c78b6b026ea00ace116))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **types:** add type TemplateRef ([#12645](https://github.com/vuejs/core/issues/12645)) ([636a861](https://github.com/vuejs/core/commit/636a8619f06c71dfd79f7f6412fd130c4f84226f))
|
||||
|
||||
|
||||
|
||||
## [3.5.13](https://github.com/vuejs/core/compare/v3.5.12...v3.5.13) (2024-11-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** handle v-memo + v-for with functional key ([#12014](https://github.com/vuejs/core/issues/12014)) ([99009ee](https://github.com/vuejs/core/commit/99009eee0efc238392daba93792d478525b21afa)), closes [#12013](https://github.com/vuejs/core/issues/12013)
|
||||
* **compiler-dom:** properly stringify template string style ([#12392](https://github.com/vuejs/core/issues/12392)) ([2d78539](https://github.com/vuejs/core/commit/2d78539da35322aea5f821b3cf9b02d006abac72)), closes [#12391](https://github.com/vuejs/core/issues/12391)
|
||||
* **custom-element:** avoid triggering mutationObserver when relecting props ([352bc88](https://github.com/vuejs/core/commit/352bc88c1bd2fda09c61ab17ea1a5967ffcd7bc0)), closes [#12214](https://github.com/vuejs/core/issues/12214) [#12215](https://github.com/vuejs/core/issues/12215)
|
||||
* **deps:** update dependency postcss to ^8.4.48 ([#12356](https://github.com/vuejs/core/issues/12356)) ([b5ff930](https://github.com/vuejs/core/commit/b5ff930089985a58c3553977ef999cec2a6708a4))
|
||||
* **hydration:** the component vnode's el should be updated when a mismatch occurs. ([#12255](https://github.com/vuejs/core/issues/12255)) ([a20a4cb](https://github.com/vuejs/core/commit/a20a4cb36a3e717d1f8f259d0d59f133f508ff0a)), closes [#12253](https://github.com/vuejs/core/issues/12253)
|
||||
* **reactivity:** avoid unnecessary watcher effect removal from inactive scope ([2193284](https://github.com/vuejs/core/commit/21932840eae72ffcd357a62ec596aaecc7ec224a)), closes [#5783](https://github.com/vuejs/core/issues/5783) [#5806](https://github.com/vuejs/core/issues/5806)
|
||||
* **reactivity:** release nested effects/scopes on effect scope stop ([#12373](https://github.com/vuejs/core/issues/12373)) ([bee2f5e](https://github.com/vuejs/core/commit/bee2f5ee62dc0cd04123b737779550726374dd0a)), closes [#12370](https://github.com/vuejs/core/issues/12370)
|
||||
* **runtime-dom:** set css vars before user onMounted hooks ([2d5c5e2](https://github.com/vuejs/core/commit/2d5c5e25e9b7a56e883674fb434135ac514429b5)), closes [#11533](https://github.com/vuejs/core/issues/11533)
|
||||
* **runtime-dom:** set css vars on update to handle child forcing reflow in onMount ([#11561](https://github.com/vuejs/core/issues/11561)) ([c4312f9](https://github.com/vuejs/core/commit/c4312f9c715c131a09e552ba46e9beb4b36d55e6))
|
||||
* **ssr:** avoid updating subtree of async component if it is resolved ([#12363](https://github.com/vuejs/core/issues/12363)) ([da7ad5e](https://github.com/vuejs/core/commit/da7ad5e3d24f3e108401188d909d27a4910da095)), closes [#12362](https://github.com/vuejs/core/issues/12362)
|
||||
* **ssr:** ensure v-text updates correctly with custom directives in SSR output ([#12311](https://github.com/vuejs/core/issues/12311)) ([1f75d4e](https://github.com/vuejs/core/commit/1f75d4e6dfe18121ebe443cd3e8105d54f727893)), closes [#12309](https://github.com/vuejs/core/issues/12309)
|
||||
* **ssr:** handle initial selected state for select with v-model + v-for option ([#12399](https://github.com/vuejs/core/issues/12399)) ([4f8d807](https://github.com/vuejs/core/commit/4f8d8078221ee52deed266677a227ad2a6d8dd22)), closes [#12395](https://github.com/vuejs/core/issues/12395)
|
||||
* **teleport:** handle deferred teleport update before mounted ([#12168](https://github.com/vuejs/core/issues/12168)) ([8bff142](https://github.com/vuejs/core/commit/8bff142f99b646e9dd15897ec75368fbf34f1534)), closes [#12161](https://github.com/vuejs/core/issues/12161)
|
||||
* **templateRef:** set ref on cached async component which wrapped in KeepAlive ([#12290](https://github.com/vuejs/core/issues/12290)) ([983eb50](https://github.com/vuejs/core/commit/983eb50a17eac76f1bba4394ad0316c62b72191d)), closes [#4999](https://github.com/vuejs/core/issues/4999) [#5004](https://github.com/vuejs/core/issues/5004)
|
||||
* **test:** update snapshot ([#12169](https://github.com/vuejs/core/issues/12169)) ([828d4a4](https://github.com/vuejs/core/commit/828d4a443919fa2aa4e2e92fbd03a5f04b258eea))
|
||||
* **Transition:** fix transition memory leak edge case ([#12182](https://github.com/vuejs/core/issues/12182)) ([660132d](https://github.com/vuejs/core/commit/660132df6c6a8c14bf75e593dc47d2fdada30322)), closes [#12181](https://github.com/vuejs/core/issues/12181)
|
||||
* **transition:** reflow before leave-active class after leave-from ([#12288](https://github.com/vuejs/core/issues/12288)) ([4b479db](https://github.com/vuejs/core/commit/4b479db61d233b054561402ae94ef08550073ea1)), closes [#2593](https://github.com/vuejs/core/issues/2593)
|
||||
* **types:** defineEmits w/ interface declaration ([#12343](https://github.com/vuejs/core/issues/12343)) ([1022eab](https://github.com/vuejs/core/commit/1022eabaa1aaf8436876f5ec5573cb1e4b3959a6)), closes [#8457](https://github.com/vuejs/core/issues/8457)
|
||||
* **v-once:** setting hasOnce to current block only when in v-once ([#12374](https://github.com/vuejs/core/issues/12374)) ([37300fc](https://github.com/vuejs/core/commit/37300fc26190a7299efddbf98800ffd96d5cad96)), closes [#12371](https://github.com/vuejs/core/issues/12371)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **reactivity:** do not track inner key `__v_skip`` ([#11690](https://github.com/vuejs/core/issues/11690)) ([d637bd6](https://github.com/vuejs/core/commit/d637bd6c0164c2883e6eabd3c2f1f8c258dedfb1))
|
||||
* **runtime-core:** use feature flag for call to resolveMergedOptions ([#12163](https://github.com/vuejs/core/issues/12163)) ([1755ac0](https://github.com/vuejs/core/commit/1755ac0a108ba3486bd8397e56d3bdcd69196594))
|
||||
|
||||
|
||||
|
||||
## [3.5.12](https://github.com/vuejs/core/compare/v3.5.11...v3.5.12) (2024-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-dom:** avoid stringify option with null value ([#12096](https://github.com/vuejs/core/issues/12096)) ([f6d9926](https://github.com/vuejs/core/commit/f6d99262364b7444ebab8742158599e8cdd79eaa)), closes [#12093](https://github.com/vuejs/core/issues/12093)
|
||||
* **compiler-sfc:** do not skip TSInstantiationExpression when transforming props destructure ([#12064](https://github.com/vuejs/core/issues/12064)) ([d3ecde8](https://github.com/vuejs/core/commit/d3ecde8a696ff62c8d0ab067fd1d7ee0565b63c5))
|
||||
* **compiler-sfc:** use sass modern api if available and avoid deprecation warning ([#11992](https://github.com/vuejs/core/issues/11992)) ([4474c11](https://github.com/vuejs/core/commit/4474c113d1fb1c26298dd6794275d5b5c7cc4d93))
|
||||
* **compiler:** clone loc to `ifNode` ([#12131](https://github.com/vuejs/core/issues/12131)) ([cde2c06](https://github.com/vuejs/core/commit/cde2c0671b00d4f6111fcbd7aa76e45872f20b0c)), closes [vuejs/language-tools#4911](https://github.com/vuejs/language-tools/issues/4911)
|
||||
* **custom-element:** properly remove hyphenated attribute ([#12143](https://github.com/vuejs/core/issues/12143)) ([e16e9a7](https://github.com/vuejs/core/commit/e16e9a7341e7cfb3c443da4e5e5b06e8158712c3)), closes [#12139](https://github.com/vuejs/core/issues/12139)
|
||||
* **defineModel:** handle kebab-case model correctly ([#12063](https://github.com/vuejs/core/issues/12063)) ([c0418a3](https://github.com/vuejs/core/commit/c0418a3b8fa96a0b108ab71b7aab5d3388f90557)), closes [#12060](https://github.com/vuejs/core/issues/12060)
|
||||
* **deps:** update dependency monaco-editor to ^0.52.0 ([#12119](https://github.com/vuejs/core/issues/12119)) ([f7cbea2](https://github.com/vuejs/core/commit/f7cbea2111c7770a180b640f36f6a5d4d6abc698))
|
||||
* **hydration:** provide compat fallback for idle callback hydration strategy ([#11935](https://github.com/vuejs/core/issues/11935)) ([1ae545a](https://github.com/vuejs/core/commit/1ae545a3786abef983be1c969726489685569c92))
|
||||
* **reactivity:** trigger reactivity for Map key `undefined` ([#12055](https://github.com/vuejs/core/issues/12055)) ([7ad289e](https://github.com/vuejs/core/commit/7ad289e1e7fea654524008ff91e43a8b8a55ef22)), closes [#12054](https://github.com/vuejs/core/issues/12054)
|
||||
* **runtime-core:** allow symbol values for slot prop key ([#12069](https://github.com/vuejs/core/issues/12069)) ([d9d4d4e](https://github.com/vuejs/core/commit/d9d4d4e158cd51a9ddda249f29de8467f60b2792)), closes [#12068](https://github.com/vuejs/core/issues/12068)
|
||||
* **runtime-core:** fix required prop check false positive for kebab-case edge cases ([#12034](https://github.com/vuejs/core/issues/12034)) ([9da1ac1](https://github.com/vuejs/core/commit/9da1ac156552ac449754e1373aac7e349841becb)), closes [#12011](https://github.com/vuejs/core/issues/12011)
|
||||
* **runtime-dom:** prevent unnecessary updates in v-model checkbox when value is unchanged ([#12146](https://github.com/vuejs/core/issues/12146)) ([ea943af](https://github.com/vuejs/core/commit/ea943afe404c4ca4b729906c5e8daf7aa2ccde9b)), closes [#12144](https://github.com/vuejs/core/issues/12144)
|
||||
* **teleport:** handle disabled teleport with updateCssVars ([#12113](https://github.com/vuejs/core/issues/12113)) ([76a8223](https://github.com/vuejs/core/commit/76a8223199c148b79a5c0ea19e235164809760cd)), closes [#12112](https://github.com/vuejs/core/issues/12112)
|
||||
* **transition/ssr:** make transition appear work with Suspense in SSR ([#12047](https://github.com/vuejs/core/issues/12047)) ([f1a4f67](https://github.com/vuejs/core/commit/f1a4f67aedfe83e440c54222213f070774faa421)), closes [#12046](https://github.com/vuejs/core/issues/12046)
|
||||
* **types:** ensure `this.$props` type does not include `string` ([#12123](https://github.com/vuejs/core/issues/12123)) ([704173e](https://github.com/vuejs/core/commit/704173e24276706de672cca6c9507e4dd9651197)), closes [#12122](https://github.com/vuejs/core/issues/12122)
|
||||
* **types:** retain union type narrowing with defaults applied ([#12108](https://github.com/vuejs/core/issues/12108)) ([05685a9](https://github.com/vuejs/core/commit/05685a9d7c42d4cd37169b867833776b91154fed)), closes [#12106](https://github.com/vuejs/core/issues/12106)
|
||||
* **useId:** ensure useId consistency when using serverPrefetch ([#12128](https://github.com/vuejs/core/issues/12128)) ([b4d3534](https://github.com/vuejs/core/commit/b4d35349d8bc39aa15bd3f1094d230e5928b177c)), closes [#12102](https://github.com/vuejs/core/issues/12102)
|
||||
* **watch:** watchEffect clean-up with SSR ([#12097](https://github.com/vuejs/core/issues/12097)) ([b094c72](https://github.com/vuejs/core/commit/b094c72b3d40c52c7124f145a9db028509a11202)), closes [#11956](https://github.com/vuejs/core/issues/11956)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **reactivity:** avoid unnecessary recursion in removeSub ([#12135](https://github.com/vuejs/core/issues/12135)) ([ec917cf](https://github.com/vuejs/core/commit/ec917cfdb9d0169cd0835d3a0e28244242657dc9))
|
||||
|
||||
|
||||
|
||||
## [3.5.11](https://github.com/vuejs/core/compare/v3.5.10...v3.5.11) (2024-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-sfc:** do not skip `TSSatisfiesExpression` when transforming props destructure ([#12062](https://github.com/vuejs/core/issues/12062)) ([2328b05](https://github.com/vuejs/core/commit/2328b051f4efa1f1394b7d4e73b7c3f76e430e7c)), closes [#12061](https://github.com/vuejs/core/issues/12061)
|
||||
* **reactivity:** prevent overwriting `next` property during batch processing ([#12075](https://github.com/vuejs/core/issues/12075)) ([d3f5e6e](https://github.com/vuejs/core/commit/d3f5e6e5319b4ffaa55ca9a2ea3d95d78e76fa58)), closes [#12072](https://github.com/vuejs/core/issues/12072)
|
||||
* **scheduler:** job ordering when the post queue is flushing ([#12090](https://github.com/vuejs/core/issues/12090)) ([577edca](https://github.com/vuejs/core/commit/577edca8e7795436efd710d1c289ea8ea2642b0e))
|
||||
* **types:** correctly infer `TypeProps` when it is `any` ([#12073](https://github.com/vuejs/core/issues/12073)) ([57315ab](https://github.com/vuejs/core/commit/57315ab9688c9741a271d1075bbd28cbe5f71e2f)), closes [#12058](https://github.com/vuejs/core/issues/12058)
|
||||
* **types:** should not intersect `PublicProps` with `Props` ([#12077](https://github.com/vuejs/core/issues/12077)) ([6f85894](https://github.com/vuejs/core/commit/6f8589437635706f825ccec51800effba1d2bf5f))
|
||||
* **types:** infer the first generic type of `Ref` correctly ([#12094](https://github.com/vuejs/core/issues/12094)) ([c97bb84](https://github.com/vuejs/core/commit/c97bb84d0b0a16b012f886b6498e924415ed63e5))
|
||||
|
||||
|
||||
|
||||
## [3.5.10](https://github.com/vuejs/core/compare/v3.5.9...v3.5.10) (2024-09-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **custom-element:** properly set kebab-case props on Vue custom elements ([ea3efa0](https://github.com/vuejs/core/commit/ea3efa09e008918c1d9ba7226833a8b1a7a57244)), closes [#12030](https://github.com/vuejs/core/issues/12030) [#12032](https://github.com/vuejs/core/issues/12032)
|
||||
* **reactivity:** fix nested batch edge case ([93c95dd](https://github.com/vuejs/core/commit/93c95dd4cd416503f43a98a1455f62658d22b0b2))
|
||||
* **reactivity:** only clear notified flags for computed in first batch iteration ([aa9ef23](https://github.com/vuejs/core/commit/aa9ef2386a0cd39a174e5a887ec2b1a3525034fc)), closes [#12045](https://github.com/vuejs/core/issues/12045)
|
||||
* **types/ref:** handle nested refs in UnwrapRef ([#12049](https://github.com/vuejs/core/issues/12049)) ([e2c19c2](https://github.com/vuejs/core/commit/e2c19c20cfee9788519a80c0e53e216b78505994)), closes [#12044](https://github.com/vuejs/core/issues/12044)
|
||||
|
||||
|
||||
|
||||
## [3.5.9](https://github.com/vuejs/core/compare/v3.5.8...v3.5.9) (2024-09-26)
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ Please make sure to respect issue requirements and use [the new issue helper](ht
|
|||
|
||||
## Stay In Touch
|
||||
|
||||
- [Twitter](https://twitter.com/vuejs)
|
||||
- [X](https://x.com/vuejs)
|
||||
- [Bluesky](https://bsky.app/profile/vuejs.org)
|
||||
- [Blog](https://blog.vuejs.org/)
|
||||
- [Job Board](https://vuejobs.com/?ref=vuejs)
|
||||
|
||||
|
@ -44,7 +45,9 @@ Please make sure to read the [Contributing Guide](https://github.com/vuejs/core/
|
|||
|
||||
Thank you to all the people who already contributed to Vue!
|
||||
|
||||
<a href="https://github.com/vuejs/core/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>
|
||||
<a href="https://github.com/vuejs/core/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890&limit=500" /></a>
|
||||
|
||||
<sub>_Note: Showing the first 500 contributors only due to GitHub image size limitations_</sub>
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -56,13 +56,13 @@
|
|||
- **hydration:** handle camel-case tag name when performing match assertion ([#3247](https://github.com/vuejs/core/issues/3247)) ([9036f88](https://github.com/vuejs/core/commit/9036f88d8304a3455265f1ecd86ec8f4a5ea4715)), closes [#3243](https://github.com/vuejs/core/issues/3243)
|
||||
- **KeepAlive:** adapt keepalive for ssr ([#3259](https://github.com/vuejs/core/issues/3259)) ([e8e9b00](https://github.com/vuejs/core/commit/e8e9b00f81ed42434afd92f84101e7a14d70a23c)), closes [#3255](https://github.com/vuejs/core/issues/3255)
|
||||
- **reactivity:** ensure computed can be wrapped by readonly ([41e02f0](https://github.com/vuejs/core/commit/41e02f0fac069c93c94438741517e713f3c94215)), closes [#3376](https://github.com/vuejs/core/issues/3376)
|
||||
- **reactivity:** ensure that shallow and normal proxies are tracked seperately (close [#2843](https://github.com/vuejs/core/issues/2843)) ([#2851](https://github.com/vuejs/core/issues/2851)) ([22cc4a7](https://github.com/vuejs/core/commit/22cc4a76592cfe336e75e2fa0c05232ae1f0f149))
|
||||
- **reactivity:** ensure that shallow and normal proxies are tracked separately (close [#2843](https://github.com/vuejs/core/issues/2843)) ([#2851](https://github.com/vuejs/core/issues/2851)) ([22cc4a7](https://github.com/vuejs/core/commit/22cc4a76592cfe336e75e2fa0c05232ae1f0f149))
|
||||
- **reactivity:** fix shallow readonly behavior for collections ([#3003](https://github.com/vuejs/core/issues/3003)) ([68de9f4](https://github.com/vuejs/core/commit/68de9f408a2e61a5726a4a0d03b026cba451c5bd)), closes [#3007](https://github.com/vuejs/core/issues/3007)
|
||||
- **rumtime-core:** custom dom props should be cloned when cloning a hoisted DOM ([#3080](https://github.com/vuejs/core/issues/3080)) ([5dbe834](https://github.com/vuejs/core/commit/5dbe8348581dacd7a3594a9b0055ce350ce8e5bf)), closes [#3072](https://github.com/vuejs/core/issues/3072)
|
||||
- **runtime-core:** cache props default values to avoid unnecessary watcher trigger ([#3474](https://github.com/vuejs/core/issues/3474)) ([44166b4](https://github.com/vuejs/core/commit/44166b43d9be1062f79612880f71284049bcab0b)), closes [#3471](https://github.com/vuejs/core/issues/3471)
|
||||
- **runtime-core:** ensure only skip unflushed job ([#3406](https://github.com/vuejs/core/issues/3406)) ([bf34e33](https://github.com/vuejs/core/commit/bf34e33c909da89681b9c5004cdf04ab198ec5a7))
|
||||
- **runtime-core:** fix async component ref handling ([#3191](https://github.com/vuejs/core/issues/3191)) ([7562e72](https://github.com/vuejs/core/commit/7562e72c2b58a5646bd4fbd9adea11eb884fe140)), closes [#3188](https://github.com/vuejs/core/issues/3188)
|
||||
- **runtime-core:** fix erraneous emits warnings w/ mixins ([60d777d](https://github.com/vuejs/core/commit/60d777d228414515cc32526ad72a53ef070501be)), closes [#2651](https://github.com/vuejs/core/issues/2651)
|
||||
- **runtime-core:** fix erroneous emits warnings w/ mixins ([60d777d](https://github.com/vuejs/core/commit/60d777d228414515cc32526ad72a53ef070501be)), closes [#2651](https://github.com/vuejs/core/issues/2651)
|
||||
- **runtime-core:** fix warning for absent props ([#3363](https://github.com/vuejs/core/issues/3363)) ([86ceef4](https://github.com/vuejs/core/commit/86ceef43523bfbbb0a24731d3802ca6849cbefd6)), closes [#3362](https://github.com/vuejs/core/issues/3362)
|
||||
- **runtime-core:** handle error in async setup ([#2881](https://github.com/vuejs/core/issues/2881)) ([d668d48](https://github.com/vuejs/core/commit/d668d48e9e5211a49ee53361ea5b4d67ba16e0a3))
|
||||
- **runtime-core:** handle error in async watchEffect ([#3129](https://github.com/vuejs/core/issues/3129)) ([eb1fae6](https://github.com/vuejs/core/commit/eb1fae63f926435fb0eef890663d24e09d4c79e1))
|
||||
|
@ -202,7 +202,7 @@ may cause build issues in projects still using TS 3.x.
|
|||
- **script-setup:** ensure useContext() return valid context ([73cdb9d](https://github.com/vuejs/core/commit/73cdb9d4208f887fe08349657122e39175d7166c))
|
||||
- **slots:** dynamically named slots should be keyed by name ([2ab8c41](https://github.com/vuejs/core/commit/2ab8c41a1a43952fb229587a9da48d9a1214ab9e)), closes [#2535](https://github.com/vuejs/core/issues/2535)
|
||||
- **slots:** should render fallback content when slot content contains no valid nodes ([#2485](https://github.com/vuejs/core/issues/2485)) ([ce4915d](https://github.com/vuejs/core/commit/ce4915d8bed12f4cdb5fa8ca39bda98d0d3aabb7)), closes [#2347](https://github.com/vuejs/core/issues/2347) [#2461](https://github.com/vuejs/core/issues/2461)
|
||||
- **suspense:** fix nested async child toggle inside already resovled suspense ([cf7f1db](https://github.com/vuejs/core/commit/cf7f1dbc9be8d50ad220e3630c38f5a9a217d693)), closes [#2215](https://github.com/vuejs/core/issues/2215)
|
||||
- **suspense:** fix nested async child toggle inside already resolved suspense ([cf7f1db](https://github.com/vuejs/core/commit/cf7f1dbc9be8d50ad220e3630c38f5a9a217d693)), closes [#2215](https://github.com/vuejs/core/issues/2215)
|
||||
- **teleport:** Teleport into SVG elements ([#2648](https://github.com/vuejs/core/issues/2648)) ([cd92836](https://github.com/vuejs/core/commit/cd928362232747a51d1fd4790bb20adcdd59d187)), closes [#2652](https://github.com/vuejs/core/issues/2652)
|
||||
- **transition:** avoid invoking stale transition end callbacks ([eaf8a67](https://github.com/vuejs/core/commit/eaf8a67c7219e1b79d6abca44a1d7f1b341b58b0)), closes [#2482](https://github.com/vuejs/core/issues/2482)
|
||||
- **transition:** respect rules in \*-leave-from transition class ([#2597](https://github.com/vuejs/core/issues/2597)) ([e2618a6](https://github.com/vuejs/core/commit/e2618a632d4add2819ffb8b575af0da189dc3204)), closes [#2593](https://github.com/vuejs/core/issues/2593)
|
||||
|
@ -236,7 +236,7 @@ may cause build issues in projects still using TS 3.x.
|
|||
|
||||
- **compiler-sfc:** compileScript inline render function mode ([886ed76](https://github.com/vuejs/core/commit/886ed7681dd203c07ff3b504538328f43e14d9b0))
|
||||
- **compiler-sfc:** new script setup implementation ([556560f](https://github.com/vuejs/core/commit/556560fae31d9e406cfae656089657b6332686c1))
|
||||
- **compiler-sfc:** new SFC css varaible injection implementation ([41bb7fa](https://github.com/vuejs/core/commit/41bb7fa330e78c4a354a2e67742bd13bee2f4293))
|
||||
- **compiler-sfc:** new SFC css variable injection implementation ([41bb7fa](https://github.com/vuejs/core/commit/41bb7fa330e78c4a354a2e67742bd13bee2f4293))
|
||||
- **compiler-sfc:** support kebab-case components in `<script setup>` sfc template ([3f99e23](https://github.com/vuejs/core/commit/3f99e239e03a8861c462d4ee91feb82066ab3e28))
|
||||
- **runtime-core:** explicit expose API ([0e59770](https://github.com/vuejs/core/commit/0e59770b9282992f6a5af4d8fef33dafb948fc8b))
|
||||
|
||||
|
@ -282,7 +282,7 @@ may cause build issues in projects still using TS 3.x.
|
|||
- **runtime-core:** fix directive merging on component root ([4d1ebb5](https://github.com/vuejs/core/commit/4d1ebb5deb4c1cb2a02e8482bf8f9cc87197b088)), closes [#2298](https://github.com/vuejs/core/issues/2298)
|
||||
- **runtime-core:** fix duplicated unmount traversal in optimized mode ([376883d](https://github.com/vuejs/core/commit/376883d1cfea6ed92807cce1f1209f943a04b625)), closes [#2169](https://github.com/vuejs/core/issues/2169)
|
||||
- **runtime-core:** fix provide function data access in extends/mixins ([f06518a](https://github.com/vuejs/core/commit/f06518a8c9201b4fa2a956595aa9d89a192fcd20)), closes [#2300](https://github.com/vuejs/core/issues/2300)
|
||||
- **runtime-core:** fix SSR memoery leak due to props normalization cache ([a66e53a](https://github.com/vuejs/core/commit/a66e53a24f445b688eef6812ecb872dc53cf2702)), closes [#2225](https://github.com/vuejs/core/issues/2225)
|
||||
- **runtime-core:** fix SSR memory leak due to props normalization cache ([a66e53a](https://github.com/vuejs/core/commit/a66e53a24f445b688eef6812ecb872dc53cf2702)), closes [#2225](https://github.com/vuejs/core/issues/2225)
|
||||
- **runtime-core:** make errorCaptured return value handling consistent with Vue 2 ([#2289](https://github.com/vuejs/core/issues/2289)) ([4d20ac8](https://github.com/vuejs/core/commit/4d20ac8173f84c87288255dcc03c62a6ee862a23)), closes [#2267](https://github.com/vuejs/core/issues/2267)
|
||||
- **runtime-core:** use consistent camelCase event casing for render functions ([#2278](https://github.com/vuejs/core/issues/2278)) ([62f2617](https://github.com/vuejs/core/commit/62f26173ba715fd8bf2b131e19d94275106e830d)), closes [#2249](https://github.com/vuejs/core/issues/2249)
|
||||
- **runtime-core:** vnode.el is null in watcher after rerendering ([#2295](https://github.com/vuejs/core/issues/2295)) ([28d5fd7](https://github.com/vuejs/core/commit/28d5fd7a2871c10df3427dfbbe0e203c2a976cb4)), closes [#2170](https://github.com/vuejs/core/issues/2170)
|
||||
|
@ -450,7 +450,7 @@ may cause build issues in projects still using TS 3.x.
|
|||
- **compiler-core:** should attach key to single element child of `<template v-for>` ([#1910](https://github.com/vuejs/core/issues/1910)) ([69cfed6](https://github.com/vuejs/core/commit/69cfed6b313821d1ae7ecb02b63b0aaccb5599c6))
|
||||
- **reactivity:** unwrap non-index accessed refs on reactive arrays ([#1859](https://github.com/vuejs/core/issues/1859)) ([3c05f8b](https://github.com/vuejs/core/commit/3c05f8bbd6cd0e01bbc5830730852f9a93d8de8a)), closes [#1846](https://github.com/vuejs/core/issues/1846)
|
||||
- **runtime-core:** correctly track dynamic nodes in renderSlot ([#1911](https://github.com/vuejs/core/issues/1911)) ([7ffb79c](https://github.com/vuejs/core/commit/7ffb79c56318861075a47bd2357e34cde8a6dad9))
|
||||
- **runtime-core:** disable block tracking when calling compiled slot function in tempalte expressions ([f02e2f9](https://github.com/vuejs/core/commit/f02e2f99d9c2ca95f4fd984d7bd62178eceaa214)), closes [#1745](https://github.com/vuejs/core/issues/1745) [#1918](https://github.com/vuejs/core/issues/1918)
|
||||
- **runtime-core:** disable block tracking when calling compiled slot function in template expressions ([f02e2f9](https://github.com/vuejs/core/commit/f02e2f99d9c2ca95f4fd984d7bd62178eceaa214)), closes [#1745](https://github.com/vuejs/core/issues/1745) [#1918](https://github.com/vuejs/core/issues/1918)
|
||||
- **teleport:** only inherit el for non-patched nodes ([d4cc7b2](https://github.com/vuejs/core/commit/d4cc7b2496f9ed21ef6cac426697eac058da76bb)), closes [#1903](https://github.com/vuejs/core/issues/1903)
|
||||
|
||||
### Performance Improvements
|
||||
|
@ -631,7 +631,7 @@ may cause build issues in projects still using TS 3.x.
|
|||
- **runtime-dom/v-on:** only block event handlers based on attach timestamp ([8b320cc](https://github.com/vuejs/core/commit/8b320cc12f74aafea9ec69f7ce70231d4f0d08fd)), closes [#1565](https://github.com/vuejs/core/issues/1565)
|
||||
- **slots:** differentiate dynamic/static compiled slots ([65beba9](https://github.com/vuejs/core/commit/65beba98fe5793133d3218945218b9e3f8d136eb)), closes [#1557](https://github.com/vuejs/core/issues/1557)
|
||||
- **v-on:** capitalize dynamic event names ([9152a89](https://github.com/vuejs/core/commit/9152a8901653d7cef864a52a3c618afcc70d827d))
|
||||
- **v-on:** refactor DOM event options modifer handling ([380c679](https://github.com/vuejs/core/commit/380c6792d8899f1a43a9e6400c5df483c63290b6)), closes [#1567](https://github.com/vuejs/core/issues/1567)
|
||||
- **v-on:** refactor DOM event options modifier handling ([380c679](https://github.com/vuejs/core/commit/380c6792d8899f1a43a9e6400c5df483c63290b6)), closes [#1567](https://github.com/vuejs/core/issues/1567)
|
||||
|
||||
### Features
|
||||
|
||||
|
@ -743,7 +743,7 @@ may cause build issues in projects still using TS 3.x.
|
|||
- **compiler-core:** fix parsing for directive with dynamic argument containing dots ([0d26413](https://github.com/vuejs/core/commit/0d26413433d41389f5525a0ef2c2dd7cfbb454d4))
|
||||
- **compiler-core:** support static slot names containing dots for 2.x compat ([825ec15](https://github.com/vuejs/core/commit/825ec1500feda8b0c43245e7e92074af7f9dcca2)), closes [#1241](https://github.com/vuejs/core/issues/1241)
|
||||
- **hmr:** force full update on nested child components ([#1312](https://github.com/vuejs/core/issues/1312)) ([8f2a748](https://github.com/vuejs/core/commit/8f2a7489b7c74f5cfc1844697c60287c37fc0eb8))
|
||||
- **reactivity:** fix toRaw for objects prototype inherting reactive ([10bb34b](https://github.com/vuejs/core/commit/10bb34bb869a47c37d945f8c80abf723fac9fc1a)), closes [#1246](https://github.com/vuejs/core/issues/1246)
|
||||
- **reactivity:** fix toRaw for objects prototype inheriting reactive ([10bb34b](https://github.com/vuejs/core/commit/10bb34bb869a47c37d945f8c80abf723fac9fc1a)), closes [#1246](https://github.com/vuejs/core/issues/1246)
|
||||
- **runtime-core:** should pass instance to patchProp on mount for event error handling ([#1337](https://github.com/vuejs/core/issues/1337)) ([aac9b03](https://github.com/vuejs/core/commit/aac9b03c11c9be0c67b924004364a42d04d78195)), closes [#1336](https://github.com/vuejs/core/issues/1336)
|
||||
- **runtime-core:** track access to $attrs ([6abac87](https://github.com/vuejs/core/commit/6abac87b3d1b7a22df80b7a70a10101a7f3d3732)), closes [#1346](https://github.com/vuejs/core/issues/1346)
|
||||
- always treat spellcheck and draggable as attributes ([4492b88](https://github.com/vuejs/core/commit/4492b88938922a7f1bcc36a608375ad99f16b22e)), closes [#1350](https://github.com/vuejs/core/issues/1350)
|
||||
|
@ -863,7 +863,7 @@ may cause build issues in projects still using TS 3.x.
|
|||
|
||||
### Bug Fixes
|
||||
|
||||
- **compiler:** bail strigification on runtime constant expressions ([f9a3766](https://github.com/vuejs/core/commit/f9a3766fd68dc6996cdbda6475287c4005f55243))
|
||||
- **compiler:** bail stringification on runtime constant expressions ([f9a3766](https://github.com/vuejs/core/commit/f9a3766fd68dc6996cdbda6475287c4005f55243))
|
||||
- **transitionGroup:** fix transition children resolving condition ([f05aeea](https://github.com/vuejs/core/commit/f05aeea7aec2e6cd859f40edc6236afd0ce2ea7d))
|
||||
|
||||
### Features
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
- **build:** avoid using async/await syntax ([438754a](https://github.com/vuejs/core/commit/438754a0d1428d10e27d1a290beb4b81da5fdaeb))
|
||||
- **build:** fix generated code containing unprocessed class field syntax ([2788154](https://github.com/vuejs/core/commit/2788154f7707928f1dd3e4d9bd144f758a8c0478)), closes [#4052](https://github.com/vuejs/core/issues/4052) [vuejs/vue-cli#6562](https://github.com/vuejs/vue-cli/issues/6562)
|
||||
- **codegen:** ensure valid types in genreated code when using global directives ([a44d528](https://github.com/vuejs/core/commit/a44d528af1227c05dedf610b6ec45504d8e58276)), closes [#4054](https://github.com/vuejs/core/issues/4054)
|
||||
- **codegen:** ensure valid types in generated code when using global directives ([a44d528](https://github.com/vuejs/core/commit/a44d528af1227c05dedf610b6ec45504d8e58276)), closes [#4054](https://github.com/vuejs/core/issues/4054)
|
||||
- **compiler-sfc:** fix parse-only mode when there is no script setup block ([253ca27](https://github.com/vuejs/core/commit/253ca2729d808fc051215876aa4af986e4caa43c))
|
||||
- **runtime-core:** add useAttrs and useSlots export ([#4053](https://github.com/vuejs/core/issues/4053)) ([735ada1](https://github.com/vuejs/core/commit/735ada1507623b8d36e80b30a4f67a8af4a45c99))
|
||||
- **runtime-core:** fix instance accessed via $parent chain when using expose() ([#4048](https://github.com/vuejs/core/issues/4048)) ([12cf9f4](https://github.com/vuejs/core/commit/12cf9f4ea148a59fd9002ecf9ea9d365829ce37c))
|
||||
|
@ -114,7 +114,7 @@
|
|||
### Performance Improvements
|
||||
|
||||
- only trigger `$attrs` update when it has actually changed ([5566d39](https://github.com/vuejs/core/commit/5566d39d467ebdd4e4234bc97d62600ff01ea28e))
|
||||
- **compiler:** skip unncessary checks when parsing end tag ([048ac29](https://github.com/vuejs/core/commit/048ac299f35709b25ae1bc1efa67d2abc53dbc3b))
|
||||
- **compiler:** skip unnecessary checks when parsing end tag ([048ac29](https://github.com/vuejs/core/commit/048ac299f35709b25ae1bc1efa67d2abc53dbc3b))
|
||||
- avoid deopt for props/emits normalization when global mixins are used ([51d2be2](https://github.com/vuejs/core/commit/51d2be20386d4dc59006d31a1cc96676871027ce))
|
||||
|
||||
### Deprecations
|
||||
|
@ -181,7 +181,7 @@
|
|||
* **compat:** avoid accidentally delete the modelValue prop ([#3772](https://github.com/vuejs/core/issues/3772)) ([4f17be7](https://github.com/vuejs/core/commit/4f17be7b1ce4872ded085a36b95c1897d8c1f299))
|
||||
* **compat:** enum coercion warning ([#3755](https://github.com/vuejs/core/issues/3755)) ([f01aadf](https://github.com/vuejs/core/commit/f01aadf2a16a7bef422eb039d7b157bef9ad32fc))
|
||||
* **compiler-core:** fix whitespace management for slots with whitespace: 'preserve' ([#3767](https://github.com/vuejs/core/issues/3767)) ([47da921](https://github.com/vuejs/core/commit/47da92146c9fb3fa6b1e250e064ca49b74d815e4)), closes [#3766](https://github.com/vuejs/core/issues/3766)
|
||||
* **compiler-dom:** comments in the v-if branchs should be ignored when used in Transition ([#3622](https://github.com/vuejs/core/issues/3622)) ([7c74feb](https://github.com/vuejs/core/commit/7c74feb3dc6beae7ff3ad22193be3b5a0f4d8aac)), closes [#3619](https://github.com/vuejs/core/issues/3619)
|
||||
* **compiler-dom:** comments in the v-if branches should be ignored when used in Transition ([#3622](https://github.com/vuejs/core/issues/3622)) ([7c74feb](https://github.com/vuejs/core/commit/7c74feb3dc6beae7ff3ad22193be3b5a0f4d8aac)), closes [#3619](https://github.com/vuejs/core/issues/3619)
|
||||
* **compiler-sfc:** support tsx in setup script ([#3825](https://github.com/vuejs/core/issues/3825)) ([01e8ba8](https://github.com/vuejs/core/commit/01e8ba8f873afe3857a23fb68b44fdc057e31781)), closes [#3808](https://github.com/vuejs/core/issues/3808)
|
||||
* **compiler-ssr:** disable hoisting in compiler-ssr ([3ef1fcc](https://github.com/vuejs/core/commit/3ef1fcc8590da186664197a0a82e7856011c1693)), closes [#3536](https://github.com/vuejs/core/issues/3536)
|
||||
* **devtools:** send update to component owning the slot ([1355ee2](https://github.com/vuejs/core/commit/1355ee27a65d466bfe8f3a7ba99aa2213e25bc50))
|
||||
|
@ -265,7 +265,7 @@
|
|||
- **compat:** avoid accidentally delete the modelValue prop ([#3772](https://github.com/vuejs/core/issues/3772)) ([4f17be7](https://github.com/vuejs/core/commit/4f17be7b1ce4872ded085a36b95c1897d8c1f299))
|
||||
- **compat:** enum coercion warning ([#3755](https://github.com/vuejs/core/issues/3755)) ([f01aadf](https://github.com/vuejs/core/commit/f01aadf2a16a7bef422eb039d7b157bef9ad32fc))
|
||||
- **compiler-core:** fix whitespace management for slots with whitespace: 'preserve' ([#3767](https://github.com/vuejs/core/issues/3767)) ([47da921](https://github.com/vuejs/core/commit/47da92146c9fb3fa6b1e250e064ca49b74d815e4)), closes [#3766](https://github.com/vuejs/core/issues/3766)
|
||||
- **compiler-dom:** comments in the v-if branchs should be ignored when used in Transition ([#3622](https://github.com/vuejs/core/issues/3622)) ([7c74feb](https://github.com/vuejs/core/commit/7c74feb3dc6beae7ff3ad22193be3b5a0f4d8aac)), closes [#3619](https://github.com/vuejs/core/issues/3619)
|
||||
- **compiler-dom:** comments in the v-if branches should be ignored when used in Transition ([#3622](https://github.com/vuejs/core/issues/3622)) ([7c74feb](https://github.com/vuejs/core/commit/7c74feb3dc6beae7ff3ad22193be3b5a0f4d8aac)), closes [#3619](https://github.com/vuejs/core/issues/3619)
|
||||
- **compiler-sfc:** support tsx in setup script ([#3825](https://github.com/vuejs/core/issues/3825)) ([01e8ba8](https://github.com/vuejs/core/commit/01e8ba8f873afe3857a23fb68b44fdc057e31781)), closes [#3808](https://github.com/vuejs/core/issues/3808)
|
||||
- **compiler-ssr:** disable hoisting in compiler-ssr ([3ef1fcc](https://github.com/vuejs/core/commit/3ef1fcc8590da186664197a0a82e7856011c1693)), closes [#3536](https://github.com/vuejs/core/issues/3536)
|
||||
- **devtools:** send update to component owning the slot ([1355ee2](https://github.com/vuejs/core/commit/1355ee27a65d466bfe8f3a7ba99aa2213e25bc50))
|
||||
|
@ -317,4 +317,4 @@
|
|||
### Performance Improvements
|
||||
|
||||
- only trigger $attrs update when it has actually changed ([5566d39](https://github.com/vuejs/core/commit/5566d39d467ebdd4e4234bc97d62600ff01ea28e))
|
||||
- **compiler:** skip unncessary checks when parsing end tag ([048ac29](https://github.com/vuejs/core/commit/048ac299f35709b25ae1bc1efa67d2abc53dbc3b))
|
||||
- **compiler:** skip unnecessary checks when parsing end tag ([048ac29](https://github.com/vuejs/core/commit/048ac299f35709b25ae1bc1efa67d2abc53dbc3b))
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* **reactivity-transform:** fix $$ escape edge cases ([e06d3b6](https://github.com/vuejs/core/commit/e06d3b614ea518e9cdf83fca9200fc816eb4e5a1)), closes [#6312](https://github.com/vuejs/core/issues/6312) [#6944](https://github.com/vuejs/core/issues/6944)
|
||||
* **reactivity-transform:** prohibit const assignment at compile time ([#6993](https://github.com/vuejs/core/issues/6993)) ([3427052](https://github.com/vuejs/core/commit/3427052229db3448252d938292a40e960a0f4b9c)), closes [#6992](https://github.com/vuejs/core/issues/6992)
|
||||
* **reactivity:** `triggerRef` working with `toRef` from reactive ([#7507](https://github.com/vuejs/core/issues/7507)) ([e64c9ae](https://github.com/vuejs/core/commit/e64c9ae957aa2606b55e8652bbde30a6ada59fb0))
|
||||
* **reactivity:** ensure watch(Effect) can run independent of unmounted instance if created in a detatched effectScope (fix [#7319](https://github.com/vuejs/core/issues/7319)) ([#7330](https://github.com/vuejs/core/issues/7330)) ([cd7c887](https://github.com/vuejs/core/commit/cd7c887b755810aedf83f3d458cb956d5b147f6f))
|
||||
* **reactivity:** ensure watch(Effect) can run independent of unmounted instance if created in a detached effectScope (fix [#7319](https://github.com/vuejs/core/issues/7319)) ([#7330](https://github.com/vuejs/core/issues/7330)) ([cd7c887](https://github.com/vuejs/core/commit/cd7c887b755810aedf83f3d458cb956d5b147f6f))
|
||||
* **reactivity:** track hasOwnProperty ([588bd44](https://github.com/vuejs/core/commit/588bd44f036b79d7dee5d23661aa7244f70e6beb)), closes [#2619](https://github.com/vuejs/core/issues/2619) [#2621](https://github.com/vuejs/core/issues/2621)
|
||||
* **runtime-core:** ensure prop type validation warning shows custom class names ([#7198](https://github.com/vuejs/core/issues/7198)) ([620327d](https://github.com/vuejs/core/commit/620327d527593c6263a21500baddbae1ebc30db8))
|
||||
* **runtime-core:** fix keep-alive cache prune logic on vnodes with same type but different keys ([#7510](https://github.com/vuejs/core/issues/7510)) ([1fde49c](https://github.com/vuejs/core/commit/1fde49c0f57cc50fedf91366a274c9759d1d9a39)), closes [#7355](https://github.com/vuejs/core/issues/7355)
|
||||
|
@ -126,7 +126,7 @@
|
|||
* **transition/keep-alive:** fix unmount bug for component with out-in transition ([#6839](https://github.com/vuejs/core/issues/6839)) ([64e6d92](https://github.com/vuejs/core/commit/64e6d9221d353598b5f61c158c978d80e3b4628c)), closes [#6835](https://github.com/vuejs/core/issues/6835)
|
||||
* **types/reactivity-transform:** fix type when initial value is not used ([#6821](https://github.com/vuejs/core/issues/6821)) ([fdc5902](https://github.com/vuejs/core/commit/fdc5902cce0d077c722dfd422850ca69fd51be8e)), closes [#6820](https://github.com/vuejs/core/issues/6820)
|
||||
* **types:** `$watch` callback parameters type ([#6136](https://github.com/vuejs/core/issues/6136)) ([41d9c47](https://github.com/vuejs/core/commit/41d9c47300888fce9d4ff6a02f69d8a912cded8f)), closes [#6135](https://github.com/vuejs/core/issues/6135)
|
||||
* **types:** ensure createBlock() helper accepts Teleport and Supsense types (fix: [#2855](https://github.com/vuejs/core/issues/2855)) ([#5458](https://github.com/vuejs/core/issues/5458)) ([e5fc7dc](https://github.com/vuejs/core/commit/e5fc7dcc02f2dd3fa8172958259049031626375f))
|
||||
* **types:** ensure createBlock() helper accepts Teleport and Suspense types (fix: [#2855](https://github.com/vuejs/core/issues/2855)) ([#5458](https://github.com/vuejs/core/issues/5458)) ([e5fc7dc](https://github.com/vuejs/core/commit/e5fc7dcc02f2dd3fa8172958259049031626375f))
|
||||
* **types:** export `Raw` type ([#6380](https://github.com/vuejs/core/issues/6380)) ([e9172db](https://github.com/vuejs/core/commit/e9172db68b86fad2e0bb1de9e5d0dddbe3c2a25e)), closes [#7048](https://github.com/vuejs/core/issues/7048)
|
||||
* **types:** should unwrap tuple correctly ([#3820](https://github.com/vuejs/core/issues/3820)) ([e816812](https://github.com/vuejs/core/commit/e816812f10b9e3a375eef8dffd617d7f08b23c00)), closes [#3819](https://github.com/vuejs/core/issues/3819)
|
||||
* **types:** stricter type condition for `EventHandlers` ([#6855](https://github.com/vuejs/core/issues/6855)) ([bad3f3c](https://github.com/vuejs/core/commit/bad3f3ce46aad1f5fec47d1d02aee26af393bcff)), closes [#6899](https://github.com/vuejs/core/issues/6899)
|
||||
|
@ -714,7 +714,7 @@
|
|||
* **compiler-core:** avoid runtime dependency on @babel/types ([1045590](https://github.com/vuejs/core/commit/1045590d4bbaf4a2b05311f11b22a0b3d22cf609)), closes [#4531](https://github.com/vuejs/core/issues/4531)
|
||||
* **compiler-core:** pick last char when dynamic directive doesn't close ([#4507](https://github.com/vuejs/core/issues/4507)) ([5d262e0](https://github.com/vuejs/core/commit/5d262e08d5d5fb29f48ba5fa5b97a9a3e34b9d4b))
|
||||
* **compiler:** condense whitespaces in static class attributes ([#4432](https://github.com/vuejs/core/issues/4432)) ([b8653d3](https://github.com/vuejs/core/commit/b8653d390a555e1ee3f92a1c49cfd8800c67e46a)), closes [#4251](https://github.com/vuejs/core/issues/4251)
|
||||
* **runtime-dom:** style patching shoud always preserve v-show display property ([d534515](https://github.com/vuejs/core/commit/d53451583684c37bda7d30bff912216e1a58126f)), closes [#4424](https://github.com/vuejs/core/issues/4424)
|
||||
* **runtime-dom:** style patching should always preserve v-show display property ([d534515](https://github.com/vuejs/core/commit/d53451583684c37bda7d30bff912216e1a58126f)), closes [#4424](https://github.com/vuejs/core/issues/4424)
|
||||
* **type:** fix prop type infer ([#4530](https://github.com/vuejs/core/issues/4530)) ([4178d5d](https://github.com/vuejs/core/commit/4178d5d7d9549a0a1d19663bc2f92c8ac6a731b2)), closes [#4525](https://github.com/vuejs/core/issues/4525)
|
||||
|
||||
|
||||
|
@ -741,7 +741,7 @@
|
|||
|
||||
* **compiler-sfc:** ensure script setup generates type-valid ts output ([bacb201](https://github.com/vuejs/core/commit/bacb2012acb4045a2db6988ba4545a7655d6ca14)), closes [#4455](https://github.com/vuejs/core/issues/4455)
|
||||
* **compiler-sfc:** generate matching prop types when withDefaults is used ([#4466](https://github.com/vuejs/core/issues/4466)) ([8580796](https://github.com/vuejs/core/commit/85807967dc874e6ea6b20f341875beda938e3058)), closes [#4455](https://github.com/vuejs/core/issues/4455)
|
||||
* **compiler:** generate function ref for script setup if inline is ture. ([#4492](https://github.com/vuejs/core/issues/4492)) ([4cd282b](https://github.com/vuejs/core/commit/4cd282b0a17589ef9ca2649e7beb0bdee4a73c57))
|
||||
* **compiler:** generate function ref for script setup if inline is true. ([#4492](https://github.com/vuejs/core/issues/4492)) ([4cd282b](https://github.com/vuejs/core/commit/4cd282b0a17589ef9ca2649e7beb0bdee4a73c57))
|
||||
* **compiler:** report invalid directive name error ([#4494](https://github.com/vuejs/core/issues/4494)) ([#4495](https://github.com/vuejs/core/issues/4495)) ([c00925e](https://github.com/vuejs/core/commit/c00925ed5c409b57a1540b79c595b7f8117e2d4c))
|
||||
* **types:** include ref-macros.d.ts in npm dist files ([d7f1b77](https://github.com/vuejs/core/commit/d7f1b771f80ab9014a4701913b50458fd251a117)), closes [#4433](https://github.com/vuejs/core/issues/4433)
|
||||
|
||||
|
@ -798,7 +798,7 @@
|
|||
### Bug Fixes
|
||||
|
||||
* **compiler-sfc:** fix import usage check for lowercase imported components ([57f1081](https://github.com/vuejs/core/commit/57f10812cc7f1e9f6c92736c36aba577943996fd)), closes [#4358](https://github.com/vuejs/core/issues/4358)
|
||||
* **runtime-core:** ensure consistent arguments for tempalte and render funtion slot usage ([644971e](https://github.com/vuejs/core/commit/644971ec06642817cf7e720ad4980182d2140f53)), closes [#4367](https://github.com/vuejs/core/issues/4367)
|
||||
* **runtime-core:** ensure consistent arguments for template and render function slot usage ([644971e](https://github.com/vuejs/core/commit/644971ec06642817cf7e720ad4980182d2140f53)), closes [#4367](https://github.com/vuejs/core/issues/4367)
|
||||
* **runtime-core:** fix child component double update on props change ([c1f564e](https://github.com/vuejs/core/commit/c1f564e1dc40eda9af657c30cd787a8d770dde0f)), closes [#4365](https://github.com/vuejs/core/issues/4365)
|
||||
|
||||
|
||||
|
|
|
@ -259,7 +259,7 @@
|
|||
|
||||
* **sfc:** support imported types in SFC macros ([#8083](https://github.com/vuejs/core/pull/8083))
|
||||
* **types/slots:** support slot presence / props type checks via `defineSlots` macro and `slots` option ([#7982](https://github.com/vuejs/core/issues/7982)) ([5a2f5d5](https://github.com/vuejs/core/commit/5a2f5d59cffa36a99e6f2feab6b3ba7958b7362f))
|
||||
* **sfc:** support more ergnomic defineEmits type syntax ([#7992](https://github.com/vuejs/core/issues/7992)) ([8876dcc](https://github.com/vuejs/core/commit/8876dccf42a7f05375d97cb18c1afdfd0fc51c94))
|
||||
* **sfc:** support more ergonomic defineEmits type syntax ([#7992](https://github.com/vuejs/core/issues/7992)) ([8876dcc](https://github.com/vuejs/core/commit/8876dccf42a7f05375d97cb18c1afdfd0fc51c94))
|
||||
* **sfc:** introduce `defineModel` macro and `useModel` helper ([#8018](https://github.com/vuejs/core/issues/8018)) ([14f3d74](https://github.com/vuejs/core/commit/14f3d747a34d45415b0036b274517d70a27ec0d3))
|
||||
* **reactivity:** improve support of getter usage in reactivity APIs ([#7997](https://github.com/vuejs/core/issues/7997)) ([59e8284](https://github.com/vuejs/core/commit/59e828448e7f37643cd0eaea924a764e9d314448))
|
||||
* **compiler-sfc:** add defineOptions macro ([#5738](https://github.com/vuejs/core/issues/5738)) ([bcf5841](https://github.com/vuejs/core/commit/bcf5841ddecc64d0bdbd56ce1463eb8ebf01bb9d))
|
||||
|
@ -483,7 +483,7 @@
|
|||
* **compiler-sfc:** support arbitrary expression as withDefaults argument ([fe61944](https://github.com/vuejs/core/commit/fe619443d2e99301975de120685dbae8d66c03a6)), closes [#6459](https://github.com/vuejs/core/issues/6459)
|
||||
* **reactivity:** improve support of getter usage in reactivity APIs ([#7997](https://github.com/vuejs/core/issues/7997)) ([59e8284](https://github.com/vuejs/core/commit/59e828448e7f37643cd0eaea924a764e9d314448))
|
||||
* **sfc:** revert withDefaults() deprecation ([4af5d1b](https://github.com/vuejs/core/commit/4af5d1b0754035058436f9e4e5c12aedef199177))
|
||||
* **sfc:** support more ergnomic defineEmits type syntax ([#7992](https://github.com/vuejs/core/issues/7992)) ([8876dcc](https://github.com/vuejs/core/commit/8876dccf42a7f05375d97cb18c1afdfd0fc51c94))
|
||||
* **sfc:** support more ergonomic defineEmits type syntax ([#7992](https://github.com/vuejs/core/issues/7992)) ([8876dcc](https://github.com/vuejs/core/commit/8876dccf42a7f05375d97cb18c1afdfd0fc51c94))
|
||||
* **types/slots:** support slot presence / props type checks via `defineSlots` macro and `slots` option ([#7982](https://github.com/vuejs/core/issues/7982)) ([5a2f5d5](https://github.com/vuejs/core/commit/5a2f5d59cffa36a99e6f2feab6b3ba7958b7362f))
|
||||
|
||||
|
||||
|
@ -544,7 +544,7 @@
|
|||
|
||||
### Bug Fixes
|
||||
|
||||
* **runtime-core:** support `getCurrentInstance` across mutiple builds of Vue ([8d2d5bf](https://github.com/vuejs/core/commit/8d2d5bf48a24dab44e5b03cb8fa0c5faa4b696e3))
|
||||
* **runtime-core:** support `getCurrentInstance` across multiple builds of Vue ([8d2d5bf](https://github.com/vuejs/core/commit/8d2d5bf48a24dab44e5b03cb8fa0c5faa4b696e3))
|
||||
* **types:** ensure defineProps with generics return correct types ([c288c7b](https://github.com/vuejs/core/commit/c288c7b0bd6077d690f42153c3fc49a45454a66a))
|
||||
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@
|
|||
### Bug Fixes
|
||||
|
||||
* **compat:** correctly transform non-identifier expressions in legacy filter syntax ([#10896](https://github.com/vuejs/core/issues/10896)) ([07b3c4b](https://github.com/vuejs/core/commit/07b3c4b7860009e19446f3d78571556c5737d82a)), closes [#10852](https://github.com/vuejs/core/issues/10852)
|
||||
* **compat:** ensure proper handling of render fuction from SFC using Vue.extend ([#7781](https://github.com/vuejs/core/issues/7781)) ([c73847f](https://github.com/vuejs/core/commit/c73847f2becc20f03cb9c68748eea92455e688ee)), closes [#7766](https://github.com/vuejs/core/issues/7766)
|
||||
* **compat:** ensure proper handling of render function from SFC using Vue.extend ([#7781](https://github.com/vuejs/core/issues/7781)) ([c73847f](https://github.com/vuejs/core/commit/c73847f2becc20f03cb9c68748eea92455e688ee)), closes [#7766](https://github.com/vuejs/core/issues/7766)
|
||||
* **compat:** only warn ATTR_FALSE_VALUE when enabled ([04729ba](https://github.com/vuejs/core/commit/04729ba2163d840f0ca7866bc964696eb5557804)), closes [#11126](https://github.com/vuejs/core/issues/11126)
|
||||
* **compile-sfc:** register props destructure rest id as setup bindings ([#10888](https://github.com/vuejs/core/issues/10888)) ([b2b5f57](https://github.com/vuejs/core/commit/b2b5f57c2c945edd0eebc1b545ec1b7568e51484)), closes [#10885](https://github.com/vuejs/core/issues/10885)
|
||||
* **compile-sfc:** Support project reference with folder, ([#10908](https://github.com/vuejs/core/issues/10908)) ([bdeac37](https://github.com/vuejs/core/commit/bdeac377c7b85888193b49ac187e927636cc40bc)), closes [#10907](https://github.com/vuejs/core/issues/10907)
|
||||
|
@ -218,7 +218,7 @@
|
|||
### Bug Fixes
|
||||
|
||||
* **compat:** include legacy scoped slots ([#10868](https://github.com/vuejs/core/issues/10868)) ([8366126](https://github.com/vuejs/core/commit/83661264a4ced3cb2ff6800904a86dd9e82bbfe2)), closes [#8869](https://github.com/vuejs/core/issues/8869)
|
||||
* **compiler-core:** add support for arrow aysnc function with unbracketed ([#5789](https://github.com/vuejs/core/issues/5789)) ([ca7d421](https://github.com/vuejs/core/commit/ca7d421e8775f6813f8943d32ab485e0c542f98b)), closes [#5788](https://github.com/vuejs/core/issues/5788)
|
||||
* **compiler-core:** add support for arrow async function with unbracketed ([#5789](https://github.com/vuejs/core/issues/5789)) ([ca7d421](https://github.com/vuejs/core/commit/ca7d421e8775f6813f8943d32ab485e0c542f98b)), closes [#5788](https://github.com/vuejs/core/issues/5788)
|
||||
* **compiler-dom:** restrict createStaticVNode usage with option elements ([#10846](https://github.com/vuejs/core/issues/10846)) ([0e3d617](https://github.com/vuejs/core/commit/0e3d6178b02d0386d779720ae2cc4eac1d1ec990)), closes [#6568](https://github.com/vuejs/core/issues/6568) [#7434](https://github.com/vuejs/core/issues/7434)
|
||||
* **compiler-sfc:** handle keyof operator ([#10874](https://github.com/vuejs/core/issues/10874)) ([10d34a5](https://github.com/vuejs/core/commit/10d34a5624775f20437ccad074a97270ef74c3fb)), closes [#10871](https://github.com/vuejs/core/issues/10871)
|
||||
* **hydration:** handle edge case of style mismatch without style attribute ([f2c1412](https://github.com/vuejs/core/commit/f2c1412e46a8fad3e13403bfa78335c4f704f21c)), closes [#10786](https://github.com/vuejs/core/issues/10786)
|
||||
|
@ -417,7 +417,7 @@
|
|||
|
||||
* **compiler-sfc:** fix type resolution for symlinked node_modules structure w/ pnpm ([75e866b](https://github.com/vuejs/core/commit/75e866bd4ef368b4e037a4933dbaf188920dc683)), closes [#10121](https://github.com/vuejs/core/issues/10121)
|
||||
* correct url for production error reference links ([c3087ff](https://github.com/vuejs/core/commit/c3087ff2cce7d96c60a870f8233441311ab4dfb4))
|
||||
* **hydration:** fix incorect mismatch warning for option with non-string value and inner text ([d16a213](https://github.com/vuejs/core/commit/d16a2138a33b106b9e1499bbb9e1c67790370c97))
|
||||
* **hydration:** fix incorrect mismatch warning for option with non-string value and inner text ([d16a213](https://github.com/vuejs/core/commit/d16a2138a33b106b9e1499bbb9e1c67790370c97))
|
||||
* **reactivity:** re-fix [#10114](https://github.com/vuejs/core/issues/10114) ([#10123](https://github.com/vuejs/core/issues/10123)) ([c2b274a](https://github.com/vuejs/core/commit/c2b274a887f61deb7e0185d1bef3b77d31e991cc))
|
||||
* **runtime-core:** should not warn out-of-render slot fn usage when mounting another app in setup ([#10125](https://github.com/vuejs/core/issues/10125)) ([6fa33e6](https://github.com/vuejs/core/commit/6fa33e67ec42af140a86fbdb86939032c3a1f345)), closes [#10124](https://github.com/vuejs/core/issues/10124)
|
||||
|
||||
|
@ -741,17 +741,6 @@ Note that this is a type-only breaking change in a minor release, which adheres
|
|||
|
||||
|
||||
|
||||
## [3.3.13](https://github.com/vuejs/core/compare/v3.3.12...v3.3.13) (2023-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** fix v-on with modifiers on inline expression of undefined ([#9866](https://github.com/vuejs/core/issues/9866)) ([bae79dd](https://github.com/vuejs/core/commit/bae79ddf8564a2da4a5365cfeb8d811990f42335)), closes [#9865](https://github.com/vuejs/core/issues/9865)
|
||||
* **runtime-dom:** cache event handlers by key/modifiers ([#9851](https://github.com/vuejs/core/issues/9851)) ([04d2c05](https://github.com/vuejs/core/commit/04d2c05054c26b02fbc1d84839b0ed5cd36455b6)), closes [#9849](https://github.com/vuejs/core/issues/9849)
|
||||
* **types:** extract properties from extended collections ([#9854](https://github.com/vuejs/core/issues/9854)) ([24b1c1d](https://github.com/vuejs/core/commit/24b1c1dd57fd55d998aa231a147500e010b10219)), closes [#9852](https://github.com/vuejs/core/issues/9852)
|
||||
|
||||
|
||||
|
||||
# [3.4.0-beta.3](https://github.com/vuejs/core/compare/v3.3.12...v3.4.0-beta.3) (2023-12-16)
|
||||
|
||||
|
||||
|
@ -764,19 +753,6 @@ Note that this is a type-only breaking change in a minor release, which adheres
|
|||
|
||||
|
||||
|
||||
## [3.3.12](https://github.com/vuejs/core/compare/v3.3.11...v3.3.12) (2023-12-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hydration:** handle appear transition before patch props ([#9837](https://github.com/vuejs/core/issues/9837)) ([e70f4c4](https://github.com/vuejs/core/commit/e70f4c47c553b6e16d8fad70743271ca23802fe7)), closes [#9832](https://github.com/vuejs/core/issues/9832)
|
||||
* **sfc/cssVars:** fix loss of CSS v-bind variables when setting inline style with string value ([#9824](https://github.com/vuejs/core/issues/9824)) ([0a387df](https://github.com/vuejs/core/commit/0a387dfb1d04afb6eae4296b6da76dfdaca77af4)), closes [#9821](https://github.com/vuejs/core/issues/9821)
|
||||
* **ssr:** fix suspense hydration of fallback content ([#7188](https://github.com/vuejs/core/issues/7188)) ([60415b5](https://github.com/vuejs/core/commit/60415b5d67df55f1fd6b176615299c08640fa142))
|
||||
* **types:** add `xmlns:xlink` to `SVGAttributes` ([#9300](https://github.com/vuejs/core/issues/9300)) ([0d61b42](https://github.com/vuejs/core/commit/0d61b429ecf63591d31e09702058fa4c7132e1a7)), closes [#9299](https://github.com/vuejs/core/issues/9299)
|
||||
* **types:** fix `shallowRef` type error ([#9839](https://github.com/vuejs/core/issues/9839)) ([9a57158](https://github.com/vuejs/core/commit/9a571582b53220270e498d8712ea59312c0bef3a))
|
||||
* **types:** support for generic keyof slots ([#8374](https://github.com/vuejs/core/issues/8374)) ([213eba4](https://github.com/vuejs/core/commit/213eba479ce080efc1053fe636f6be4a4c889b44))
|
||||
|
||||
|
||||
|
||||
# [3.4.0-beta.2](https://github.com/vuejs/core/compare/v3.4.0-beta.1...v3.4.0-beta.2) (2023-12-14)
|
||||
|
||||
|
||||
|
@ -836,22 +812,6 @@ default.
|
|||
|
||||
|
||||
|
||||
## [3.3.11](https://github.com/vuejs/core/compare/v3.3.10...v3.3.11) (2023-12-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **custom-element:** correctly handle number type props in prod ([#8989](https://github.com/vuejs/core/issues/8989)) ([d74d364](https://github.com/vuejs/core/commit/d74d364d62db8e48881af6b5a75ce4fb5f36cc35))
|
||||
* **reactivity:** fix mutation on user proxy of reactive Array ([6ecbd5c](https://github.com/vuejs/core/commit/6ecbd5ce2a7f59314a8326a1d193874b87f4d8c8)), closes [#9742](https://github.com/vuejs/core/issues/9742) [#9751](https://github.com/vuejs/core/issues/9751) [#9750](https://github.com/vuejs/core/issues/9750)
|
||||
* **runtime-dom:** fix width and height prop check condition ([5b00286](https://github.com/vuejs/core/commit/5b002869c533220706f9788b496b8ca8d8e98609)), closes [#9762](https://github.com/vuejs/core/issues/9762)
|
||||
* **shared:** handle Map with symbol keys in toDisplayString ([#9731](https://github.com/vuejs/core/issues/9731)) ([364821d](https://github.com/vuejs/core/commit/364821d6bdb1775e2f55a69bcfb9f40f7acf1506)), closes [#9727](https://github.com/vuejs/core/issues/9727)
|
||||
* **shared:** handle more Symbol cases in toDisplayString ([983d45d](https://github.com/vuejs/core/commit/983d45d4f8eb766b5a16b7ea93b86d3c51618fa6))
|
||||
* **Suspense:** properly get anchor when mount fallback vnode ([#9770](https://github.com/vuejs/core/issues/9770)) ([b700328](https://github.com/vuejs/core/commit/b700328342e17dc16b19316c2e134a26107139d2)), closes [#9769](https://github.com/vuejs/core/issues/9769)
|
||||
* **types:** ref() return type should not be any when initial value is any ([#9768](https://github.com/vuejs/core/issues/9768)) ([cdac121](https://github.com/vuejs/core/commit/cdac12161ec27b45ded48854c3d749664b6d4a6d))
|
||||
* **watch:** should not fire pre watcher on child component unmount ([#7181](https://github.com/vuejs/core/issues/7181)) ([6784f0b](https://github.com/vuejs/core/commit/6784f0b1f8501746ea70d87d18ed63a62cf6b76d)), closes [#7030](https://github.com/vuejs/core/issues/7030)
|
||||
|
||||
|
||||
|
||||
# [3.4.0-alpha.4](https://github.com/vuejs/core/compare/v3.3.10...v3.4.0-alpha.4) (2023-12-04)
|
||||
|
||||
|
||||
|
@ -873,37 +833,6 @@ default.
|
|||
|
||||
|
||||
|
||||
## [3.3.10](https://github.com/vuejs/core/compare/v3.3.9...v3.3.10) (2023-12-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **app:** prevent template from being cached between apps with different options ([#9724](https://github.com/vuejs/core/issues/9724)) ([ec71585](https://github.com/vuejs/core/commit/ec715854ca12520b2afc9e9b3981cbae05ae5206)), closes [#9618](https://github.com/vuejs/core/issues/9618)
|
||||
* **compiler-sfc:** avoid passing forEach index to genMap ([f12db7f](https://github.com/vuejs/core/commit/f12db7fb564a534cef2e5805cc9f54afe5d72fbf))
|
||||
* **compiler-sfc:** deindent pug/jade templates ([6345197](https://github.com/vuejs/core/commit/634519720a21fb5a6871454e1cadad7053a568b8)), closes [#3231](https://github.com/vuejs/core/issues/3231) [#3842](https://github.com/vuejs/core/issues/3842) [#7723](https://github.com/vuejs/core/issues/7723)
|
||||
* **compiler-sfc:** fix :where and :is selector in scoped mode with multiple selectors ([#9735](https://github.com/vuejs/core/issues/9735)) ([c3e2c55](https://github.com/vuejs/core/commit/c3e2c556b532656b50b8ab5cd2d9eabc26622d63)), closes [#9707](https://github.com/vuejs/core/issues/9707)
|
||||
* **compiler-sfc:** generate more treeshaking friendly code ([#9507](https://github.com/vuejs/core/issues/9507)) ([8d74ca0](https://github.com/vuejs/core/commit/8d74ca0e6fa2738ca6854b7e879ff59419f948c7)), closes [#9500](https://github.com/vuejs/core/issues/9500)
|
||||
* **compiler-sfc:** support inferring generic types ([#8511](https://github.com/vuejs/core/issues/8511)) ([eb5e307](https://github.com/vuejs/core/commit/eb5e307c0be62002e62c4c800d0dfacb39b0d4ca)), closes [#8482](https://github.com/vuejs/core/issues/8482)
|
||||
* **compiler-sfc:** support resolving components from props ([#8785](https://github.com/vuejs/core/issues/8785)) ([7cbcee3](https://github.com/vuejs/core/commit/7cbcee3d831241a8bd3588ae92d3f27e3641e25f))
|
||||
* **compiler-sfc:** throw error when failing to load TS during type resolution ([#8883](https://github.com/vuejs/core/issues/8883)) ([4936d2e](https://github.com/vuejs/core/commit/4936d2e11a8d0ca3704bfe408548cb26bb3fd5e9))
|
||||
* **cssVars:** cssVar names should be double-escaped when generating code for ssr ([#8824](https://github.com/vuejs/core/issues/8824)) ([5199a12](https://github.com/vuejs/core/commit/5199a12f8855cd06f24bf355708b5a2134f63176)), closes [#7823](https://github.com/vuejs/core/issues/7823)
|
||||
* **deps:** update compiler to ^7.23.4 ([#9681](https://github.com/vuejs/core/issues/9681)) ([31f6ebc](https://github.com/vuejs/core/commit/31f6ebc4df84490ed29fb75e7bf4259200eb51f0))
|
||||
* **runtime-core:** Suspense get anchor properly in Transition ([#9309](https://github.com/vuejs/core/issues/9309)) ([65f3fe2](https://github.com/vuejs/core/commit/65f3fe273127a8b68e1222fbb306d28d85f01757)), closes [#8105](https://github.com/vuejs/core/issues/8105)
|
||||
* **runtime-dom:** set width/height with units as attribute ([#8781](https://github.com/vuejs/core/issues/8781)) ([bfc1838](https://github.com/vuejs/core/commit/bfc1838f31199de3f189198a3c234fa7bae91386))
|
||||
* **ssr:** avoid computed being accidentally cached before server render ([#9688](https://github.com/vuejs/core/issues/9688)) ([30d5d93](https://github.com/vuejs/core/commit/30d5d93a92b2154406ec04f8aca6b217fa01177c)), closes [#5300](https://github.com/vuejs/core/issues/5300)
|
||||
* **types:** expose emits as props in functional components ([#9234](https://github.com/vuejs/core/issues/9234)) ([887e54c](https://github.com/vuejs/core/commit/887e54c347ea9eac4c721b5e2288f054873d1d30))
|
||||
* **types:** fix reactive collection types ([#8960](https://github.com/vuejs/core/issues/8960)) ([ad27473](https://github.com/vuejs/core/commit/ad274737015c36906d76f3189203093fa3a2e4e7)), closes [#8904](https://github.com/vuejs/core/issues/8904)
|
||||
* **types:** improve return type withKeys and withModifiers ([#9734](https://github.com/vuejs/core/issues/9734)) ([43c3cfd](https://github.com/vuejs/core/commit/43c3cfdec5ae5d70fa2a21e857abc2d73f1a0d07))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* optimize on* prop check ([38aaa8c](https://github.com/vuejs/core/commit/38aaa8c88648c54fe2616ad9c0961288092fcb44))
|
||||
* **runtime-dom:** cache modifier wrapper functions ([da4a4fb](https://github.com/vuejs/core/commit/da4a4fb5e8eee3c6d31f24ebd79a9d0feca56cb2)), closes [#8882](https://github.com/vuejs/core/issues/8882)
|
||||
* **v-on:** constant handlers with modifiers should not be treated as dynamic ([4d94ebf](https://github.com/vuejs/core/commit/4d94ebfe75174b340d2b794e699cad1add3600a9))
|
||||
|
||||
|
||||
|
||||
# [3.4.0-alpha.3](https://github.com/vuejs/core/compare/v3.4.0-alpha.2...v3.4.0-alpha.3) (2023-11-28)
|
||||
|
||||
|
||||
|
@ -960,55 +889,6 @@ default.
|
|||
|
||||
|
||||
|
||||
## [3.3.9](https://github.com/vuejs/core/compare/v3.3.8...v3.3.9) (2023-11-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** avoid rewriting scope variables in inline for loops ([#7245](https://github.com/vuejs/core/issues/7245)) ([a2d810e](https://github.com/vuejs/core/commit/a2d810eb40cef631f61991ca68b426ee9546aba0)), closes [#7238](https://github.com/vuejs/core/issues/7238)
|
||||
* **compiler-core:** fix `resolveParserPlugins` decorators check ([#9566](https://github.com/vuejs/core/issues/9566)) ([9d0eba9](https://github.com/vuejs/core/commit/9d0eba916f3bf6fb5c03222400edae1a2db7444f)), closes [#9560](https://github.com/vuejs/core/issues/9560)
|
||||
* **compiler-sfc:** consistently escape type-only prop names ([#8654](https://github.com/vuejs/core/issues/8654)) ([3e08d24](https://github.com/vuejs/core/commit/3e08d246dfd8523c54fb8e7a4a6fd5506ffb1bcc)), closes [#8635](https://github.com/vuejs/core/issues/8635) [#8910](https://github.com/vuejs/core/issues/8910) [vitejs/vite-plugin-vue#184](https://github.com/vitejs/vite-plugin-vue/issues/184)
|
||||
* **compiler-sfc:** malformed filename on windows using path.posix.join() ([#9478](https://github.com/vuejs/core/issues/9478)) ([f18a174](https://github.com/vuejs/core/commit/f18a174979626b3429db93c5d5b7ae5448917c70)), closes [#8671](https://github.com/vuejs/core/issues/8671) [#9583](https://github.com/vuejs/core/issues/9583) [#9446](https://github.com/vuejs/core/issues/9446) [#9473](https://github.com/vuejs/core/issues/9473)
|
||||
* **compiler-sfc:** support `:is` and `:where` selector in scoped css rewrite ([#8929](https://github.com/vuejs/core/issues/8929)) ([3227e50](https://github.com/vuejs/core/commit/3227e50b32105f8893f7dff2f29278c5b3a9f621))
|
||||
* **compiler-sfc:** support resolve extends interface for defineEmits ([#8470](https://github.com/vuejs/core/issues/8470)) ([9e1b74b](https://github.com/vuejs/core/commit/9e1b74bcd5fa4151f5d1bc02c69fbbfa4762f577)), closes [#8465](https://github.com/vuejs/core/issues/8465)
|
||||
* **hmr/transition:** fix kept-alive component inside transition disappearing after hmr ([#7126](https://github.com/vuejs/core/issues/7126)) ([d11e978](https://github.com/vuejs/core/commit/d11e978fc98dcc83526c167e603b8308f317f786)), closes [#7121](https://github.com/vuejs/core/issues/7121)
|
||||
* **hydration:** force hydration for v-bind with .prop modifier ([364f319](https://github.com/vuejs/core/commit/364f319d214226770d97c98d8fcada80c9e8dde3)), closes [#7490](https://github.com/vuejs/core/issues/7490)
|
||||
* **hydration:** properly hydrate indeterminate prop ([34b5a5d](https://github.com/vuejs/core/commit/34b5a5da4ae9c9faccac237acd7acc8e7e017571)), closes [#7476](https://github.com/vuejs/core/issues/7476)
|
||||
* **reactivity:** clear method on readonly collections should return undefined ([#7316](https://github.com/vuejs/core/issues/7316)) ([657476d](https://github.com/vuejs/core/commit/657476dcdb964be4fbb1277c215c073f3275728e))
|
||||
* **reactivity:** onCleanup also needs to be cleaned ([#8655](https://github.com/vuejs/core/issues/8655)) ([73fd810](https://github.com/vuejs/core/commit/73fd810eebdd383a2b4629f67736c4db1f428abd)), closes [#5151](https://github.com/vuejs/core/issues/5151) [#7695](https://github.com/vuejs/core/issues/7695)
|
||||
* **ssr:** hydration `__vnode` missing for devtools ([#9328](https://github.com/vuejs/core/issues/9328)) ([5156ac5](https://github.com/vuejs/core/commit/5156ac5b38cfa80d3db26f2c9bf40cb22a7521cb))
|
||||
* **types:** allow falsy value types in `StyleValue` ([#7954](https://github.com/vuejs/core/issues/7954)) ([17aa92b](https://github.com/vuejs/core/commit/17aa92b79b31d8bb8b5873ddc599420cb9806db8)), closes [#7955](https://github.com/vuejs/core/issues/7955)
|
||||
* **types:** defineCustomElement using defineComponent return type with emits ([#7937](https://github.com/vuejs/core/issues/7937)) ([5d932a8](https://github.com/vuejs/core/commit/5d932a8e6d14343c9d7fc7c2ecb58ac618b2f938)), closes [#7782](https://github.com/vuejs/core/issues/7782)
|
||||
* **types:** fix `unref` and `toValue` when input union type contains ComputedRef ([#8748](https://github.com/vuejs/core/issues/8748)) ([176d476](https://github.com/vuejs/core/commit/176d47671271b1abc21b1508e9a493c7efca6451)), closes [#8747](https://github.com/vuejs/core/issues/8747) [#8857](https://github.com/vuejs/core/issues/8857)
|
||||
* **types:** fix instance type when props type is incompatible with setup returned type ([#7338](https://github.com/vuejs/core/issues/7338)) ([0e1e8f9](https://github.com/vuejs/core/commit/0e1e8f919e5a74cdaadf9c80ee135088b25e7fa3)), closes [#5885](https://github.com/vuejs/core/issues/5885)
|
||||
* **types:** fix shallowRef return type with union value type ([#7853](https://github.com/vuejs/core/issues/7853)) ([7c44800](https://github.com/vuejs/core/commit/7c448000b0def910c2cfabfdf7ff20a3d6bc844f)), closes [#7852](https://github.com/vuejs/core/issues/7852)
|
||||
* **types:** more precise types for class bindings ([#8012](https://github.com/vuejs/core/issues/8012)) ([46e3374](https://github.com/vuejs/core/commit/46e33744c890bd49482c5e5c5cdea44e00ec84d5))
|
||||
* **types:** remove optional properties from defineProps return type ([#6421](https://github.com/vuejs/core/issues/6421)) ([94c049d](https://github.com/vuejs/core/commit/94c049d930d922069e38ea8700d7ff0970f71e61)), closes [#6420](https://github.com/vuejs/core/issues/6420)
|
||||
* **types:** return type of withDefaults should be readonly ([#8601](https://github.com/vuejs/core/issues/8601)) ([f15debc](https://github.com/vuejs/core/commit/f15debc01acb22d23f5acee97e6f02db88cef11a))
|
||||
* **types:** revert class type restrictions ([5d077c8](https://github.com/vuejs/core/commit/5d077c8754cc14f85d2d6d386df70cf8c0d93842)), closes [#8012](https://github.com/vuejs/core/issues/8012)
|
||||
* **types:** update jsx type definitions ([#8607](https://github.com/vuejs/core/issues/8607)) ([58e2a94](https://github.com/vuejs/core/commit/58e2a94871ae06a909c5f8bad07fb401193e6a38))
|
||||
* **types:** widen ClassValue type ([2424013](https://github.com/vuejs/core/commit/242401305944422d0c361b16101a4d18908927af))
|
||||
* **v-model:** avoid overwriting number input with same value ([#7004](https://github.com/vuejs/core/issues/7004)) ([40f4b77](https://github.com/vuejs/core/commit/40f4b77bb570868cb6e47791078767797e465989)), closes [#7003](https://github.com/vuejs/core/issues/7003)
|
||||
* **v-model:** unnecessary value binding error should apply to dynamic instead of static binding ([2859b65](https://github.com/vuejs/core/commit/2859b653c9a22460e60233cac10fe139e359b046)), closes [#3596](https://github.com/vuejs/core/issues/3596)
|
||||
|
||||
|
||||
|
||||
## [3.3.8](https://github.com/vuejs/core/compare/v3.3.7...v3.3.8) (2023-11-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compile-sfc:** support `Error` type in `defineProps` ([#5955](https://github.com/vuejs/core/issues/5955)) ([a989345](https://github.com/vuejs/core/commit/a9893458ec519aae442e1b99e64e6d74685cd22c))
|
||||
* **compiler-core:** known global should be shadowed by local variables in expression rewrite ([#9492](https://github.com/vuejs/core/issues/9492)) ([a75d1c5](https://github.com/vuejs/core/commit/a75d1c5c6242e91a73cc5ba01e6da620dea0b3d9)), closes [#9482](https://github.com/vuejs/core/issues/9482)
|
||||
* **compiler-sfc:** fix dynamic directive arguments usage check for slots ([#9495](https://github.com/vuejs/core/issues/9495)) ([b39fa1f](https://github.com/vuejs/core/commit/b39fa1f8157647859331ce439c42ae016a49b415)), closes [#9493](https://github.com/vuejs/core/issues/9493)
|
||||
* **deps:** update dependency @vue/repl to ^2.6.2 ([#9536](https://github.com/vuejs/core/issues/9536)) ([5cef325](https://github.com/vuejs/core/commit/5cef325f41e3b38657c72fa1a38dedeee1c7a60a))
|
||||
* **deps:** update dependency @vue/repl to ^2.6.3 ([#9540](https://github.com/vuejs/core/issues/9540)) ([176d590](https://github.com/vuejs/core/commit/176d59058c9aecffe9da4d4311e98496684f06d4))
|
||||
* **hydration:** fix tagName access error on comment/text node hydration mismatch ([dd8a0cf](https://github.com/vuejs/core/commit/dd8a0cf5dcde13d2cbd899262a0e07f16e14e489)), closes [#9531](https://github.com/vuejs/core/issues/9531)
|
||||
* **types:** avoid exposing lru-cache types in generated dts ([462aeb3](https://github.com/vuejs/core/commit/462aeb3b600765e219ded2ee9a0ed1e74df61de0)), closes [#9521](https://github.com/vuejs/core/issues/9521)
|
||||
* **warn:** avoid warning on empty children with Suspense ([#3962](https://github.com/vuejs/core/issues/3962)) ([405f345](https://github.com/vuejs/core/commit/405f34587a63a5f1e3d147b9848219ea98acc22d))
|
||||
|
||||
|
||||
|
||||
# [3.4.0-alpha.1](https://github.com/vuejs/core/compare/v3.3.7...v3.4.0-alpha.1) (2023-10-28)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import importX from 'eslint-plugin-import-x'
|
||||
import tseslint from 'typescript-eslint'
|
||||
import vitest from 'eslint-plugin-vitest'
|
||||
import vitest from '@vitest/eslint-plugin'
|
||||
import { builtinModules } from 'node:module'
|
||||
|
||||
const DOMGlobals = ['window', 'document']
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[build.environment]
|
||||
NODE_VERSION = "18"
|
||||
NODE_VERSION = "22"
|
||||
NPM_FLAGS = "--version" # prevent Netlify npm install
|
||||
|
|
76
package.json
76
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"version": "3.5.9",
|
||||
"packageManager": "pnpm@9.10.0",
|
||||
"version": "3.5.16",
|
||||
"packageManager": "pnpm@10.11.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev.js",
|
||||
|
@ -22,7 +22,10 @@
|
|||
"test-dts": "run-s build-dts test-dts-only",
|
||||
"test-dts-only": "tsc -p packages-private/dts-built-test/tsconfig.json && tsc -p ./packages-private/dts-test/tsconfig.test.json",
|
||||
"test-coverage": "vitest run --project unit --coverage",
|
||||
"test-bench": "vitest bench",
|
||||
"prebench": "node scripts/build.js -pf esm-browser reactivity",
|
||||
"prebench-compare": "node scripts/build.js -pf esm-browser reactivity",
|
||||
"bench": "vitest bench --project=unit --outputJson=temp/bench.json",
|
||||
"bench-compare": "vitest bench --project=unit --compare=temp/bench.json",
|
||||
"release": "node scripts/release.js",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"dev-esm": "node scripts/dev.js -if esm-bundler-runtime",
|
||||
|
@ -62,62 +65,51 @@
|
|||
"@babel/parser": "catalog:",
|
||||
"@babel/types": "catalog:",
|
||||
"@rollup/plugin-alias": "^5.1.1",
|
||||
"@rollup/plugin-commonjs": "^26.0.3",
|
||||
"@rollup/plugin-commonjs": "^28.0.3",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-replace": "5.0.4",
|
||||
"@swc/core": "^1.7.28",
|
||||
"@swc/core": "^1.11.29",
|
||||
"@types/hash-sum": "^1.0.2",
|
||||
"@types/node": "^20.16.5",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/node": "^22.15.29",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/serve-handler": "^6.1.4",
|
||||
"@vitest/coverage-v8": "^2.1.1",
|
||||
"@vitest/coverage-v8": "^3.1.4",
|
||||
"@vitest/eslint-plugin": "^1.2.1",
|
||||
"@vue/consolidate": "1.0.0",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"enquirer": "^2.4.1",
|
||||
"esbuild": "^0.24.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"eslint": "^9.10.0",
|
||||
"eslint-plugin-import-x": "^4.2.1",
|
||||
"eslint-plugin-vitest": "^0.5.4",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-import-x": "^4.13.1",
|
||||
"estree-walker": "catalog:",
|
||||
"jsdom": "^25.0.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
"jsdom": "^26.1.0",
|
||||
"lint-staged": "^16.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"magic-string": "^0.30.11",
|
||||
"markdown-table": "^3.0.3",
|
||||
"magic-string": "^0.30.17",
|
||||
"markdown-table": "^3.0.4",
|
||||
"marked": "13.0.3",
|
||||
"npm-run-all2": "^6.2.3",
|
||||
"picocolors": "^1.1.0",
|
||||
"prettier": "^3.3.3",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"prettier": "^3.5.3",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"pug": "^3.0.3",
|
||||
"puppeteer": "~23.3.0",
|
||||
"puppeteer": "~24.9.0",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.22.4",
|
||||
"rollup-plugin-dts": "^6.1.1",
|
||||
"rollup-plugin-esbuild": "^6.1.1",
|
||||
"rollup": "^4.41.1",
|
||||
"rollup-plugin-dts": "^6.2.1",
|
||||
"rollup-plugin-esbuild": "^6.2.1",
|
||||
"rollup-plugin-polyfill-node": "^0.13.0",
|
||||
"semver": "^7.6.3",
|
||||
"serve": "^14.2.3",
|
||||
"serve-handler": "^6.1.5",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
"semver": "^7.7.2",
|
||||
"serve": "^14.2.4",
|
||||
"serve-handler": "^6.1.6",
|
||||
"simple-git-hooks": "^2.13.0",
|
||||
"todomvc-app-css": "^2.4.3",
|
||||
"tslib": "^2.7.0",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.5.0",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"vite": "catalog:",
|
||||
"vitest": "^2.1.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"allowedVersions": {
|
||||
"typescript-eslint>eslint": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin>eslint": "^9.0.0",
|
||||
"@typescript-eslint/parser>eslint": "^9.0.0",
|
||||
"@typescript-eslint/type-utils>eslint": "^9.0.0",
|
||||
"@typescript-eslint/utils>eslint": "^9.0.0"
|
||||
}
|
||||
}
|
||||
"vitest": "^3.1.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ app.directive<HTMLElement, string, 'prevent' | 'stop', 'arg1' | 'arg2'>(
|
|||
mounted(el, binding) {
|
||||
expectType<HTMLElement>(el)
|
||||
expectType<string>(binding.value)
|
||||
expectType<{ prevent: boolean; stop: boolean }>(binding.modifiers)
|
||||
expectType<{ prevent?: boolean; stop?: boolean }>(binding.modifiers)
|
||||
expectType<'arg1' | 'arg2'>(binding.arg!)
|
||||
|
||||
// @ts-expect-error not any
|
||||
|
|
|
@ -12,8 +12,11 @@ app.use(PluginWithoutType, 2)
|
|||
app.use(PluginWithoutType, { anything: 'goes' }, true)
|
||||
|
||||
type PluginOptions = {
|
||||
/** option1 */
|
||||
option1?: string
|
||||
/** option2 */
|
||||
option2: number
|
||||
/** option3 */
|
||||
option3: boolean
|
||||
}
|
||||
|
||||
|
@ -25,6 +28,20 @@ const PluginWithObjectOptions = {
|
|||
},
|
||||
}
|
||||
|
||||
const objectPluginOptional = {
|
||||
install(app: App, options?: PluginOptions) {},
|
||||
}
|
||||
app.use(objectPluginOptional)
|
||||
app.use(
|
||||
objectPluginOptional,
|
||||
// Test JSDoc and `go to definition` for options
|
||||
{
|
||||
option1: 'foo',
|
||||
option2: 1,
|
||||
option3: true,
|
||||
},
|
||||
)
|
||||
|
||||
for (const Plugin of [
|
||||
PluginWithObjectOptions,
|
||||
PluginWithObjectOptions.install,
|
||||
|
@ -92,7 +109,27 @@ const PluginTyped: Plugin<PluginOptions> = (app, options) => {}
|
|||
|
||||
// @ts-expect-error: needs options
|
||||
app.use(PluginTyped)
|
||||
app.use(PluginTyped, { option2: 2, option3: true })
|
||||
app.use(
|
||||
PluginTyped,
|
||||
// Test autocomplete for options
|
||||
{
|
||||
option1: '',
|
||||
option2: 2,
|
||||
option3: true,
|
||||
},
|
||||
)
|
||||
|
||||
const functionPluginOptional = (app: App, options?: PluginOptions) => {}
|
||||
app.use(functionPluginOptional)
|
||||
app.use(functionPluginOptional, { option2: 2, option3: true })
|
||||
|
||||
// type optional params
|
||||
const functionPluginOptional2: Plugin<[options?: PluginOptions]> = (
|
||||
app,
|
||||
options,
|
||||
) => {}
|
||||
app.use(functionPluginOptional2)
|
||||
app.use(functionPluginOptional2, { option2: 2, option3: true })
|
||||
|
||||
// vuetify usage
|
||||
const key: string = ''
|
||||
|
|
|
@ -137,3 +137,18 @@ describe('Generic component', () => {
|
|||
expectType<string | number>(comp.msg)
|
||||
expectType<Array<string | number>>(comp.list)
|
||||
})
|
||||
|
||||
// #12751
|
||||
{
|
||||
const Comp = defineComponent({
|
||||
__typeEmits: {} as {
|
||||
'update:visible': [value?: boolean]
|
||||
},
|
||||
})
|
||||
const comp: ComponentInstance<typeof Comp> = {} as any
|
||||
|
||||
expectType<((value?: boolean) => any) | undefined>(comp['onUpdate:visible'])
|
||||
expectType<{ 'onUpdate:visible'?: (value?: boolean) => any }>(comp['$props'])
|
||||
// @ts-expect-error
|
||||
comp['$props']['$props']
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@ import { type IsAny, type IsUnion, describe, expectType } from './utils'
|
|||
describe('with object props', () => {
|
||||
interface ExpectedProps {
|
||||
a?: number | undefined
|
||||
aa: number
|
||||
aaa: number | null
|
||||
aaaa: number | undefined
|
||||
b: string
|
||||
e?: Function
|
||||
h: boolean
|
||||
|
@ -53,6 +56,19 @@ describe('with object props', () => {
|
|||
|
||||
const props = {
|
||||
a: Number,
|
||||
aa: {
|
||||
type: Number as PropType<number | undefined>,
|
||||
default: 1,
|
||||
},
|
||||
aaa: {
|
||||
type: Number as PropType<number | null>,
|
||||
default: 1,
|
||||
},
|
||||
aaaa: {
|
||||
type: Number as PropType<number | undefined>,
|
||||
// `as const` prevents widening to `boolean` (keeps literal `true` type)
|
||||
required: true as const,
|
||||
},
|
||||
// required should make property non-void
|
||||
b: {
|
||||
type: String,
|
||||
|
@ -146,6 +162,13 @@ describe('with object props', () => {
|
|||
setup(props) {
|
||||
// type assertion. See https://github.com/SamVerschueren/tsd
|
||||
expectType<ExpectedProps['a']>(props.a)
|
||||
expectType<ExpectedProps['aa']>(props.aa)
|
||||
expectType<ExpectedProps['aaa']>(props.aaa)
|
||||
|
||||
// @ts-expect-error should included `undefined`
|
||||
expectType<number>(props.aaaa)
|
||||
expectType<ExpectedProps['aaaa']>(props.aaaa)
|
||||
|
||||
expectType<ExpectedProps['b']>(props.b)
|
||||
expectType<ExpectedProps['e']>(props.e)
|
||||
expectType<ExpectedProps['h']>(props.h)
|
||||
|
@ -198,6 +221,8 @@ describe('with object props', () => {
|
|||
render() {
|
||||
const props = this.$props
|
||||
expectType<ExpectedProps['a']>(props.a)
|
||||
expectType<ExpectedProps['aa']>(props.aa)
|
||||
expectType<ExpectedProps['aaa']>(props.aaa)
|
||||
expectType<ExpectedProps['b']>(props.b)
|
||||
expectType<ExpectedProps['e']>(props.e)
|
||||
expectType<ExpectedProps['h']>(props.h)
|
||||
|
@ -225,6 +250,8 @@ describe('with object props', () => {
|
|||
|
||||
// should also expose declared props on `this`
|
||||
expectType<ExpectedProps['a']>(this.a)
|
||||
expectType<ExpectedProps['aa']>(this.aa)
|
||||
expectType<ExpectedProps['aaa']>(this.aaa)
|
||||
expectType<ExpectedProps['b']>(this.b)
|
||||
expectType<ExpectedProps['e']>(this.e)
|
||||
expectType<ExpectedProps['h']>(this.h)
|
||||
|
@ -269,6 +296,7 @@ describe('with object props', () => {
|
|||
expectType<JSX.Element>(
|
||||
<MyComponent
|
||||
a={1}
|
||||
aaaa={1}
|
||||
b="b"
|
||||
bb="bb"
|
||||
e={() => {}}
|
||||
|
@ -295,6 +323,7 @@ describe('with object props', () => {
|
|||
|
||||
expectType<Component>(
|
||||
<MyComponent
|
||||
aaaa={1}
|
||||
b="b"
|
||||
dd={{ n: 1 }}
|
||||
ddd={['ddd']}
|
||||
|
@ -2068,3 +2097,13 @@ expectString(instance.actionText)
|
|||
// public prop on $props should be optional
|
||||
// @ts-expect-error
|
||||
expectString(instance.$props.actionText)
|
||||
|
||||
// #12122
|
||||
defineComponent({
|
||||
props: { foo: String },
|
||||
render() {
|
||||
expectType<{ readonly foo?: string }>(this.$props)
|
||||
// @ts-expect-error
|
||||
expectType<string>(this.$props)
|
||||
},
|
||||
})
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('custom', () => {
|
|||
value: number
|
||||
oldValue: number | null
|
||||
arg?: 'Arg'
|
||||
modifiers: Record<'a' | 'b', boolean>
|
||||
modifiers: Partial<Record<'a' | 'b', boolean>>
|
||||
}>(testDirective<number, 'a' | 'b', 'Arg'>())
|
||||
|
||||
expectType<{
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
type MaybeRefOrGetter,
|
||||
type Ref,
|
||||
type ShallowRef,
|
||||
type TemplateRef,
|
||||
type ToRefs,
|
||||
type WritableComputedRef,
|
||||
computed,
|
||||
|
@ -189,6 +190,24 @@ describe('allow getter and setter types to be unrelated', <T>() => {
|
|||
f.value = ref(1)
|
||||
})
|
||||
|
||||
describe('correctly unwraps nested refs', () => {
|
||||
const obj = {
|
||||
n: 24,
|
||||
ref: ref(24),
|
||||
nestedRef: ref({ n: ref(0) }),
|
||||
}
|
||||
|
||||
const a = ref(obj)
|
||||
expectType<number>(a.value.n)
|
||||
expectType<number>(a.value.ref)
|
||||
expectType<number>(a.value.nestedRef.n)
|
||||
|
||||
const b = reactive({ a })
|
||||
expectType<number>(b.a.n)
|
||||
expectType<number>(b.a.ref)
|
||||
expectType<number>(b.a.nestedRef.n)
|
||||
})
|
||||
|
||||
// computed
|
||||
describe('allow computed getter and setter types to be unrelated', () => {
|
||||
const obj = ref({
|
||||
|
@ -517,7 +536,7 @@ expectType<string>(toValue(unref2))
|
|||
|
||||
// useTemplateRef
|
||||
const tRef = useTemplateRef('foo')
|
||||
expectType<Readonly<ShallowRef<unknown>>>(tRef)
|
||||
expectType<TemplateRef>(tRef)
|
||||
|
||||
const tRef2 = useTemplateRef<HTMLElement>('bar')
|
||||
expectType<Readonly<ShallowRef<HTMLElement | null>>>(tRef2)
|
||||
expectType<TemplateRef<HTMLElement>>(tRef2)
|
||||
|
|
|
@ -240,6 +240,23 @@ describe('withDefaults w/ defineProp type is different from the defaults type',
|
|||
res1.value
|
||||
})
|
||||
|
||||
describe('withDefaults w/ defineProp discriminate union type', () => {
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
{ type: 'button'; buttonType?: 'submit' } | { type: 'link'; href: string }
|
||||
>(),
|
||||
{
|
||||
type: 'button',
|
||||
},
|
||||
)
|
||||
if (props.type === 'button') {
|
||||
expectType<'submit' | undefined>(props.buttonType)
|
||||
}
|
||||
if (props.type === 'link') {
|
||||
expectType<string>(props.href)
|
||||
}
|
||||
})
|
||||
|
||||
describe('defineProps w/ runtime declaration', () => {
|
||||
// runtime declaration
|
||||
const props = defineProps({
|
||||
|
@ -289,6 +306,14 @@ describe('defineEmits w/ type declaration', () => {
|
|||
emit2('baz')
|
||||
})
|
||||
|
||||
describe('defineEmits w/ interface declaration', () => {
|
||||
interface Emits {
|
||||
foo: [value: string]
|
||||
}
|
||||
const emit = defineEmits<Emits>()
|
||||
emit('foo', 'hi')
|
||||
})
|
||||
|
||||
describe('defineEmits w/ alt type declaration', () => {
|
||||
const emit = defineEmits<{
|
||||
foo: [id: string]
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"vite": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/repl": "^4.4.2",
|
||||
"@vue/repl": "^4.5.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"jszip": "^3.10.1",
|
||||
"vue": "workspace:*"
|
||||
|
|
|
@ -123,6 +123,7 @@ onMounted(() => {
|
|||
:prod="productionMode"
|
||||
:ssr="useSSRMode"
|
||||
:autoSave="autoSave"
|
||||
:theme="theme"
|
||||
@toggle-theme="toggleTheme"
|
||||
@toggle-prod="toggleProdMode"
|
||||
@toggle-ssr="toggleSSR"
|
||||
|
@ -164,8 +165,9 @@ onMounted(() => {
|
|||
|
||||
body {
|
||||
font-size: 13px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
margin: 0;
|
||||
--base: #444;
|
||||
--nav-height: 50px;
|
||||
|
|
|
@ -15,6 +15,7 @@ const props = defineProps<{
|
|||
prod: boolean
|
||||
ssr: boolean
|
||||
autoSave: boolean
|
||||
theme: 'dark' | 'light'
|
||||
}>()
|
||||
const emit = defineEmits([
|
||||
'toggle-theme',
|
||||
|
@ -45,6 +46,7 @@ function resetVueVersion() {
|
|||
|
||||
async function copyLink(e: MouseEvent) {
|
||||
if (e.metaKey) {
|
||||
resetVueVersion()
|
||||
// hidden logic for going to local debug from play.vuejs.org
|
||||
window.location.href = 'http://localhost:5173/' + window.location.hash
|
||||
return
|
||||
|
@ -117,7 +119,11 @@ function toggleDark() {
|
|||
>
|
||||
<span>{{ autoSave ? 'AutoSave ON' : 'AutoSave OFF' }}</span>
|
||||
</button>
|
||||
<button title="Toggle dark mode" class="toggle-dark" @click="toggleDark">
|
||||
<button
|
||||
:title="`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`"
|
||||
class="toggle-dark"
|
||||
@click="toggleDark"
|
||||
>
|
||||
<Sun class="light" />
|
||||
<Moon class="dark" />
|
||||
</button>
|
||||
|
|
|
@ -17,7 +17,10 @@ export async function downloadProject(store: ReplStore) {
|
|||
|
||||
// basic structure
|
||||
zip.file('index.html', index)
|
||||
zip.file('package.json', pkg)
|
||||
zip.file(
|
||||
'package.json',
|
||||
pkg.replace(`"vue": "latest"`, `"vue": "${store.vueVersion || 'latest'}"`),
|
||||
)
|
||||
zip.file('vite.config.js', config)
|
||||
zip.file('README.md', readme)
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
"serve": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.4.0"
|
||||
"vue": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"vite": "^5.4.8"
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"enableNonBrowserBranches": true
|
||||
},
|
||||
"dependencies": {
|
||||
"monaco-editor": "^0.51.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
--bg: #1d1f21;
|
||||
--border: #333;
|
||||
}
|
||||
|
|
|
@ -2271,6 +2271,11 @@ describe('compiler: parse', () => {
|
|||
expect(span.loc.start.offset).toBe(0)
|
||||
expect(span.loc.end.offset).toBe(27)
|
||||
})
|
||||
|
||||
test('correct loc when a line in attribute value ends with &', () => {
|
||||
const [span] = baseParse(`<span v-if="foo &&\nbar"></span>`).children
|
||||
expect(span.loc.end.line).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('decodeEntities option', () => {
|
||||
|
|
|
@ -8,7 +8,7 @@ return function render(_ctx, _cache) {
|
|||
const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("div", { key: "foo" }, null, -1 /* HOISTED */)
|
||||
_createElementVNode("div", { key: "foo" }, null, -1 /* CACHED */)
|
||||
])))
|
||||
}
|
||||
}"
|
||||
|
@ -25,11 +25,11 @@ return function render(_ctx, _cache) {
|
|||
_createElementVNode("p", null, [
|
||||
_createElementVNode("span"),
|
||||
_createElementVNode("span")
|
||||
], -1 /* HOISTED */),
|
||||
], -1 /* CACHED */),
|
||||
_createElementVNode("p", null, [
|
||||
_createElementVNode("span"),
|
||||
_createElementVNode("span")
|
||||
], -1 /* HOISTED */)
|
||||
], -1 /* CACHED */)
|
||||
])))
|
||||
}
|
||||
}"
|
||||
|
@ -45,7 +45,7 @@ return function render(_ctx, _cache) {
|
|||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("div", null, [
|
||||
_createCommentVNode("comment")
|
||||
], -1 /* HOISTED */)
|
||||
], -1 /* CACHED */)
|
||||
])))
|
||||
}
|
||||
}"
|
||||
|
@ -59,9 +59,9 @@ return function render(_ctx, _cache) {
|
|||
const { createElementVNode: _createElementVNode, createTextVNode: _createTextVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("span", null, null, -1 /* HOISTED */),
|
||||
_createElementVNode("span", null, null, -1 /* CACHED */),
|
||||
_createTextVNode("foo"),
|
||||
_createElementVNode("div", null, null, -1 /* HOISTED */)
|
||||
_createElementVNode("div", null, null, -1 /* CACHED */)
|
||||
])))
|
||||
}
|
||||
}"
|
||||
|
@ -75,7 +75,7 @@ return function render(_ctx, _cache) {
|
|||
const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("span", { class: "inline" }, "hello", -1 /* HOISTED */)
|
||||
_createElementVNode("span", { class: "inline" }, "hello", -1 /* CACHED */)
|
||||
])))
|
||||
}
|
||||
}"
|
||||
|
@ -148,7 +148,7 @@ return function render(_ctx, _cache) {
|
|||
const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("span", null, "foo " + _toDisplayString(1) + " " + _toDisplayString(true), -1 /* HOISTED */)
|
||||
_createElementVNode("span", null, "foo " + _toDisplayString(1) + " " + _toDisplayString(true), -1 /* CACHED */)
|
||||
])))
|
||||
}
|
||||
}"
|
||||
|
@ -162,7 +162,7 @@ return function render(_ctx, _cache) {
|
|||
const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("span", { foo: 0 }, _toDisplayString(1), -1 /* HOISTED */)
|
||||
_createElementVNode("span", { foo: 0 }, _toDisplayString(1), -1 /* CACHED */)
|
||||
])))
|
||||
}
|
||||
}"
|
||||
|
@ -178,7 +178,7 @@ return function render(_ctx, _cache) {
|
|||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(1, (i) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, [...(_cache[0] || (_cache[0] = [
|
||||
_createElementVNode("span", { class: "hi" }, null, -1 /* HOISTED */)
|
||||
_createElementVNode("span", { class: "hi" }, null, -1 /* CACHED */)
|
||||
]))]))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
]))
|
||||
|
@ -216,7 +216,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_withDirectives((_openBlock(), _createElementBlock("svg", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("path", { d: "M2,3H5.5L12" }, null, -1 /* HOISTED */)
|
||||
_createElementVNode("path", { d: "M2,3H5.5L12" }, null, -1 /* CACHED */)
|
||||
]))), [
|
||||
[_directive_foo]
|
||||
])
|
||||
|
@ -402,7 +402,7 @@ return function render(_ctx, _cache) {
|
|||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
ok
|
||||
? (_openBlock(), _createElementBlock("div", _hoisted_1, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("span", null, null, -1 /* HOISTED */)
|
||||
_createElementVNode("span", null, null, -1 /* CACHED */)
|
||||
])))
|
||||
: _createCommentVNode("v-if", true)
|
||||
]))
|
||||
|
@ -410,6 +410,32 @@ return function render(_ctx, _cache) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: cacheStatic transform > should hoist props for root with single element excluding comments 1`] = `
|
||||
"const _Vue = Vue
|
||||
const { createElementVNode: _createElementVNode, createCommentVNode: _createCommentVNode } = _Vue
|
||||
|
||||
const _hoisted_1 = { id: "a" }
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
with (_ctx) {
|
||||
const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||
_createCommentVNode("comment"),
|
||||
_createElementVNode("div", _hoisted_1, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("div", { id: "b" }, [
|
||||
_createElementVNode("div", { id: "c" }, [
|
||||
_createElementVNode("div", { id: "d" }, [
|
||||
_createElementVNode("div", { id: "e" }, "hello")
|
||||
])
|
||||
])
|
||||
], -1 /* CACHED */)
|
||||
]))
|
||||
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: cacheStatic transform > should hoist v-for children if static 1`] = `
|
||||
"const _Vue = Vue
|
||||
const { createElementVNode: _createElementVNode } = _Vue
|
||||
|
@ -423,7 +449,7 @@ return function render(_ctx, _cache) {
|
|||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (i) => {
|
||||
return (_openBlock(), _createElementBlock("div", _hoisted_1, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("span", null, null, -1 /* HOISTED */)
|
||||
_createElementVNode("span", null, null, -1 /* CACHED */)
|
||||
])))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
]))
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`compiler: v-memo transform > element v-for key expression prefixing + v-memo 1`] = `
|
||||
"import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, isMemoSame as _isMemoSame, withMemo as _withMemo } from "vue"
|
||||
|
||||
export function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.tableData, (data, __, ___, _cached) => {
|
||||
const _memo = (_ctx.getLetter(data))
|
||||
if (_cached && _cached.key === _ctx.getId(data) && _isMemoSame(_cached, _memo)) return _cached
|
||||
const _item = (_openBlock(), _createElementBlock("span", {
|
||||
key: _ctx.getId(data)
|
||||
}))
|
||||
_item.memo = _memo
|
||||
return _item
|
||||
}, _cache, 0), 128 /* KEYED_FRAGMENT */))
|
||||
]))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: v-memo transform > on component 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createVNode as _createVNode, withMemo as _withMemo, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ return function render(_ctx, _cache) {
|
|||
const { setBlockTracking: _setBlockTracking, createElementVNode: _createElementVNode } = _Vue
|
||||
|
||||
return _cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -28,7 +28,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createVNode(_component_Comp, { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -47,7 +47,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -66,7 +66,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _renderSlot($slots, "default")).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -85,7 +85,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createElementVNode("div")).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
|
|
@ -246,6 +246,28 @@ return function render(_ctx, _cache) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: transform component slots > with whitespace: 'preserve' > named slot with v-if + v-else 1`] = `
|
||||
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
|
||||
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 /* DYNAMIC */ }, [
|
||||
ok
|
||||
? {
|
||||
name: "one",
|
||||
fn: _withCtx(() => ["foo"]),
|
||||
key: "0"
|
||||
}
|
||||
: {
|
||||
name: "two",
|
||||
fn: _withCtx(() => ["baz"]),
|
||||
key: "1"
|
||||
}
|
||||
]), 1024 /* DYNAMIC_SLOTS */))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: transform component slots > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = `
|
||||
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||
|
||||
|
|
|
@ -170,6 +170,11 @@ describe('compiler: cacheStatic transform', () => {
|
|||
{
|
||||
/* _ slot flag */
|
||||
},
|
||||
{
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: { content: '__' },
|
||||
value: { content: '[0]' },
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
@ -197,6 +202,11 @@ describe('compiler: cacheStatic transform', () => {
|
|||
{
|
||||
/* _ slot flag */
|
||||
},
|
||||
{
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: { content: '__' },
|
||||
value: { content: '[0]' },
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
@ -533,6 +543,32 @@ describe('compiler: cacheStatic transform', () => {
|
|||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should hoist props for root with single element excluding comments', () => {
|
||||
// deeply nested div to trigger stringification condition
|
||||
const root = transformWithCache(
|
||||
`<!--comment--><div id="a"><div id="b"><div id="c"><div id="d"><div id="e">hello</div></div></div></div></div>`,
|
||||
)
|
||||
expect(root.cached.length).toBe(1)
|
||||
expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'a' })])
|
||||
|
||||
expect((root.codegenNode as VNodeCall).children).toMatchObject([
|
||||
{
|
||||
type: NodeTypes.COMMENT,
|
||||
content: 'comment',
|
||||
},
|
||||
{
|
||||
type: NodeTypes.ELEMENT,
|
||||
codegenNode: {
|
||||
type: NodeTypes.VNODE_CALL,
|
||||
tag: `"div"`,
|
||||
props: { content: `_hoisted_1` },
|
||||
children: { type: NodeTypes.JS_CACHE_EXPRESSION },
|
||||
},
|
||||
},
|
||||
])
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
describe('prefixIdentifiers', () => {
|
||||
test('cache nested static tree with static interpolation', () => {
|
||||
const root = transformWithCache(
|
||||
|
|
|
@ -53,4 +53,12 @@ describe('compiler: v-memo transform', () => {
|
|||
),
|
||||
).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('element v-for key expression prefixing + v-memo', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<span v-for="data of tableData" :key="getId(data)" v-memo="getLetter(data)"></span>`,
|
||||
),
|
||||
).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -988,5 +988,19 @@ describe('compiler: transform component slots', () => {
|
|||
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('named slot with v-if + v-else', () => {
|
||||
const source = `
|
||||
<Comp>
|
||||
<template #one v-if="ok">foo</template>
|
||||
<template #two v-else>baz</template>
|
||||
</Comp>
|
||||
`
|
||||
const { root } = parseWithSlots(source, {
|
||||
whitespace: 'preserve',
|
||||
})
|
||||
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-core",
|
||||
"version": "3.5.9",
|
||||
"version": "3.5.16",
|
||||
"description": "@vue/compiler-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-core.esm-bundler.js",
|
||||
|
|
|
@ -418,6 +418,7 @@ export interface CacheExpression extends Node {
|
|||
index: number
|
||||
value: JSChildNode
|
||||
needPauseTracking: boolean
|
||||
inVOnce: boolean
|
||||
needArraySpread: boolean
|
||||
}
|
||||
|
||||
|
@ -774,12 +775,14 @@ export function createCacheExpression(
|
|||
index: number,
|
||||
value: JSChildNode,
|
||||
needPauseTracking: boolean = false,
|
||||
inVOnce: boolean = false,
|
||||
): CacheExpression {
|
||||
return {
|
||||
type: NodeTypes.JS_CACHE_EXPRESSION,
|
||||
index,
|
||||
value,
|
||||
needPauseTracking: needPauseTracking,
|
||||
inVOnce,
|
||||
needArraySpread: false,
|
||||
loc: locStub,
|
||||
}
|
||||
|
|
|
@ -188,7 +188,9 @@ function createCodegenContext(
|
|||
name = content
|
||||
}
|
||||
}
|
||||
addMapping(node.loc.start, name)
|
||||
if (node.loc.source) {
|
||||
addMapping(node.loc.start, name)
|
||||
}
|
||||
}
|
||||
if (newlineIndex === NewlineType.Unknown) {
|
||||
// multiple newlines, full iteration
|
||||
|
@ -225,7 +227,7 @@ function createCodegenContext(
|
|||
context.column = code.length - newlineIndex
|
||||
}
|
||||
}
|
||||
if (node && node.loc !== locStub) {
|
||||
if (node && node.loc !== locStub && node.loc.source) {
|
||||
addMapping(node.loc.end)
|
||||
}
|
||||
}
|
||||
|
@ -1017,7 +1019,9 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) {
|
|||
push(`_cache[${node.index}] || (`)
|
||||
if (needPauseTracking) {
|
||||
indent()
|
||||
push(`${helper(SET_BLOCK_TRACKING)}(-1),`)
|
||||
push(`${helper(SET_BLOCK_TRACKING)}(-1`)
|
||||
if (node.inVOnce) push(`, true`)
|
||||
push(`),`)
|
||||
newline()
|
||||
push(`(`)
|
||||
}
|
||||
|
|
|
@ -388,7 +388,7 @@ const tokenizer = new Tokenizer(stack, {
|
|||
CompilerDeprecationTypes.COMPILER_V_BIND_SYNC,
|
||||
currentOptions,
|
||||
currentProp.loc,
|
||||
currentProp.rawName,
|
||||
currentProp.arg!.loc.source,
|
||||
)
|
||||
) {
|
||||
currentProp.name = 'model'
|
||||
|
@ -647,7 +647,7 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) {
|
|||
|
||||
// whitespace management
|
||||
if (!tokenizer.inRCDATA) {
|
||||
el.children = condenseWhitespace(children, tag)
|
||||
el.children = condenseWhitespace(children)
|
||||
}
|
||||
|
||||
if (ns === Namespaces.HTML && currentOptions.isIgnoreNewlineTag(tag)) {
|
||||
|
@ -832,10 +832,7 @@ function isUpperCase(c: number) {
|
|||
}
|
||||
|
||||
const windowsNewlineRE = /\r\n/g
|
||||
function condenseWhitespace(
|
||||
nodes: TemplateChildNode[],
|
||||
tag?: string,
|
||||
): TemplateChildNode[] {
|
||||
function condenseWhitespace(nodes: TemplateChildNode[]): TemplateChildNode[] {
|
||||
const shouldCondense = currentOptions.whitespace !== 'preserve'
|
||||
let removedWhitespace = false
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
|
@ -933,6 +930,10 @@ function getLoc(start: number, end?: number): SourceLocation {
|
|||
}
|
||||
}
|
||||
|
||||
export function cloneLoc(loc: SourceLocation): SourceLocation {
|
||||
return getLoc(loc.start.offset, loc.end.offset)
|
||||
}
|
||||
|
||||
function setLocEnd(loc: SourceLocation, end: number) {
|
||||
loc.end = tokenizer.getPos(end)
|
||||
loc.source = getSlice(loc.start.offset, end)
|
||||
|
|
|
@ -929,7 +929,7 @@ export default class Tokenizer {
|
|||
this.buffer = input
|
||||
while (this.index < this.buffer.length) {
|
||||
const c = this.buffer.charCodeAt(this.index)
|
||||
if (c === CharCodes.NewLine) {
|
||||
if (c === CharCodes.NewLine && this.state !== State.InEntity) {
|
||||
this.newlines.push(this.index)
|
||||
}
|
||||
switch (this.state) {
|
||||
|
|
|
@ -37,7 +37,7 @@ import {
|
|||
helperNameMap,
|
||||
} from './runtimeHelpers'
|
||||
import { isVSlot } from './utils'
|
||||
import { cacheStatic, isSingleElementRoot } from './transforms/cacheStatic'
|
||||
import { cacheStatic, getSingleElementRoot } from './transforms/cacheStatic'
|
||||
import type { CompilerCompatOptions } from './compat/compatConfig'
|
||||
|
||||
// There are two types of transforms:
|
||||
|
@ -116,7 +116,7 @@ export interface TransformContext
|
|||
addIdentifiers(exp: ExpressionNode | string): void
|
||||
removeIdentifiers(exp: ExpressionNode | string): void
|
||||
hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
|
||||
cache(exp: JSChildNode, isVNode?: boolean): CacheExpression
|
||||
cache(exp: JSChildNode, isVNode?: boolean, inVOnce?: boolean): CacheExpression
|
||||
constantCache: WeakMap<TemplateChildNode, ConstantTypes>
|
||||
|
||||
// 2.x Compat only
|
||||
|
@ -297,11 +297,12 @@ export function createTransformContext(
|
|||
identifier.hoisted = exp
|
||||
return identifier
|
||||
},
|
||||
cache(exp, isVNode = false) {
|
||||
cache(exp, isVNode = false, inVOnce = false) {
|
||||
const cacheExp = createCacheExpression(
|
||||
context.cached.length,
|
||||
exp,
|
||||
isVNode,
|
||||
inVOnce,
|
||||
)
|
||||
context.cached.push(cacheExp)
|
||||
return cacheExp
|
||||
|
@ -355,12 +356,12 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
|
|||
const { helper } = context
|
||||
const { children } = root
|
||||
if (children.length === 1) {
|
||||
const child = children[0]
|
||||
const singleElementRootChild = getSingleElementRoot(root)
|
||||
// if the single child is an element, turn it into a block.
|
||||
if (isSingleElementRoot(root, child) && child.codegenNode) {
|
||||
if (singleElementRootChild && singleElementRootChild.codegenNode) {
|
||||
// single element root is never hoisted so codegenNode will never be
|
||||
// SimpleExpressionNode
|
||||
const codegenNode = child.codegenNode
|
||||
const codegenNode = singleElementRootChild.codegenNode
|
||||
if (codegenNode.type === NodeTypes.VNODE_CALL) {
|
||||
convertToBlock(codegenNode, context)
|
||||
}
|
||||
|
@ -369,7 +370,7 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
|
|||
// - single <slot/>, IfNode, ForNode: already blocks.
|
||||
// - single text node: always patched.
|
||||
// root codegen falls through via genNode()
|
||||
root.codegenNode = child
|
||||
root.codegenNode = children[0]
|
||||
}
|
||||
} else if (children.length > 1) {
|
||||
// root has multiple nodes - return a fragment block.
|
||||
|
|
|
@ -12,11 +12,14 @@ import {
|
|||
type RootNode,
|
||||
type SimpleExpressionNode,
|
||||
type SlotFunctionExpression,
|
||||
type SlotsObjectProperty,
|
||||
type TemplateChildNode,
|
||||
type TemplateNode,
|
||||
type TextCallNode,
|
||||
type VNodeCall,
|
||||
createArrayExpression,
|
||||
createObjectProperty,
|
||||
createSimpleExpression,
|
||||
getVNodeBlockHelper,
|
||||
getVNodeHelper,
|
||||
} from '../ast'
|
||||
|
@ -38,20 +41,19 @@ export function cacheStatic(root: RootNode, context: TransformContext): void {
|
|||
context,
|
||||
// Root node is unfortunately non-hoistable due to potential parent
|
||||
// fallthrough attributes.
|
||||
isSingleElementRoot(root, root.children[0]),
|
||||
!!getSingleElementRoot(root),
|
||||
)
|
||||
}
|
||||
|
||||
export function isSingleElementRoot(
|
||||
export function getSingleElementRoot(
|
||||
root: RootNode,
|
||||
child: TemplateChildNode,
|
||||
): child is PlainElementNode | ComponentNode | TemplateNode {
|
||||
const { children } = root
|
||||
return (
|
||||
children.length === 1 &&
|
||||
child.type === NodeTypes.ELEMENT &&
|
||||
!isSlotOutlet(child)
|
||||
)
|
||||
): PlainElementNode | ComponentNode | TemplateNode | null {
|
||||
const children = root.children.filter(x => x.type !== NodeTypes.COMMENT)
|
||||
return children.length === 1 &&
|
||||
children[0].type === NodeTypes.ELEMENT &&
|
||||
!isSlotOutlet(children[0])
|
||||
? children[0]
|
||||
: null
|
||||
}
|
||||
|
||||
function walk(
|
||||
|
@ -140,6 +142,7 @@ function walk(
|
|||
}
|
||||
|
||||
let cachedAsArray = false
|
||||
const slotCacheKeys = []
|
||||
if (toCache.length === children.length && node.type === NodeTypes.ELEMENT) {
|
||||
if (
|
||||
node.tagType === ElementTypes.ELEMENT &&
|
||||
|
@ -163,6 +166,7 @@ function walk(
|
|||
// default slot
|
||||
const slot = getSlotNode(node.codegenNode, 'default')
|
||||
if (slot) {
|
||||
slotCacheKeys.push(context.cached.length)
|
||||
slot.returns = getCacheExpression(
|
||||
createArrayExpression(slot.returns as TemplateChildNode[]),
|
||||
)
|
||||
|
@ -186,6 +190,7 @@ function walk(
|
|||
slotName.arg &&
|
||||
getSlotNode(parent.codegenNode, slotName.arg)
|
||||
if (slot) {
|
||||
slotCacheKeys.push(context.cached.length)
|
||||
slot.returns = getCacheExpression(
|
||||
createArrayExpression(slot.returns as TemplateChildNode[]),
|
||||
)
|
||||
|
@ -196,10 +201,31 @@ function walk(
|
|||
|
||||
if (!cachedAsArray) {
|
||||
for (const child of toCache) {
|
||||
slotCacheKeys.push(context.cached.length)
|
||||
child.codegenNode = context.cache(child.codegenNode!)
|
||||
}
|
||||
}
|
||||
|
||||
// put the slot cached keys on the slot object, so that the cache
|
||||
// can be removed when component unmounting to prevent memory leaks
|
||||
if (
|
||||
slotCacheKeys.length &&
|
||||
node.type === NodeTypes.ELEMENT &&
|
||||
node.tagType === ElementTypes.COMPONENT &&
|
||||
node.codegenNode &&
|
||||
node.codegenNode.type === NodeTypes.VNODE_CALL &&
|
||||
node.codegenNode.children &&
|
||||
!isArray(node.codegenNode.children) &&
|
||||
node.codegenNode.children.type === NodeTypes.JS_OBJECT_EXPRESSION
|
||||
) {
|
||||
node.codegenNode.children.properties.push(
|
||||
createObjectProperty(
|
||||
`__`,
|
||||
createSimpleExpression(JSON.stringify(slotCacheKeys), false),
|
||||
) as SlotsObjectProperty,
|
||||
)
|
||||
}
|
||||
|
||||
function getCacheExpression(value: JSChildNode): CacheExpression {
|
||||
const exp = context.cache(value)
|
||||
// #6978, #7138, #7114
|
||||
|
|
|
@ -594,11 +594,9 @@ export function buildProps(
|
|||
hasDynamicKeys = true
|
||||
if (exp) {
|
||||
if (isVBind) {
|
||||
// #10696 in case a v-bind object contains ref
|
||||
pushRefVForMarker()
|
||||
// have to merge early for compat build check
|
||||
pushMergeArg()
|
||||
if (__COMPAT__) {
|
||||
// have to merge early for compat build check
|
||||
pushMergeArg()
|
||||
// 2.x v-bind object order compat
|
||||
if (__DEV__) {
|
||||
const hasOverridableKeys = mergeArgs.some(arg => {
|
||||
|
@ -641,6 +639,9 @@ export function buildProps(
|
|||
}
|
||||
}
|
||||
|
||||
// #10696 in case a v-bind object contains ref
|
||||
pushRefVForMarker()
|
||||
pushMergeArg()
|
||||
mergeArgs.push(exp)
|
||||
} else {
|
||||
// v-on="obj" -> toHandlers(obj)
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
isStaticPropertyKey,
|
||||
walkIdentifiers,
|
||||
} from '../babelUtils'
|
||||
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
|
||||
import { advancePositionWithClone, findDir, isSimpleIdentifier } from '../utils'
|
||||
import {
|
||||
genPropsAccessExp,
|
||||
hasOwn,
|
||||
|
@ -54,6 +54,7 @@ export const transformExpression: NodeTransform = (node, context) => {
|
|||
)
|
||||
} else if (node.type === NodeTypes.ELEMENT) {
|
||||
// handle directives on element
|
||||
const memo = findDir(node, 'memo')
|
||||
for (let i = 0; i < node.props.length; i++) {
|
||||
const dir = node.props[i]
|
||||
// do not process for v-on & v-for since they are special handled
|
||||
|
@ -65,7 +66,14 @@ export const transformExpression: NodeTransform = (node, context) => {
|
|||
if (
|
||||
exp &&
|
||||
exp.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||
!(dir.name === 'on' && arg)
|
||||
!(dir.name === 'on' && arg) &&
|
||||
// key has been processed in transformFor(vMemo + vFor)
|
||||
!(
|
||||
memo &&
|
||||
arg &&
|
||||
arg.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||
arg.content === 'key'
|
||||
)
|
||||
) {
|
||||
dir.exp = processExpression(
|
||||
exp,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { camelize } from '@vue/shared'
|
|||
import { CAMELIZE } from '../runtimeHelpers'
|
||||
import { processExpression } from './transformExpression'
|
||||
|
||||
// v-bind without arg is handled directly in ./transformElements.ts due to it affecting
|
||||
// v-bind without arg is handled directly in ./transformElement.ts due to its affecting
|
||||
// codegen for the entire props object. This transform here is only for v-bind
|
||||
// *with* args.
|
||||
export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
||||
|
|
|
@ -63,17 +63,27 @@ export const transformFor: NodeTransform = createStructuralDirectiveTransform(
|
|||
const isTemplate = isTemplateNode(node)
|
||||
const memo = findDir(node, 'memo')
|
||||
const keyProp = findProp(node, `key`, false, true)
|
||||
if (keyProp && keyProp.type === NodeTypes.DIRECTIVE && !keyProp.exp) {
|
||||
const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE
|
||||
if (isDirKey && !keyProp.exp) {
|
||||
// resolve :key shorthand #10882
|
||||
transformBindShorthand(keyProp, context)
|
||||
}
|
||||
const keyExp =
|
||||
let keyExp =
|
||||
keyProp &&
|
||||
(keyProp.type === NodeTypes.ATTRIBUTE
|
||||
? keyProp.value
|
||||
? createSimpleExpression(keyProp.value.content, true)
|
||||
: undefined
|
||||
: keyProp.exp)
|
||||
|
||||
if (memo && keyExp && isDirKey) {
|
||||
if (!__BROWSER__) {
|
||||
keyProp.exp = keyExp = processExpression(
|
||||
keyExp as SimpleExpressionNode,
|
||||
context,
|
||||
)
|
||||
}
|
||||
}
|
||||
const keyProperty =
|
||||
keyProp && keyExp ? createObjectProperty(`key`, keyExp) : null
|
||||
|
||||
|
@ -253,7 +263,7 @@ export function processFor(
|
|||
dir: DirectiveNode,
|
||||
context: TransformContext,
|
||||
processCodegen?: (forNode: ForNode) => (() => void) | undefined,
|
||||
) {
|
||||
): (() => void) | undefined {
|
||||
if (!dir.exp) {
|
||||
context.onError(
|
||||
createCompilerError(ErrorCodes.X_V_FOR_NO_EXPRESSION, dir.loc),
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
import { ErrorCodes, createCompilerError } from '../errors'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { validateBrowserExpression } from '../validateExpression'
|
||||
import { cloneLoc } from '../parser'
|
||||
import { CREATE_COMMENT, FRAGMENT } from '../runtimeHelpers'
|
||||
import { findDir, findProp, getMemoedVNodeCall, injectProp } from '../utils'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
|
@ -110,7 +111,7 @@ export function processIf(
|
|||
const branch = createIfBranch(node, dir)
|
||||
const ifNode: IfNode = {
|
||||
type: NodeTypes.IF,
|
||||
loc: node.loc,
|
||||
loc: cloneLoc(node.loc),
|
||||
branches: [branch],
|
||||
}
|
||||
context.replaceNode(ifNode)
|
||||
|
|
|
@ -17,7 +17,7 @@ import { hasScopeRef, isFnExpression, isMemberExpression } from '../utils'
|
|||
import { TO_HANDLER_KEY } from '../runtimeHelpers'
|
||||
|
||||
export interface VOnDirectiveNode extends DirectiveNode {
|
||||
// v-on without arg is handled directly in ./transformElements.ts due to it affecting
|
||||
// v-on without arg is handled directly in ./transformElement.ts due to its affecting
|
||||
// codegen for the entire props object. This transform here is only for v-on
|
||||
// *with* args.
|
||||
arg: ExpressionNode
|
||||
|
|
|
@ -17,7 +17,11 @@ export const transformOnce: NodeTransform = (node, context) => {
|
|||
context.inVOnce = false
|
||||
const cur = context.currentNode as ElementNode | IfNode | ForNode
|
||||
if (cur.codegenNode) {
|
||||
cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)
|
||||
cur.codegenNode = context.cache(
|
||||
cur.codegenNode,
|
||||
true /* isVNode */,
|
||||
true /* inVOnce */,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ export function buildSlots(
|
|||
let prev
|
||||
while (j--) {
|
||||
prev = children[j]
|
||||
if (prev.type !== NodeTypes.COMMENT) {
|
||||
if (prev.type !== NodeTypes.COMMENT && isNonWhitespaceContent(prev)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -342,7 +342,6 @@ export function buildSlots(
|
|||
: hasForwardedSlots(node.children)
|
||||
? SlotFlags.FORWARDED
|
||||
: SlotFlags.STABLE
|
||||
|
||||
let slots = createObjectExpression(
|
||||
slotsProperties.concat(
|
||||
createObjectProperty(
|
||||
|
|
|
@ -6,7 +6,7 @@ exports[`stringify static html > eligible content (elements > 20) + non-eligible
|
|||
return function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createStaticVNode("<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>", 20),
|
||||
_createElementVNode("div", { key: "1" }, "1", -1 /* HOISTED */),
|
||||
_createElementVNode("div", { key: "1" }, "1", -1 /* CACHED */),
|
||||
_createStaticVNode("<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>", 20)
|
||||
])))
|
||||
}"
|
||||
|
@ -32,6 +32,33 @@ return function render(_ctx, _cache) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > serializing template string style 1`] = `
|
||||
"const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createStaticVNode("<div style=\\"color:red;\\"><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span></div>", 1)
|
||||
])))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > should bail for <option> elements with null values 1`] = `
|
||||
"const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("select", null, [
|
||||
_createElementVNode("option", { value: null }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" })
|
||||
], -1 /* CACHED */)
|
||||
])))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > should bail for <option> elements with number values 1`] = `
|
||||
"const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
|
@ -43,11 +70,27 @@ return function render(_ctx, _cache) {
|
|||
_createElementVNode("option", { value: 1 }),
|
||||
_createElementVNode("option", { value: 1 }),
|
||||
_createElementVNode("option", { value: 1 })
|
||||
], -1 /* HOISTED */)
|
||||
], -1 /* CACHED */)
|
||||
])))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > should bail for comments 1`] = `
|
||||
"const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
const _hoisted_1 = { class: "a" }
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||
_createCommentVNode(" Comment 1 "),
|
||||
_createElementVNode("div", _hoisted_1, [
|
||||
_createCommentVNode(" Comment 2 "),
|
||||
_cache[0] || (_cache[0] = _createStaticVNode("<span class=\\"b\\"></span><span class=\\"b\\"></span><span class=\\"b\\"></span><span class=\\"b\\"></span><span class=\\"b\\"></span>", 5))
|
||||
])
|
||||
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > should bail on bindings that are cached but not stringifiable 1`] = `
|
||||
"const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
|
@ -60,7 +103,7 @@ return function render(_ctx, _cache) {
|
|||
_createElementVNode("span", { class: "foo" }, "foo"),
|
||||
_createElementVNode("span", { class: "foo" }, "foo"),
|
||||
_createElementVNode("img", { src: _imports_0_ })
|
||||
], -1 /* HOISTED */)
|
||||
], -1 /* CACHED */)
|
||||
])))
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -162,6 +162,27 @@ describe('stringify static html', () => {
|
|||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
// #12391
|
||||
test('serializing template string style', () => {
|
||||
const { ast, code } = compileWithStringify(
|
||||
`<div><div :style="\`color:red;\`">${repeat(
|
||||
`<span :class="[{ foo: true }, { bar: true }]">{{ 1 }} + {{ false }}</span>`,
|
||||
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT,
|
||||
)}</div></div>`,
|
||||
)
|
||||
// should be optimized now
|
||||
expect(ast.cached).toMatchObject([
|
||||
cachedArrayStaticNodeMatcher(
|
||||
`<div style="color:red;">${repeat(
|
||||
`<span class="foo bar">1 + false</span>`,
|
||||
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT,
|
||||
)}</div>`,
|
||||
1,
|
||||
),
|
||||
])
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('escape', () => {
|
||||
const { ast, code } = compileWithStringify(
|
||||
`<div><div>${repeat(
|
||||
|
@ -470,6 +491,27 @@ describe('stringify static html', () => {
|
|||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should bail for comments', () => {
|
||||
const { code } = compileWithStringify(
|
||||
`<!-- Comment 1 --><div class="a"><!-- Comment 2 -->${repeat(
|
||||
`<span class="b"/>`,
|
||||
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT,
|
||||
)}</div>`,
|
||||
)
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should bail for <option> elements with null values', () => {
|
||||
const { ast, code } = compileWithStringify(
|
||||
`<div><select><option :value="null" />${repeat(
|
||||
`<option value="1" />`,
|
||||
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT,
|
||||
)}</select></div>`,
|
||||
)
|
||||
expect(ast.cached).toMatchObject([cachedArrayBailedMatcher()])
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('eligible content (elements > 20) + non-eligible content', () => {
|
||||
const { code } = compileWithStringify(
|
||||
`<div>${repeat(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { type CompilerError, compile } from '../../src'
|
||||
import { isValidHTMLNesting } from '../../src/htmlNesting'
|
||||
|
||||
describe('validate html nesting', () => {
|
||||
it('should warn with p > div', () => {
|
||||
|
@ -17,4 +18,185 @@ describe('validate html nesting', () => {
|
|||
})
|
||||
expect(err).toBeUndefined()
|
||||
})
|
||||
|
||||
// #13318
|
||||
it('should not warn when parent tag is template', () => {
|
||||
let err: CompilerError | undefined
|
||||
compile(`<template><tr/></template>`, {
|
||||
onWarn: e => (err = e),
|
||||
})
|
||||
expect(err).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Copied from https://github.com/MananTank/validate-html-nesting
|
||||
* with ISC license
|
||||
*/
|
||||
describe('isValidHTMLNesting', () => {
|
||||
test('form', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('form', 'form')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('form', 'div')).toBe(true)
|
||||
expect(isValidHTMLNesting('form', 'input')).toBe(true)
|
||||
expect(isValidHTMLNesting('form', 'select')).toBe(true)
|
||||
expect(isValidHTMLNesting('form', 'button')).toBe(true)
|
||||
expect(isValidHTMLNesting('form', 'label')).toBe(true)
|
||||
expect(isValidHTMLNesting('form', 'h1')).toBe(true)
|
||||
})
|
||||
|
||||
test('p', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('p', 'p')).toBe(false)
|
||||
expect(isValidHTMLNesting('p', 'div')).toBe(false)
|
||||
expect(isValidHTMLNesting('p', 'hr')).toBe(false)
|
||||
expect(isValidHTMLNesting('p', 'blockquote')).toBe(false)
|
||||
expect(isValidHTMLNesting('p', 'pre')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('p', 'a')).toBe(true)
|
||||
expect(isValidHTMLNesting('p', 'span')).toBe(true)
|
||||
expect(isValidHTMLNesting('p', 'abbr')).toBe(true)
|
||||
expect(isValidHTMLNesting('p', 'button')).toBe(true)
|
||||
expect(isValidHTMLNesting('p', 'b')).toBe(true)
|
||||
expect(isValidHTMLNesting('p', 'i')).toBe(true)
|
||||
expect(isValidHTMLNesting('p', 'input')).toBe(true)
|
||||
expect(isValidHTMLNesting('p', 'label')).toBe(true)
|
||||
})
|
||||
|
||||
test('a', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('a', 'a')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('a', 'div')).toBe(true)
|
||||
expect(isValidHTMLNesting('a', 'span')).toBe(true)
|
||||
})
|
||||
|
||||
test('button', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('button', 'button')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('button', 'div')).toBe(true)
|
||||
expect(isValidHTMLNesting('button', 'span')).toBe(true)
|
||||
})
|
||||
|
||||
test('table', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('table', 'tr')).toBe(false)
|
||||
expect(isValidHTMLNesting('table', 'table')).toBe(false)
|
||||
expect(isValidHTMLNesting('table', 'td')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('table', 'thead')).toBe(true)
|
||||
expect(isValidHTMLNesting('table', 'tbody')).toBe(true)
|
||||
expect(isValidHTMLNesting('table', 'tfoot')).toBe(true)
|
||||
expect(isValidHTMLNesting('table', 'caption')).toBe(true)
|
||||
expect(isValidHTMLNesting('table', 'colgroup')).toBe(true)
|
||||
})
|
||||
|
||||
test('td', () => {
|
||||
// valid
|
||||
expect(isValidHTMLNesting('td', 'span')).toBe(true)
|
||||
expect(isValidHTMLNesting('tr', 'td')).toBe(true)
|
||||
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('td', 'td')).toBe(false)
|
||||
expect(isValidHTMLNesting('div', 'td')).toBe(false)
|
||||
})
|
||||
|
||||
test('tbody', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('tbody', 'td')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('tbody', 'tr')).toBe(true)
|
||||
})
|
||||
|
||||
test('tr', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('tr', 'tr')).toBe(false)
|
||||
expect(isValidHTMLNesting('table', 'tr')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('tbody', 'tr')).toBe(true)
|
||||
expect(isValidHTMLNesting('thead', 'tr')).toBe(true)
|
||||
expect(isValidHTMLNesting('tfoot', 'tr')).toBe(true)
|
||||
expect(isValidHTMLNesting('tr', 'td')).toBe(true)
|
||||
expect(isValidHTMLNesting('tr', 'th')).toBe(true)
|
||||
})
|
||||
|
||||
test('li', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('li', 'li')).toBe(false)
|
||||
// valid
|
||||
expect(isValidHTMLNesting('li', 'div')).toBe(true)
|
||||
expect(isValidHTMLNesting('li', 'ul')).toBe(true)
|
||||
})
|
||||
|
||||
test('headings', () => {
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('h1', 'h1')).toBe(false)
|
||||
expect(isValidHTMLNesting('h2', 'h1')).toBe(false)
|
||||
expect(isValidHTMLNesting('h3', 'h1')).toBe(false)
|
||||
expect(isValidHTMLNesting('h1', 'h6')).toBe(false)
|
||||
|
||||
// valid
|
||||
expect(isValidHTMLNesting('h1', 'div')).toBe(true)
|
||||
})
|
||||
|
||||
describe('SVG', () => {
|
||||
test('svg', () => {
|
||||
// invalid non-svg tags as children
|
||||
expect(isValidHTMLNesting('svg', 'div')).toBe(false)
|
||||
expect(isValidHTMLNesting('svg', 'img')).toBe(false)
|
||||
expect(isValidHTMLNesting('svg', 'p')).toBe(false)
|
||||
expect(isValidHTMLNesting('svg', 'h2')).toBe(false)
|
||||
expect(isValidHTMLNesting('svg', 'span')).toBe(false)
|
||||
|
||||
// valid non-svg tags as children
|
||||
expect(isValidHTMLNesting('svg', 'a')).toBe(true)
|
||||
expect(isValidHTMLNesting('svg', 'textarea')).toBe(true)
|
||||
expect(isValidHTMLNesting('svg', 'input')).toBe(true)
|
||||
expect(isValidHTMLNesting('svg', 'select')).toBe(true)
|
||||
|
||||
// valid svg tags as children
|
||||
expect(isValidHTMLNesting('svg', 'g')).toBe(true)
|
||||
expect(isValidHTMLNesting('svg', 'ellipse')).toBe(true)
|
||||
expect(isValidHTMLNesting('svg', 'feOffset')).toBe(true)
|
||||
})
|
||||
|
||||
test('foreignObject', () => {
|
||||
// valid
|
||||
expect(isValidHTMLNesting('foreignObject', 'g')).toBe(true)
|
||||
expect(isValidHTMLNesting('foreignObject', 'div')).toBe(true)
|
||||
expect(isValidHTMLNesting('foreignObject', 'a')).toBe(true)
|
||||
expect(isValidHTMLNesting('foreignObject', 'textarea')).toBe(true)
|
||||
})
|
||||
|
||||
test('g', () => {
|
||||
// valid
|
||||
expect(isValidHTMLNesting('g', 'div')).toBe(true)
|
||||
expect(isValidHTMLNesting('g', 'p')).toBe(true)
|
||||
expect(isValidHTMLNesting('g', 'a')).toBe(true)
|
||||
expect(isValidHTMLNesting('g', 'textarea')).toBe(true)
|
||||
expect(isValidHTMLNesting('g', 'g')).toBe(true)
|
||||
})
|
||||
|
||||
test('dl', () => {
|
||||
// valid
|
||||
expect(isValidHTMLNesting('dl', 'dt')).toBe(true)
|
||||
expect(isValidHTMLNesting('dl', 'dd')).toBe(true)
|
||||
expect(isValidHTMLNesting('dl', 'div')).toBe(true)
|
||||
expect(isValidHTMLNesting('div', 'dt')).toBe(true)
|
||||
expect(isValidHTMLNesting('div', 'dd')).toBe(true)
|
||||
|
||||
// invalid
|
||||
expect(isValidHTMLNesting('span', 'dt')).toBe(false)
|
||||
expect(isValidHTMLNesting('span', 'dd')).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-dom",
|
||||
"version": "3.5.9",
|
||||
"version": "3.5.16",
|
||||
"description": "@vue/compiler-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-dom.esm-bundler.js",
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
* returns true if given parent-child nesting is valid HTML
|
||||
*/
|
||||
export function isValidHTMLNesting(parent: string, child: string): boolean {
|
||||
// if the parent is a template, it can have any child
|
||||
if (parent === 'template') {
|
||||
return true
|
||||
}
|
||||
|
||||
// if we know the list of children that are the only valid children for the given parent
|
||||
if (parent in onlyValidChildren) {
|
||||
return onlyValidChildren[parent].has(child)
|
||||
|
|
|
@ -261,8 +261,7 @@ function analyzeNode(node: StringifiableNode): [number, number] | false {
|
|||
isOptionTag &&
|
||||
isStaticArgOf(p.arg, 'value') &&
|
||||
p.exp &&
|
||||
p.exp.ast &&
|
||||
p.exp.ast.type !== 'StringLiteral'
|
||||
!p.exp.isStatic
|
||||
) {
|
||||
return bail()
|
||||
}
|
||||
|
|
|
@ -861,7 +861,7 @@ export default {
|
|||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||
_createElementVNode("div", null, _toDisplayString(count.value), 1 /* TEXT */),
|
||||
_cache[0] || (_cache[0] = _createElementVNode("div", null, "static", -1 /* HOISTED */))
|
||||
_cache[0] || (_cache[0] = _createElementVNode("div", null, "static", -1 /* CACHED */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@ const _hoisted_1 = _imports_0 + '#fragment'
|
|||
|
||||
export function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||
_cache[0] || (_cache[0] = _createElementVNode("use", { href: _hoisted_1 }, null, -1 /* HOISTED */)),
|
||||
_cache[1] || (_cache[1] = _createElementVNode("use", { href: _hoisted_1 }, null, -1 /* HOISTED */))
|
||||
_cache[0] || (_cache[0] = _createElementVNode("use", { href: _hoisted_1 }, null, -1 /* CACHED */)),
|
||||
_cache[1] || (_cache[1] = _createElementVNode("use", { href: _hoisted_1 }, null, -1 /* CACHED */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -10,8 +10,8 @@ const _hoisted_2 = _imports_0 + ' 1x, ' + "/foo/logo.png" + ' 2x'
|
|||
|
||||
export function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock(_Fragment, null, [
|
||||
_cache[0] || (_cache[0] = _createElementVNode("img", { srcset: _hoisted_1 }, null, -1 /* HOISTED */)),
|
||||
_cache[1] || (_cache[1] = _createElementVNode("img", { srcset: _hoisted_2 }, null, -1 /* HOISTED */))
|
||||
_cache[0] || (_cache[0] = _createElementVNode("img", { srcset: _hoisted_1 }, null, -1 /* CACHED */)),
|
||||
_cache[1] || (_cache[1] = _createElementVNode("img", { srcset: _hoisted_2 }, null, -1 /* CACHED */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
@ -35,51 +35,51 @@ export function render(_ctx, _cache) {
|
|||
_cache[0] || (_cache[0] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: ""
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[1] || (_cache[1] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_1
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[2] || (_cache[2] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_2
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[3] || (_cache[3] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_3
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[4] || (_cache[4] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_4
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[5] || (_cache[5] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_5
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[6] || (_cache[6] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_6
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[7] || (_cache[7] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_7
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[8] || (_cache[8] = _createElementVNode("img", {
|
||||
src: "/logo.png",
|
||||
srcset: "/logo.png, /logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[9] || (_cache[9] = _createElementVNode("img", {
|
||||
src: "https://example.com/logo.png",
|
||||
srcset: "https://example.com/logo.png, https://example.com/logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[10] || (_cache[10] = _createElementVNode("img", {
|
||||
src: "/logo.png",
|
||||
srcset: _hoisted_8
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[11] || (_cache[11] = _createElementVNode("img", {
|
||||
src: "data:image/png;base64,i",
|
||||
srcset: "data:image/png;base64,i 1x, data:image/png;base64,i 2x"
|
||||
}, null, -1 /* HOISTED */))
|
||||
}, null, -1 /* CACHED */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
@ -92,51 +92,51 @@ export function render(_ctx, _cache) {
|
|||
_cache[0] || (_cache[0] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: ""
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[1] || (_cache[1] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: "/foo/logo.png"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[2] || (_cache[2] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: "/foo/logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[3] || (_cache[3] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: "/foo/logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[4] || (_cache[4] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: "/foo/logo.png, /foo/logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[5] || (_cache[5] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: "/foo/logo.png 2x, /foo/logo.png"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[6] || (_cache[6] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: "/foo/logo.png 2x, /foo/logo.png 3x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[7] || (_cache[7] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: "/foo/logo.png, /foo/logo.png 2x, /foo/logo.png 3x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[8] || (_cache[8] = _createElementVNode("img", {
|
||||
src: "/logo.png",
|
||||
srcset: "/logo.png, /logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[9] || (_cache[9] = _createElementVNode("img", {
|
||||
src: "https://example.com/logo.png",
|
||||
srcset: "https://example.com/logo.png, https://example.com/logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[10] || (_cache[10] = _createElementVNode("img", {
|
||||
src: "/logo.png",
|
||||
srcset: "/logo.png, /foo/logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[11] || (_cache[11] = _createElementVNode("img", {
|
||||
src: "data:image/png;base64,i",
|
||||
srcset: "data:image/png;base64,i 1x, data:image/png;base64,i 2x"
|
||||
}, null, -1 /* HOISTED */))
|
||||
}, null, -1 /* CACHED */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
@ -162,51 +162,51 @@ export function render(_ctx, _cache) {
|
|||
_cache[0] || (_cache[0] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: ""
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[1] || (_cache[1] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_1
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[2] || (_cache[2] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_2
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[3] || (_cache[3] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_3
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[4] || (_cache[4] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_4
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[5] || (_cache[5] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_5
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[6] || (_cache[6] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_6
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[7] || (_cache[7] = _createElementVNode("img", {
|
||||
src: "./logo.png",
|
||||
srcset: _hoisted_7
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[8] || (_cache[8] = _createElementVNode("img", {
|
||||
src: "/logo.png",
|
||||
srcset: _hoisted_8
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[9] || (_cache[9] = _createElementVNode("img", {
|
||||
src: "https://example.com/logo.png",
|
||||
srcset: "https://example.com/logo.png, https://example.com/logo.png 2x"
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[10] || (_cache[10] = _createElementVNode("img", {
|
||||
src: "/logo.png",
|
||||
srcset: _hoisted_9
|
||||
}, null, -1 /* HOISTED */)),
|
||||
}, null, -1 /* CACHED */)),
|
||||
_cache[11] || (_cache[11] = _createElementVNode("img", {
|
||||
src: "data:image/png;base64,i",
|
||||
srcset: "data:image/png;base64,i 1x, data:image/png;base64,i 2x"
|
||||
}, null, -1 /* HOISTED */))
|
||||
}, null, -1 /* CACHED */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import { BindingTypes } from '@vue/compiler-core'
|
||||
import { assertCode, compileSFCScript as compile, mockId } from './utils'
|
||||
import {
|
||||
assertCode,
|
||||
compileSFCScript as compile,
|
||||
getPositionInCode,
|
||||
mockId,
|
||||
} from './utils'
|
||||
import { type RawSourceMap, SourceMapConsumer } from 'source-map-js'
|
||||
|
||||
describe('SFC compile <script setup>', () => {
|
||||
test('should compile JS syntax', () => {
|
||||
|
@ -690,6 +696,27 @@ describe('SFC compile <script setup>', () => {
|
|||
expect(content).toMatch(`new (_unref(Foo)).Bar()`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
// #12682
|
||||
test('source map', () => {
|
||||
const source = `
|
||||
<script setup>
|
||||
const count = ref(0)
|
||||
</script>
|
||||
<template>
|
||||
<button @click="throw new Error(\`msg\`);"></button>
|
||||
</template>
|
||||
`
|
||||
const { content, map } = compile(source, { inlineTemplate: true })
|
||||
expect(map).not.toBeUndefined()
|
||||
const consumer = new SourceMapConsumer(map as RawSourceMap)
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(content, 'count')),
|
||||
).toMatchObject(getPositionInCode(source, `count`))
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(content, 'Error')),
|
||||
).toMatchObject(getPositionInCode(source, `Error`))
|
||||
})
|
||||
})
|
||||
|
||||
describe('with TypeScript', () => {
|
||||
|
|
|
@ -148,6 +148,27 @@ export default /*@__PURE__*/_defineComponent({
|
|||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`defineProps > w/ TSTypeAliasDeclaration 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type FunFoo<O> = (item: O) => boolean;
|
||||
type FunBar = FunFoo<number>;
|
||||
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: { type: Function, required: false, default: () => true },
|
||||
bar: { type: Function, required: false, default: () => true }
|
||||
},
|
||||
setup(__props: any, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
|
@ -233,6 +254,33 @@ export default /*@__PURE__*/_defineComponent({
|
|||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`defineProps > w/ extends intersection type 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
interface Props extends Foo {
|
||||
z: number
|
||||
y: string
|
||||
}
|
||||
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
z: { type: Number, required: true },
|
||||
y: { type: String, required: true },
|
||||
x: { type: Number, required: false }
|
||||
},
|
||||
setup(__props: any, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
|
@ -268,6 +316,31 @@ export default /*@__PURE__*/_defineComponent({
|
|||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`defineProps > w/ intersection type 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
type Bar = {
|
||||
y: string;
|
||||
};
|
||||
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
x: { type: Number, required: false },
|
||||
y: { type: String, required: true }
|
||||
},
|
||||
setup(__props: any, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
|
|
|
@ -192,6 +192,25 @@ return () => {}
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`sfc reactive props destructure > handle function parameters with same name as destructured props 1`] = `
|
||||
"
|
||||
export default {
|
||||
setup(__props) {
|
||||
|
||||
|
||||
function test(value) {
|
||||
try {
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
console.log(__props.value)
|
||||
|
||||
return () => {}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`sfc reactive props destructure > multi-variable declaration 1`] = `
|
||||
"
|
||||
export default {
|
||||
|
@ -320,3 +339,22 @@ return { rest }
|
|||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`sfc reactive props destructure > with TSInstantiationExpression 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Foo = <T extends string | number>(data: T) => void
|
||||
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
value: { type: Function }
|
||||
},
|
||||
setup(__props: any) {
|
||||
|
||||
|
||||
const foo = __props.value<123>
|
||||
|
||||
return () => {}
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
|
|
@ -261,6 +261,51 @@ const props = defineProps({ foo: String })
|
|||
})
|
||||
})
|
||||
|
||||
test('w/ extends intersection type', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup lang="ts">
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
interface Props extends Foo {
|
||||
z: number
|
||||
y: string
|
||||
}
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`z: { type: Number, required: true }`)
|
||||
expect(content).toMatch(`y: { type: String, required: true }`)
|
||||
expect(content).toMatch(`x: { type: Number, required: false }`)
|
||||
expect(bindings).toStrictEqual({
|
||||
x: BindingTypes.PROPS,
|
||||
y: BindingTypes.PROPS,
|
||||
z: BindingTypes.PROPS,
|
||||
})
|
||||
})
|
||||
|
||||
test('w/ intersection type', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup lang="ts">
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
type Bar = {
|
||||
y: string;
|
||||
};
|
||||
defineProps<Foo & Bar>()
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`y: { type: String, required: true }`)
|
||||
expect(content).toMatch(`x: { type: Number, required: false }`)
|
||||
expect(bindings).toStrictEqual({
|
||||
x: BindingTypes.PROPS,
|
||||
y: BindingTypes.PROPS,
|
||||
})
|
||||
})
|
||||
|
||||
test('w/ exported interface', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup lang="ts">
|
||||
|
@ -763,4 +808,30 @@ const props = defineProps({ foo: String })
|
|||
expect(content).toMatch(`foo: { default: 5.5, type: Number }`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('w/ TSTypeAliasDeclaration', () => {
|
||||
const { content } = compile(`
|
||||
<script setup lang="ts">
|
||||
type FunFoo<O> = (item: O) => boolean;
|
||||
type FunBar = FunFoo<number>;
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
foo?: FunFoo<number>;
|
||||
bar?: FunBar;
|
||||
}>(),
|
||||
{
|
||||
foo: () => true,
|
||||
bar: () => true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(
|
||||
`foo: { type: Function, required: false, default: () => true }`,
|
||||
)
|
||||
expect(content).toMatch(
|
||||
`bar: { type: Function, required: false, default: () => true }`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -198,6 +198,21 @@ describe('sfc reactive props destructure', () => {
|
|||
}`)
|
||||
})
|
||||
|
||||
test('with TSInstantiationExpression', () => {
|
||||
const { content } = compile(
|
||||
`
|
||||
<script setup lang="ts">
|
||||
type Foo = <T extends string | number>(data: T) => void
|
||||
const { value } = defineProps<{ value: Foo }>()
|
||||
const foo = value<123>
|
||||
</script>
|
||||
`,
|
||||
{ isProd: true },
|
||||
)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`const foo = __props.value<123>`)
|
||||
})
|
||||
|
||||
test('aliasing', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup>
|
||||
|
@ -343,6 +358,22 @@ describe('sfc reactive props destructure', () => {
|
|||
expect(content).toMatch(`props: ['item'],`)
|
||||
})
|
||||
|
||||
test('handle function parameters with same name as destructured props', () => {
|
||||
const { content } = compile(`
|
||||
<script setup>
|
||||
const { value } = defineProps()
|
||||
function test(value) {
|
||||
try {
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
console.log(value)
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`console.log(__props.value)`)
|
||||
})
|
||||
|
||||
test('defineProps/defineEmits in multi-variable declaration (full removal)', () => {
|
||||
const { content } = compile(`
|
||||
<script setup>
|
||||
|
|
|
@ -278,6 +278,23 @@ describe('resolveType', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('utility type: mapped type with Omit and Pick', () => {
|
||||
expect(
|
||||
resolve(`
|
||||
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
|
||||
interface Test {
|
||||
foo: string;
|
||||
bar?: string;
|
||||
}
|
||||
type OptionalTest = Optional<Test, 'foo'>
|
||||
defineProps<OptionalTest>()
|
||||
`).props,
|
||||
).toStrictEqual({
|
||||
foo: ['String'],
|
||||
bar: ['String'],
|
||||
})
|
||||
})
|
||||
|
||||
test('utility type: ReadonlyArray', () => {
|
||||
expect(
|
||||
resolve(`
|
||||
|
@ -1434,6 +1451,29 @@ describe('resolveType', () => {
|
|||
colsLg: ['Number'],
|
||||
})
|
||||
})
|
||||
|
||||
test('allowArbitraryExtensions', () => {
|
||||
const files = {
|
||||
'/foo.d.vue.ts': 'export type Foo = number;',
|
||||
'/foo.vue': '<template><div /></template>',
|
||||
'/bar.d.css.ts': 'export type Bar = string;',
|
||||
'/bar.css': ':root { --color: red; }',
|
||||
}
|
||||
|
||||
const { props } = resolve(
|
||||
`
|
||||
import { Foo } from './foo.vue'
|
||||
import { Bar } from './bar.css'
|
||||
defineProps<{ foo: Foo; bar: Bar }>()
|
||||
`,
|
||||
files,
|
||||
)
|
||||
|
||||
expect(props).toStrictEqual({
|
||||
foo: ['Number'],
|
||||
bar: ['String'],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -39,6 +39,24 @@ describe('SFC scoped CSS', () => {
|
|||
expect(compileScoped(`h1 .foo { color: red; }`)).toMatch(
|
||||
`h1 .foo[data-v-test] { color: red;`,
|
||||
)
|
||||
|
||||
// #13387
|
||||
expect(
|
||||
compileScoped(`main {
|
||||
width: 100%;
|
||||
> * {
|
||||
max-width: 200px;
|
||||
}
|
||||
}`),
|
||||
).toMatchInlineSnapshot(`
|
||||
"main {
|
||||
&[data-v-test] {
|
||||
width: 100%;
|
||||
}
|
||||
> *[data-v-test] {
|
||||
max-width: 200px;
|
||||
}
|
||||
}"`)
|
||||
})
|
||||
|
||||
test('nesting selector', () => {
|
||||
|
@ -211,38 +229,42 @@ color: red
|
|||
expect(
|
||||
compileScoped(`.div { color: red; } .div:where(:hover) { color: blue; }`),
|
||||
).toMatchInlineSnapshot(`
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:where(:hover) { color: blue;
|
||||
}"`)
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:where(:hover) { color: blue;
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileScoped(`.div { color: red; } .div:is(:hover) { color: blue; }`),
|
||||
).toMatchInlineSnapshot(`
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:is(:hover) { color: blue;
|
||||
}"`)
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:is(:hover) { color: blue;
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileScoped(
|
||||
`.div { color: red; } .div:where(.foo:hover) { color: blue; }`,
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:where(.foo:hover) { color: blue;
|
||||
}"`)
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:where(.foo:hover) { color: blue;
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileScoped(
|
||||
`.div { color: red; } .div:is(.foo:hover) { color: blue; }`,
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:is(.foo:hover) { color: blue;
|
||||
}"`)
|
||||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:is(.foo:hover) { color: blue;
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('media query', () => {
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
} from '../src/compileTemplate'
|
||||
import { type SFCTemplateBlock, parse } from '../src/parse'
|
||||
import { compileScript } from '../src'
|
||||
import { getPositionInCode } from './utils'
|
||||
|
||||
function compile(opts: Omit<SFCTemplateCompileOptions, 'id'>) {
|
||||
return compileTemplate({
|
||||
|
@ -157,6 +158,35 @@ test('source map', () => {
|
|||
).toMatchObject(getPositionInCode(template.content, `foobar`))
|
||||
})
|
||||
|
||||
test('source map: v-if generated comment should not have original position', () => {
|
||||
const template = parse(
|
||||
`
|
||||
<template>
|
||||
<div v-if="true"></div>
|
||||
</template>
|
||||
`,
|
||||
{ filename: 'example.vue', sourceMap: true },
|
||||
).descriptor.template!
|
||||
|
||||
const { code, map } = compile({
|
||||
filename: 'example.vue',
|
||||
source: template.content,
|
||||
})
|
||||
|
||||
expect(map!.sources).toEqual([`example.vue`])
|
||||
expect(map!.sourcesContent).toEqual([template.content])
|
||||
|
||||
const consumer = new SourceMapConsumer(map as RawSourceMap)
|
||||
const commentNode = code.match(/_createCommentVNode\("v-if", true\)/)
|
||||
expect(commentNode).not.toBeNull()
|
||||
const commentPosition = getPositionInCode(code, commentNode![0])
|
||||
const originalPosition = consumer.originalPositionFor(commentPosition)
|
||||
// the comment node should not be mapped to the original source
|
||||
expect(originalPosition.column).toBeNull()
|
||||
expect(originalPosition.line).toBeNull()
|
||||
expect(originalPosition.source).toBeNull()
|
||||
})
|
||||
|
||||
test('should work w/ AST from descriptor', () => {
|
||||
const source = `
|
||||
<template>
|
||||
|
@ -482,36 +512,3 @@ test('non-identifier expression in legacy filter syntax', () => {
|
|||
babelParse(compilationResult.code, { sourceType: 'module' })
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
interface Pos {
|
||||
line: number
|
||||
column: number
|
||||
name?: string
|
||||
}
|
||||
|
||||
function getPositionInCode(
|
||||
code: string,
|
||||
token: string,
|
||||
expectName: string | boolean = false,
|
||||
): Pos {
|
||||
const generatedOffset = code.indexOf(token)
|
||||
let line = 1
|
||||
let lastNewLinePos = -1
|
||||
for (let i = 0; i < generatedOffset; i++) {
|
||||
if (code.charCodeAt(i) === 10 /* newline char code */) {
|
||||
line++
|
||||
lastNewLinePos = i
|
||||
}
|
||||
}
|
||||
const res: Pos = {
|
||||
line,
|
||||
column:
|
||||
lastNewLinePos === -1
|
||||
? generatedOffset
|
||||
: generatedOffset - lastNewLinePos - 1,
|
||||
}
|
||||
if (expectName) {
|
||||
res.name = typeof expectName === 'string' ? expectName : token
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ font-weight: bold;
|
|||
|
||||
const consumer = new SourceMapConsumer(script!.map!)
|
||||
consumer.eachMapping(mapping => {
|
||||
expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
|
||||
expect(mapping.originalLine! - mapping.generatedLine).toBe(padding)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -100,8 +100,8 @@ font-weight: bold;
|
|||
|
||||
const consumer = new SourceMapConsumer(template.map!)
|
||||
consumer.eachMapping(mapping => {
|
||||
expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
|
||||
expect(mapping.originalColumn - mapping.generatedColumn).toBe(2)
|
||||
expect(mapping.originalLine! - mapping.generatedLine).toBe(padding)
|
||||
expect(mapping.originalColumn! - mapping.generatedColumn).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -115,7 +115,7 @@ font-weight: bold;
|
|||
|
||||
const consumer = new SourceMapConsumer(custom!.map!)
|
||||
consumer.eachMapping(mapping => {
|
||||
expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
|
||||
expect(mapping.originalLine! - mapping.generatedLine).toBe(padding)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -40,3 +40,36 @@ export function assertCode(code: string): void {
|
|||
}
|
||||
expect(code).toMatchSnapshot()
|
||||
}
|
||||
|
||||
interface Pos {
|
||||
line: number
|
||||
column: number
|
||||
name?: string
|
||||
}
|
||||
|
||||
export function getPositionInCode(
|
||||
code: string,
|
||||
token: string,
|
||||
expectName: string | boolean = false,
|
||||
): Pos {
|
||||
const generatedOffset = code.indexOf(token)
|
||||
let line = 1
|
||||
let lastNewLinePos = -1
|
||||
for (let i = 0; i < generatedOffset; i++) {
|
||||
if (code.charCodeAt(i) === 10 /* newline char code */) {
|
||||
line++
|
||||
lastNewLinePos = i
|
||||
}
|
||||
}
|
||||
const res: Pos = {
|
||||
line,
|
||||
column:
|
||||
lastNewLinePos === -1
|
||||
? generatedOffset
|
||||
: generatedOffset - lastNewLinePos - 1,
|
||||
}
|
||||
if (expectName) {
|
||||
res.name = typeof expectName === 'string' ? expectName : token
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-sfc",
|
||||
"version": "3.5.9",
|
||||
"version": "3.5.16",
|
||||
"description": "@vue/compiler-sfc",
|
||||
"main": "dist/compiler-sfc.cjs.js",
|
||||
"module": "dist/compiler-sfc.esm-browser.js",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"@vue/shared": "workspace:*",
|
||||
"estree-walker": "catalog:",
|
||||
"magic-string": "catalog:",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss": "^8.5.3",
|
||||
"source-map-js": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -58,10 +58,10 @@
|
|||
"hash-sum": "^2.0.0",
|
||||
"lru-cache": "10.1.0",
|
||||
"merge-source-map": "^1.1.0",
|
||||
"minimatch": "~9.0.5",
|
||||
"postcss-modules": "^6.0.0",
|
||||
"postcss-selector-parser": "^6.1.2",
|
||||
"minimatch": "~10.0.1",
|
||||
"postcss-modules": "^6.0.1",
|
||||
"postcss-selector-parser": "^7.1.0",
|
||||
"pug": "^3.0.3",
|
||||
"sass": "^1.78.0"
|
||||
"sass": "^1.89.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@ import type {
|
|||
Statement,
|
||||
} from '@babel/types'
|
||||
import { walk } from 'estree-walker'
|
||||
import type { RawSourceMap } from 'source-map-js'
|
||||
import {
|
||||
type RawSourceMap,
|
||||
SourceMapConsumer,
|
||||
SourceMapGenerator,
|
||||
} from 'source-map-js'
|
||||
import {
|
||||
normalScriptDefaultVar,
|
||||
processNormalScript,
|
||||
|
@ -170,8 +174,6 @@ export function compileScript(
|
|||
const scriptLang = script && script.lang
|
||||
const scriptSetupLang = scriptSetup && scriptSetup.lang
|
||||
|
||||
let refBindings: string[] | undefined
|
||||
|
||||
if (!scriptSetup) {
|
||||
if (!script) {
|
||||
throw new Error(`[@vue/compiler-sfc] SFC contains no <script> tags.`)
|
||||
|
@ -740,12 +742,6 @@ export function compileScript(
|
|||
for (const key in setupBindings) {
|
||||
ctx.bindingMetadata[key] = setupBindings[key]
|
||||
}
|
||||
// known ref bindings
|
||||
if (refBindings) {
|
||||
for (const key of refBindings) {
|
||||
ctx.bindingMetadata[key] = BindingTypes.SETUP_REF
|
||||
}
|
||||
}
|
||||
|
||||
// 7. inject `useCssVars` calls
|
||||
if (
|
||||
|
@ -817,6 +813,7 @@ export function compileScript(
|
|||
args += `, { ${destructureElements.join(', ')} }`
|
||||
}
|
||||
|
||||
let templateMap
|
||||
// 9. generate return statement
|
||||
let returned
|
||||
if (
|
||||
|
@ -866,7 +863,7 @@ export function compileScript(
|
|||
}
|
||||
// inline render function mode - we are going to compile the template and
|
||||
// inline it right here
|
||||
const { code, ast, preamble, tips, errors } = compileTemplate({
|
||||
const { code, ast, preamble, tips, errors, map } = compileTemplate({
|
||||
filename,
|
||||
ast: sfc.template.ast,
|
||||
source: sfc.template.content,
|
||||
|
@ -884,6 +881,7 @@ export function compileScript(
|
|||
bindingMetadata: ctx.bindingMetadata,
|
||||
},
|
||||
})
|
||||
templateMap = map
|
||||
if (tips.length) {
|
||||
tips.forEach(warnOnce)
|
||||
}
|
||||
|
@ -1022,19 +1020,28 @@ export function compileScript(
|
|||
)
|
||||
}
|
||||
|
||||
const content = ctx.s.toString()
|
||||
let map =
|
||||
options.sourceMap !== false
|
||||
? (ctx.s.generateMap({
|
||||
source: filename,
|
||||
hires: true,
|
||||
includeContent: true,
|
||||
}) as unknown as RawSourceMap)
|
||||
: undefined
|
||||
// merge source maps of the script setup and template in inline mode
|
||||
if (templateMap && map) {
|
||||
const offset = content.indexOf(returned)
|
||||
const templateLineOffset =
|
||||
content.slice(0, offset).split(/\r?\n/).length - 1
|
||||
map = mergeSourceMaps(map, templateMap, templateLineOffset)
|
||||
}
|
||||
return {
|
||||
...scriptSetup,
|
||||
bindings: ctx.bindingMetadata,
|
||||
imports: ctx.userImports,
|
||||
content: ctx.s.toString(),
|
||||
map:
|
||||
options.sourceMap !== false
|
||||
? (ctx.s.generateMap({
|
||||
source: filename,
|
||||
hires: true,
|
||||
includeContent: true,
|
||||
}) as unknown as RawSourceMap)
|
||||
: undefined,
|
||||
content,
|
||||
map,
|
||||
scriptAst: scriptAst?.body,
|
||||
scriptSetupAst: scriptSetupAst?.body,
|
||||
deps: ctx.deps ? [...ctx.deps] : undefined,
|
||||
|
@ -1112,6 +1119,7 @@ function walkDeclaration(
|
|||
m === userImportAliases['shallowRef'] ||
|
||||
m === userImportAliases['customRef'] ||
|
||||
m === userImportAliases['toRef'] ||
|
||||
m === userImportAliases['useTemplateRef'] ||
|
||||
m === DEFINE_MODEL,
|
||||
)
|
||||
) {
|
||||
|
@ -1291,3 +1299,42 @@ function isStaticNode(node: Node): boolean {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function mergeSourceMaps(
|
||||
scriptMap: RawSourceMap,
|
||||
templateMap: RawSourceMap,
|
||||
templateLineOffset: number,
|
||||
): RawSourceMap {
|
||||
const generator = new SourceMapGenerator()
|
||||
const addMapping = (map: RawSourceMap, lineOffset = 0) => {
|
||||
const consumer = new SourceMapConsumer(map)
|
||||
;(consumer as any).sources.forEach((sourceFile: string) => {
|
||||
;(generator as any)._sources.add(sourceFile)
|
||||
const sourceContent = consumer.sourceContentFor(sourceFile)
|
||||
if (sourceContent != null) {
|
||||
generator.setSourceContent(sourceFile, sourceContent)
|
||||
}
|
||||
})
|
||||
consumer.eachMapping(m => {
|
||||
if (m.originalLine == null) return
|
||||
generator.addMapping({
|
||||
generated: {
|
||||
line: m.generatedLine + lineOffset,
|
||||
column: m.generatedColumn,
|
||||
},
|
||||
original: {
|
||||
line: m.originalLine,
|
||||
column: m.originalColumn!,
|
||||
},
|
||||
source: m.source,
|
||||
name: m.name,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
addMapping(scriptMap)
|
||||
addMapping(templateMap, templateLineOffset)
|
||||
;(generator as any)._sourceRoot = scriptMap.sourceRoot
|
||||
;(generator as any)._file = scriptMap.file
|
||||
return (generator as any).toJSON()
|
||||
}
|
||||
|
|
|
@ -289,7 +289,7 @@ function mapLines(oldMap: RawSourceMap, newMap: RawSourceMap): RawSourceMap {
|
|||
|
||||
const origPosInOldMap = oldMapConsumer.originalPositionFor({
|
||||
line: m.originalLine,
|
||||
column: m.originalColumn,
|
||||
column: m.originalColumn!,
|
||||
})
|
||||
|
||||
if (origPosInOldMap.source == null) {
|
||||
|
@ -305,7 +305,7 @@ function mapLines(oldMap: RawSourceMap, newMap: RawSourceMap): RawSourceMap {
|
|||
line: origPosInOldMap.line, // map line
|
||||
// use current column, since the oldMap produced by @vue/compiler-sfc
|
||||
// does not
|
||||
column: m.originalColumn,
|
||||
column: m.originalColumn!,
|
||||
},
|
||||
source: origPosInOldMap.source,
|
||||
name: origPosInOldMap.name,
|
||||
|
|
|
@ -39,7 +39,7 @@ export function rewriteDefaultAST(
|
|||
ast.forEach(node => {
|
||||
if (node.type === 'ExportDefaultDeclaration') {
|
||||
if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
||||
let start: number =
|
||||
const start: number =
|
||||
node.declaration.decorators && node.declaration.decorators.length > 0
|
||||
? node.declaration.decorators[
|
||||
node.declaration.decorators.length - 1
|
||||
|
|
|
@ -10,6 +10,7 @@ import type {
|
|||
import { walk } from 'estree-walker'
|
||||
import {
|
||||
BindingTypes,
|
||||
TS_NODE_TYPES,
|
||||
extractIdentifiers,
|
||||
isFunctionType,
|
||||
isInDestructureAssignment,
|
||||
|
@ -240,9 +241,7 @@ export function transformDestructuredProps(
|
|||
if (
|
||||
parent &&
|
||||
parent.type.startsWith('TS') &&
|
||||
parent.type !== 'TSAsExpression' &&
|
||||
parent.type !== 'TSNonNullExpression' &&
|
||||
parent.type !== 'TSTypeAssertion'
|
||||
!TS_NODE_TYPES.includes(parent.type)
|
||||
) {
|
||||
return this.skip()
|
||||
}
|
||||
|
@ -292,7 +291,8 @@ export function transformDestructuredProps(
|
|||
parent && parentStack.pop()
|
||||
if (
|
||||
(node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
|
||||
isFunctionType(node)
|
||||
isFunctionType(node) ||
|
||||
node.type === 'CatchClause'
|
||||
) {
|
||||
popScope()
|
||||
}
|
||||
|
|
|
@ -546,26 +546,43 @@ function resolveStringType(
|
|||
ctx: TypeResolveContext,
|
||||
node: Node,
|
||||
scope: TypeScope,
|
||||
typeParameters?: Record<string, Node>,
|
||||
): string[] {
|
||||
switch (node.type) {
|
||||
case 'StringLiteral':
|
||||
return [node.value]
|
||||
case 'TSLiteralType':
|
||||
return resolveStringType(ctx, node.literal, scope)
|
||||
return resolveStringType(ctx, node.literal, scope, typeParameters)
|
||||
case 'TSUnionType':
|
||||
return node.types.map(t => resolveStringType(ctx, t, scope)).flat()
|
||||
return node.types
|
||||
.map(t => resolveStringType(ctx, t, scope, typeParameters))
|
||||
.flat()
|
||||
case 'TemplateLiteral': {
|
||||
return resolveTemplateKeys(ctx, node, scope)
|
||||
}
|
||||
case 'TSTypeReference': {
|
||||
const resolved = resolveTypeReference(ctx, node, scope)
|
||||
if (resolved) {
|
||||
return resolveStringType(ctx, resolved, scope)
|
||||
return resolveStringType(ctx, resolved, scope, typeParameters)
|
||||
}
|
||||
if (node.typeName.type === 'Identifier') {
|
||||
const name = node.typeName.name
|
||||
if (typeParameters && typeParameters[name]) {
|
||||
return resolveStringType(
|
||||
ctx,
|
||||
typeParameters[name],
|
||||
scope,
|
||||
typeParameters,
|
||||
)
|
||||
}
|
||||
const getParam = (index = 0) =>
|
||||
resolveStringType(ctx, node.typeParameters!.params[index], scope)
|
||||
switch (node.typeName.name) {
|
||||
resolveStringType(
|
||||
ctx,
|
||||
node.typeParameters!.params[index],
|
||||
scope,
|
||||
typeParameters,
|
||||
)
|
||||
switch (name) {
|
||||
case 'Extract':
|
||||
return getParam(1)
|
||||
case 'Exclude': {
|
||||
|
@ -671,6 +688,7 @@ function resolveBuiltin(
|
|||
ctx,
|
||||
node.typeParameters!.params[1],
|
||||
scope,
|
||||
typeParameters,
|
||||
)
|
||||
const res: ResolvedElements = { props: {}, calls: t.calls }
|
||||
for (const key of picked) {
|
||||
|
@ -683,6 +701,7 @@ function resolveBuiltin(
|
|||
ctx,
|
||||
node.typeParameters!.params[1],
|
||||
scope,
|
||||
typeParameters,
|
||||
)
|
||||
const res: ResolvedElements = { props: {}, calls: t.calls }
|
||||
for (const key in t.props) {
|
||||
|
@ -860,13 +879,13 @@ function resolveFS(ctx: TypeResolveContext): FS | undefined {
|
|||
}
|
||||
return (ctx.fs = {
|
||||
fileExists(file) {
|
||||
if (file.endsWith('.vue.ts')) {
|
||||
if (file.endsWith('.vue.ts') && !file.endsWith('.d.vue.ts')) {
|
||||
file = file.replace(/\.ts$/, '')
|
||||
}
|
||||
return fs.fileExists(file)
|
||||
},
|
||||
readFile(file) {
|
||||
if (file.endsWith('.vue.ts')) {
|
||||
if (file.endsWith('.vue.ts') && !file.endsWith('.d.vue.ts')) {
|
||||
file = file.replace(/\.ts$/, '')
|
||||
}
|
||||
return fs.readFile(file)
|
||||
|
@ -1059,7 +1078,7 @@ function resolveWithTS(
|
|||
|
||||
if (res.resolvedModule) {
|
||||
let filename = res.resolvedModule.resolvedFileName
|
||||
if (filename.endsWith('.vue.ts')) {
|
||||
if (filename.endsWith('.vue.ts') && !filename.endsWith('.d.vue.ts')) {
|
||||
filename = filename.replace(/\.ts$/, '')
|
||||
}
|
||||
return fs.realpath ? fs.realpath(filename) : filename
|
||||
|
@ -1129,7 +1148,7 @@ export function fileToScope(
|
|||
// fs should be guaranteed to exist here
|
||||
const fs = resolveFS(ctx)!
|
||||
const source = fs.readFile(filename) || ''
|
||||
const body = parseFile(filename, source, ctx.options.babelParserPlugins)
|
||||
const body = parseFile(filename, source, fs, ctx.options.babelParserPlugins)
|
||||
const scope = new TypeScope(filename, source, 0, recordImports(body))
|
||||
recordTypes(ctx, body, scope, asGlobal)
|
||||
fileToScopeCache.set(filename, scope)
|
||||
|
@ -1139,6 +1158,7 @@ export function fileToScope(
|
|||
function parseFile(
|
||||
filename: string,
|
||||
content: string,
|
||||
fs: FS,
|
||||
parserPlugins?: SFCScriptCompileOptions['babelParserPlugins'],
|
||||
): Statement[] {
|
||||
const ext = extname(filename)
|
||||
|
@ -1151,7 +1171,21 @@ function parseFile(
|
|||
),
|
||||
sourceType: 'module',
|
||||
}).program.body
|
||||
} else if (ext === '.vue') {
|
||||
}
|
||||
|
||||
// simulate `allowArbitraryExtensions` on TypeScript >= 5.0
|
||||
const isUnknownTypeSource = !/\.[cm]?[tj]sx?$/.test(filename)
|
||||
const arbitraryTypeSource = `${filename.slice(0, -ext.length)}.d${ext}.ts`
|
||||
const hasArbitraryTypeDeclaration =
|
||||
isUnknownTypeSource && fs.fileExists(arbitraryTypeSource)
|
||||
if (hasArbitraryTypeDeclaration) {
|
||||
return babelParse(fs.readFile(arbitraryTypeSource)!, {
|
||||
plugins: resolveParserPlugins('ts', parserPlugins, true),
|
||||
sourceType: 'module',
|
||||
}).program.body
|
||||
}
|
||||
|
||||
if (ext === '.vue') {
|
||||
const {
|
||||
descriptor: { script, scriptSetup },
|
||||
} = parse(content)
|
||||
|
@ -1554,6 +1588,14 @@ export function inferRuntimeType(
|
|||
case 'TSTypeReference': {
|
||||
const resolved = resolveTypeReference(ctx, node, scope)
|
||||
if (resolved) {
|
||||
if (resolved.type === 'TSTypeAliasDeclaration') {
|
||||
return inferRuntimeType(
|
||||
ctx,
|
||||
resolved.typeAnnotation,
|
||||
resolved._ownerScope,
|
||||
isKeyOf,
|
||||
)
|
||||
}
|
||||
return inferRuntimeType(ctx, resolved, resolved._ownerScope, isKeyOf)
|
||||
}
|
||||
|
||||
|
|
|
@ -189,8 +189,7 @@ function rewriteSelector(
|
|||
// global: replace with inner selector and do not inject [id].
|
||||
// ::v-global(.foo) -> .foo
|
||||
if (value === ':global' || value === '::v-global') {
|
||||
selectorRoot.insertAfter(selector, n.nodes[0])
|
||||
selectorRoot.removeChild(selector)
|
||||
selector.replaceWith(n.nodes[0])
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,28 +23,48 @@ export interface StylePreprocessorResults {
|
|||
|
||||
// .scss/.sass processor
|
||||
const scss: StylePreprocessor = (source, map, options, load = require) => {
|
||||
const nodeSass = load('sass')
|
||||
const finalOptions = {
|
||||
...options,
|
||||
data: getSource(source, options.filename, options.additionalData),
|
||||
file: options.filename,
|
||||
outFile: options.filename,
|
||||
sourceMap: !!map,
|
||||
}
|
||||
const nodeSass: typeof import('sass') = load('sass')
|
||||
const { compileString, renderSync } = nodeSass
|
||||
|
||||
const data = getSource(source, options.filename, options.additionalData)
|
||||
let css: string
|
||||
let dependencies: string[]
|
||||
let sourceMap: any
|
||||
|
||||
try {
|
||||
const result = nodeSass.renderSync(finalOptions)
|
||||
const dependencies = result.stats.includedFiles
|
||||
if (map) {
|
||||
return {
|
||||
code: result.css.toString(),
|
||||
map: merge(map, JSON.parse(result.map.toString())),
|
||||
errors: [],
|
||||
dependencies,
|
||||
}
|
||||
if (compileString) {
|
||||
const { pathToFileURL, fileURLToPath }: typeof import('url') = load('url')
|
||||
|
||||
const result = compileString(data, {
|
||||
...options,
|
||||
url: pathToFileURL(options.filename),
|
||||
sourceMap: !!map,
|
||||
})
|
||||
css = result.css
|
||||
dependencies = result.loadedUrls.map(url => fileURLToPath(url))
|
||||
sourceMap = map ? result.sourceMap! : undefined
|
||||
} else {
|
||||
const result = renderSync({
|
||||
...options,
|
||||
data,
|
||||
file: options.filename,
|
||||
outFile: options.filename,
|
||||
sourceMap: !!map,
|
||||
})
|
||||
css = result.css.toString()
|
||||
dependencies = result.stats.includedFiles
|
||||
sourceMap = map ? JSON.parse(result.map!.toString()) : undefined
|
||||
}
|
||||
|
||||
return { code: result.css.toString(), errors: [], dependencies }
|
||||
if (map) {
|
||||
return {
|
||||
code: css,
|
||||
errors: [],
|
||||
dependencies,
|
||||
map: merge(map, sourceMap!),
|
||||
}
|
||||
}
|
||||
return { code: css, errors: [], dependencies }
|
||||
} catch (e: any) {
|
||||
return { code: '', errors: [e], dependencies: [] }
|
||||
}
|
||||
|
|
|
@ -337,6 +337,39 @@ describe('ssr: element', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('custom dir with v-text', () => {
|
||||
expect(getCompiledString(`<div v-xxx v-text="foo" />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"\`<div\${
|
||||
_ssrRenderAttrs(_ssrGetDirectiveProps(_ctx, _directive_xxx))
|
||||
}>\${
|
||||
_ssrInterpolate(_ctx.foo)
|
||||
}</div>\`"
|
||||
`)
|
||||
})
|
||||
|
||||
test('custom dir with v-text and normal attrs', () => {
|
||||
expect(getCompiledString(`<div class="test" v-xxx v-text="foo" />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"\`<div\${
|
||||
_ssrRenderAttrs(_mergeProps({ class: "test" }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
||||
}>\${
|
||||
_ssrInterpolate(_ctx.foo)
|
||||
}</div>\`"
|
||||
`)
|
||||
})
|
||||
|
||||
test('mulptiple custom dirs with v-text', () => {
|
||||
expect(getCompiledString(`<div v-xxx v-yyy v-text="foo" />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"\`<div\${
|
||||
_ssrRenderAttrs(_mergeProps(_ssrGetDirectiveProps(_ctx, _directive_xxx), _ssrGetDirectiveProps(_ctx, _directive_yyy)))
|
||||
}>\${
|
||||
_ssrInterpolate(_ctx.foo)
|
||||
}</div>\`"
|
||||
`)
|
||||
})
|
||||
|
||||
test('custom dir with object v-bind', () => {
|
||||
expect(getCompiledString(`<div v-bind="x" v-xxx />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
|
|
@ -52,6 +52,52 @@ describe('ssr: v-model', () => {
|
|||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileWithWrapper(
|
||||
`<select v-model="model"><option v-for="i in items" :value="i"></option></select>`,
|
||||
).code,
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require("vue/server-renderer")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}><select><!--[-->\`)
|
||||
_ssrRenderList(_ctx.items, (i) => {
|
||||
_push(\`<option\${
|
||||
_ssrRenderAttr("value", i)
|
||||
}\${
|
||||
(_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
|
||||
? _ssrLooseContain(_ctx.model, i)
|
||||
: _ssrLooseEqual(_ctx.model, i))) ? " selected" : ""
|
||||
}></option>\`)
|
||||
})
|
||||
_push(\`<!--]--></select></div>\`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileWithWrapper(
|
||||
`<select v-model="model"><option v-if="true" :value="i"></option></select>`,
|
||||
).code,
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}><select>\`)
|
||||
if (true) {
|
||||
_push(\`<option\${
|
||||
_ssrRenderAttr("value", _ctx.i)
|
||||
}\${
|
||||
(_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
|
||||
? _ssrLooseContain(_ctx.model, _ctx.i)
|
||||
: _ssrLooseEqual(_ctx.model, _ctx.i))) ? " selected" : ""
|
||||
}></option>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
_push(\`</select></div>\`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileWithWrapper(
|
||||
`<select multiple v-model="model"><option value="1" selected></option><option value="2"></option></select>`,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-ssr",
|
||||
"version": "3.5.9",
|
||||
"version": "3.5.16",
|
||||
"description": "@vue/compiler-ssr",
|
||||
"main": "dist/compiler-ssr.cjs.js",
|
||||
"types": "dist/compiler-ssr.d.ts",
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
createSequenceExpression,
|
||||
createSimpleExpression,
|
||||
createTemplateLiteral,
|
||||
findDir,
|
||||
hasDynamicKeyVBind,
|
||||
isStaticArgOf,
|
||||
isStaticExp,
|
||||
|
@ -164,24 +165,28 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||
]
|
||||
}
|
||||
} else if (directives.length && !node.children.length) {
|
||||
const tempId = `_temp${context.temps++}`
|
||||
propsExp.arguments = [
|
||||
createAssignmentExpression(
|
||||
createSimpleExpression(tempId, false),
|
||||
mergedProps,
|
||||
),
|
||||
]
|
||||
rawChildrenMap.set(
|
||||
node,
|
||||
createConditionalExpression(
|
||||
createSimpleExpression(`"textContent" in ${tempId}`, false),
|
||||
createCallExpression(context.helper(SSR_INTERPOLATE), [
|
||||
createSimpleExpression(`${tempId}.textContent`, false),
|
||||
]),
|
||||
createSimpleExpression(`${tempId}.innerHTML ?? ''`, false),
|
||||
false,
|
||||
),
|
||||
)
|
||||
// v-text directive has higher priority than the merged props
|
||||
const vText = findDir(node, 'text')
|
||||
if (!vText) {
|
||||
const tempId = `_temp${context.temps++}`
|
||||
propsExp.arguments = [
|
||||
createAssignmentExpression(
|
||||
createSimpleExpression(tempId, false),
|
||||
mergedProps,
|
||||
),
|
||||
]
|
||||
rawChildrenMap.set(
|
||||
node,
|
||||
createConditionalExpression(
|
||||
createSimpleExpression(`"textContent" in ${tempId}`, false),
|
||||
createCallExpression(context.helper(SSR_INTERPOLATE), [
|
||||
createSimpleExpression(`${tempId}.textContent`, false),
|
||||
]),
|
||||
createSimpleExpression(`${tempId}.innerHTML ?? ''`, false),
|
||||
false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (needTagForRuntime) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
type ExpressionNode,
|
||||
NodeTypes,
|
||||
type PlainElementNode,
|
||||
type TemplateChildNode,
|
||||
createCallExpression,
|
||||
createConditionalExpression,
|
||||
createDOMCompilerError,
|
||||
|
@ -162,11 +163,18 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => {
|
|||
checkDuplicatedValue()
|
||||
node.children = [createInterpolation(model, model.loc)]
|
||||
} else if (node.tag === 'select') {
|
||||
node.children.forEach(child => {
|
||||
if (child.type === NodeTypes.ELEMENT) {
|
||||
processOption(child as PlainElementNode)
|
||||
}
|
||||
})
|
||||
const processChildren = (children: TemplateChildNode[]) => {
|
||||
children.forEach(child => {
|
||||
if (child.type === NodeTypes.ELEMENT) {
|
||||
processOption(child as PlainElementNode)
|
||||
} else if (child.type === NodeTypes.FOR) {
|
||||
processChildren(child.children)
|
||||
} else if (child.type === NodeTypes.IF) {
|
||||
child.branches.forEach(b => processChildren(b.children))
|
||||
}
|
||||
})
|
||||
}
|
||||
processChildren(node.children)
|
||||
} else {
|
||||
context.onError(
|
||||
createDOMCompilerError(
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { bench, describe } from 'vitest'
|
||||
import { type ComputedRef, type Ref, computed, effect, ref } from '../src'
|
||||
import type { ComputedRef, Ref } from '../src'
|
||||
import { computed, effect, ref } from '../dist/reactivity.esm-browser.prod'
|
||||
|
||||
declare module '../dist/reactivity.esm-browser.prod' {
|
||||
function computed(...args: any[]): any
|
||||
}
|
||||
|
||||
describe('computed', () => {
|
||||
bench('create computed', () => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { bench, describe } from 'vitest'
|
||||
import { type Ref, effect, ref } from '../src'
|
||||
import type { Ref } from '../src'
|
||||
import { effect, ref } from '../dist/reactivity.esm-browser.prod'
|
||||
|
||||
describe('effect', () => {
|
||||
{
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { bench } from 'vitest'
|
||||
import { effect, reactive, shallowReadArray } from '../src'
|
||||
import {
|
||||
effect,
|
||||
reactive,
|
||||
shallowReadArray,
|
||||
} from '../dist/reactivity.esm-browser.prod'
|
||||
|
||||
for (let amount = 1e1; amount < 1e4; amount *= 10) {
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { bench } from 'vitest'
|
||||
import { type ComputedRef, computed, reactive } from '../src'
|
||||
import type { ComputedRef } from '../src'
|
||||
import { computed, reactive } from '../dist/reactivity.esm-browser.prod'
|
||||
|
||||
function createMap(obj: Record<string, any>) {
|
||||
const map = new Map()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { bench } from 'vitest'
|
||||
import { reactive } from '../src'
|
||||
import { reactive } from '../dist/reactivity.esm-browser.prod'
|
||||
|
||||
bench('create reactive obj', () => {
|
||||
reactive({ a: 1 })
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { bench, describe } from 'vitest'
|
||||
import { ref } from '../src/index'
|
||||
import { ref } from '../dist/reactivity.esm-browser.prod'
|
||||
|
||||
describe('ref', () => {
|
||||
bench('create ref', () => {
|
||||
|
|
|
@ -1012,6 +1012,17 @@ describe('reactivity/computed', () => {
|
|||
expect(cValue.value).toBe(1)
|
||||
})
|
||||
|
||||
test('should not recompute if computed does not track reactive data', async () => {
|
||||
const spy = vi.fn()
|
||||
const c1 = computed(() => spy())
|
||||
|
||||
c1.value
|
||||
ref(0).value++ // update globalVersion
|
||||
c1.value
|
||||
|
||||
expect(spy).toBeCalledTimes(1)
|
||||
})
|
||||
|
||||
test('computed should remain live after losing all subscribers', () => {
|
||||
const state = reactive({ a: 1 })
|
||||
const p = computed(() => state.a + 1)
|
||||
|
@ -1057,4 +1068,86 @@ describe('reactivity/computed', () => {
|
|||
obj.flag = 1
|
||||
expect(foo).toBe(2)
|
||||
})
|
||||
|
||||
// #11928
|
||||
test('should not lead to exponential perf cost with deeply chained computed', () => {
|
||||
const start = {
|
||||
prop1: shallowRef(1),
|
||||
prop2: shallowRef(2),
|
||||
prop3: shallowRef(3),
|
||||
prop4: shallowRef(4),
|
||||
}
|
||||
|
||||
let layer = start
|
||||
|
||||
const LAYERS = 1000
|
||||
|
||||
for (let i = LAYERS; i > 0; i--) {
|
||||
const m = layer
|
||||
const s = {
|
||||
prop1: computed(() => m.prop2.value),
|
||||
prop2: computed(() => m.prop1.value - m.prop3.value),
|
||||
prop3: computed(() => m.prop2.value + m.prop4.value),
|
||||
prop4: computed(() => m.prop3.value),
|
||||
}
|
||||
effect(() => s.prop1.value)
|
||||
effect(() => s.prop2.value)
|
||||
effect(() => s.prop3.value)
|
||||
effect(() => s.prop4.value)
|
||||
|
||||
s.prop1.value
|
||||
s.prop2.value
|
||||
s.prop3.value
|
||||
s.prop4.value
|
||||
|
||||
layer = s
|
||||
}
|
||||
|
||||
const t = performance.now()
|
||||
start.prop1.value = 4
|
||||
start.prop2.value = 3
|
||||
start.prop3.value = 2
|
||||
start.prop4.value = 1
|
||||
expect(performance.now() - t).toBeLessThan(process.env.CI ? 100 : 30)
|
||||
|
||||
const end = layer
|
||||
expect([
|
||||
end.prop1.value,
|
||||
end.prop2.value,
|
||||
end.prop3.value,
|
||||
end.prop4.value,
|
||||
]).toMatchObject([-2, -4, 2, 3])
|
||||
})
|
||||
|
||||
test('performance when removing dependencies from deeply nested computeds', () => {
|
||||
const base = ref(1)
|
||||
const trigger = ref(true)
|
||||
const computeds: ComputedRef<number>[] = []
|
||||
|
||||
const LAYERS = 30
|
||||
|
||||
for (let i = 0; i < LAYERS; i++) {
|
||||
const earlier = [...computeds]
|
||||
|
||||
computeds.push(
|
||||
computed(() => {
|
||||
return base.value + earlier.reduce((sum, c) => sum + c.value, 0)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const tail = computed(() =>
|
||||
trigger.value ? computeds[computeds.length - 1].value : 0,
|
||||
)
|
||||
|
||||
const t0 = performance.now()
|
||||
expect(tail.value).toBe(2 ** (LAYERS - 1))
|
||||
const t1 = performance.now()
|
||||
expect(t1 - t0).toBeLessThan(process.env.CI ? 100 : 30)
|
||||
|
||||
trigger.value = false
|
||||
expect(tail.value).toBe(0)
|
||||
const t2 = performance.now()
|
||||
expect(t2 - t1).toBeLessThan(process.env.CI ? 100 : 30)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -176,7 +176,7 @@ describe('reactivity/effect/scope', () => {
|
|||
|
||||
expect('[Vue warn] cannot run an inactive effect scope.').toHaveBeenWarned()
|
||||
|
||||
expect(scope.effects.length).toBe(1)
|
||||
expect(scope.effects.length).toBe(0)
|
||||
|
||||
counter.num = 7
|
||||
expect(dummy).toBe(0)
|
||||
|
@ -322,4 +322,44 @@ describe('reactivity/effect/scope', () => {
|
|||
scope.resume()
|
||||
expect(fnSpy).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
test('removing a watcher while stopping its effectScope', async () => {
|
||||
const count = ref(0)
|
||||
const scope = effectScope()
|
||||
let watcherCalls = 0
|
||||
let cleanupCalls = 0
|
||||
|
||||
scope.run(() => {
|
||||
const stop1 = watch(count, () => {
|
||||
watcherCalls++
|
||||
})
|
||||
watch(count, (val, old, onCleanup) => {
|
||||
watcherCalls++
|
||||
onCleanup(() => {
|
||||
cleanupCalls++
|
||||
stop1()
|
||||
})
|
||||
})
|
||||
watch(count, () => {
|
||||
watcherCalls++
|
||||
})
|
||||
})
|
||||
|
||||
expect(watcherCalls).toBe(0)
|
||||
expect(cleanupCalls).toBe(0)
|
||||
|
||||
count.value++
|
||||
await nextTick()
|
||||
expect(watcherCalls).toBe(3)
|
||||
expect(cleanupCalls).toBe(0)
|
||||
|
||||
scope.stop()
|
||||
count.value++
|
||||
await nextTick()
|
||||
expect(watcherCalls).toBe(3)
|
||||
expect(cleanupCalls).toBe(1)
|
||||
|
||||
expect(scope.effects.length).toBe(0)
|
||||
expect(scope.cleanups.length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isRef, ref } from '../src/ref'
|
||||
import { isRef, ref, shallowRef } from '../src/ref'
|
||||
import {
|
||||
isProxy,
|
||||
isReactive,
|
||||
|
@ -195,8 +195,8 @@ describe('reactivity/reactive', () => {
|
|||
test('toRaw on object using reactive as prototype', () => {
|
||||
const original = { foo: 1 }
|
||||
const observed = reactive(original)
|
||||
const inherted = Object.create(observed)
|
||||
expect(toRaw(inherted)).toBe(inherted)
|
||||
const inherited = Object.create(observed)
|
||||
expect(toRaw(inherited)).toBe(inherited)
|
||||
})
|
||||
|
||||
test('toRaw on user Proxy wrapping reactive', () => {
|
||||
|
@ -301,6 +301,13 @@ describe('reactivity/reactive', () => {
|
|||
expect(() => markRaw(obj)).not.toThrowError()
|
||||
})
|
||||
|
||||
test('should not markRaw object as reactive', () => {
|
||||
const a = reactive({ a: 1 })
|
||||
const b = reactive({ b: 2 }) as any
|
||||
b.a = markRaw(toRaw(a))
|
||||
expect(b.a === a).toBe(false)
|
||||
})
|
||||
|
||||
test('should not observe non-extensible objects', () => {
|
||||
const obj = reactive({
|
||||
foo: Object.preventExtensions({ a: 1 }),
|
||||
|
@ -409,4 +416,27 @@ describe('reactivity/reactive', () => {
|
|||
e.effect.stop()
|
||||
expect(targetMap.get(obj)?.get('x')).toBeFalsy()
|
||||
})
|
||||
|
||||
test('should trigger reactivity when Map key is undefined', () => {
|
||||
const map = reactive(new Map())
|
||||
const c = computed(() => map.get(void 0))
|
||||
|
||||
expect(c.value).toBe(void 0)
|
||||
|
||||
map.set(void 0, 1)
|
||||
expect(c.value).toBe(1)
|
||||
})
|
||||
|
||||
test('should return true for reactive objects', () => {
|
||||
expect(isReactive(reactive({}))).toBe(true)
|
||||
expect(isReactive(readonly(reactive({})))).toBe(true)
|
||||
expect(isReactive(ref({}).value)).toBe(true)
|
||||
expect(isReactive(readonly(ref({})).value)).toBe(true)
|
||||
expect(isReactive(shallowReactive({}))).toBe(true)
|
||||
})
|
||||
|
||||
test('should return false for non-reactive objects', () => {
|
||||
expect(isReactive(ref(true))).toBe(false)
|
||||
expect(isReactive(shallowRef({}).value)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -51,6 +51,7 @@ describe('reactivity/reactive/Array', () => {
|
|||
const raw = {}
|
||||
const arr = reactive([{}, {}])
|
||||
arr.push(raw)
|
||||
|
||||
expect(arr.indexOf(raw)).toBe(2)
|
||||
expect(arr.indexOf(raw, 3)).toBe(-1)
|
||||
expect(arr.includes(raw)).toBe(true)
|
||||
|
@ -89,6 +90,84 @@ describe('reactivity/reactive/Array', () => {
|
|||
expect(index).toBe(1)
|
||||
})
|
||||
|
||||
// only non-existent reactive will try to search by using its raw value
|
||||
describe('Array identity methods should not be called more than necessary', () => {
|
||||
const identityMethods = ['includes', 'indexOf', 'lastIndexOf'] as const
|
||||
|
||||
function instrumentArr(rawTarget: any[]) {
|
||||
identityMethods.forEach(key => {
|
||||
const spy = vi.fn(rawTarget[key] as any)
|
||||
rawTarget[key] = spy
|
||||
})
|
||||
}
|
||||
|
||||
function searchValue(target: any[], ...args: unknown[]) {
|
||||
return identityMethods.map(key => (target[key] as any)(...args))
|
||||
}
|
||||
|
||||
function unInstrumentArr(rawTarget: any[]) {
|
||||
identityMethods.forEach(key => {
|
||||
;(rawTarget[key] as any).mockClear()
|
||||
// relink to prototype method
|
||||
rawTarget[key] = Array.prototype[key] as any
|
||||
})
|
||||
}
|
||||
|
||||
function expectHaveBeenCalledTimes(rawTarget: any[], times: number) {
|
||||
identityMethods.forEach(key => {
|
||||
expect(rawTarget[key]).toHaveBeenCalledTimes(times)
|
||||
})
|
||||
}
|
||||
|
||||
test('should be called once with a non-existent raw value', () => {
|
||||
const reactiveArr = reactive([])
|
||||
instrumentArr(toRaw(reactiveArr))
|
||||
const searchResult = searchValue(reactiveArr, {})
|
||||
|
||||
expectHaveBeenCalledTimes(toRaw(reactiveArr), 1)
|
||||
expect(searchResult).toStrictEqual([false, -1, -1])
|
||||
|
||||
unInstrumentArr(toRaw(reactiveArr))
|
||||
})
|
||||
|
||||
test('should be called once with an existent reactive value', () => {
|
||||
const existReactiveValue = reactive({})
|
||||
const reactiveArr = reactive([existReactiveValue, existReactiveValue])
|
||||
|
||||
instrumentArr(toRaw(reactiveArr))
|
||||
const searchResult = searchValue(reactiveArr, existReactiveValue)
|
||||
|
||||
expectHaveBeenCalledTimes(toRaw(reactiveArr), 1)
|
||||
expect(searchResult).toStrictEqual([true, 0, 1])
|
||||
|
||||
unInstrumentArr(toRaw(reactiveArr))
|
||||
})
|
||||
|
||||
test('should be called twice with a non-existent reactive value', () => {
|
||||
const reactiveArr = reactive([])
|
||||
instrumentArr(toRaw(reactiveArr))
|
||||
const searchResult = searchValue(reactiveArr, reactive({}))
|
||||
|
||||
expectHaveBeenCalledTimes(toRaw(reactiveArr), 2)
|
||||
expect(searchResult).toStrictEqual([false, -1, -1])
|
||||
|
||||
unInstrumentArr(toRaw(reactiveArr))
|
||||
})
|
||||
|
||||
test('should be called twice with a non-existent reactive value, but the raw value exists', () => {
|
||||
const existRaw = {}
|
||||
const reactiveArr = reactive([existRaw, existRaw])
|
||||
|
||||
instrumentArr(toRaw(reactiveArr))
|
||||
const searchResult = searchValue(reactiveArr, reactive(existRaw))
|
||||
|
||||
expectHaveBeenCalledTimes(toRaw(reactiveArr), 2)
|
||||
expect(searchResult).toStrictEqual([true, 0, 1])
|
||||
|
||||
unInstrumentArr(toRaw(reactiveArr))
|
||||
})
|
||||
})
|
||||
|
||||
test('delete on Array should not trigger length dependency', () => {
|
||||
const arr = reactive([1, 2, 3])
|
||||
const fn = vi.fn()
|
||||
|
|
|
@ -8,7 +8,9 @@ import {
|
|||
reactive,
|
||||
readonly,
|
||||
ref,
|
||||
shallowRef,
|
||||
toRaw,
|
||||
triggerRef,
|
||||
} from '../src'
|
||||
|
||||
/**
|
||||
|
@ -520,3 +522,16 @@ describe('reactivity/readonly', () => {
|
|||
expect(r.value).toBe(ro)
|
||||
})
|
||||
})
|
||||
|
||||
test('should be able to trigger with triggerRef', () => {
|
||||
const r = shallowRef({ a: 1 })
|
||||
const ror = readonly(r)
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = ror.value.a
|
||||
})
|
||||
r.value.a = 2
|
||||
expect(dummy).toBe(1)
|
||||
triggerRef(ror)
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue