chore: Merge remote-tracking branch 'origin/vapor' into generator-order
This commit is contained in:
commit
ba32beea40
|
@ -290,27 +290,39 @@ This is made possible via several configurations:
|
|||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
vue["vue"]
|
||||
compiler-sfc["@vue/compiler-sfc"]
|
||||
compiler-dom["@vue/compiler-dom"]
|
||||
compiler-vapor["@vue/compiler-vapor"]
|
||||
compiler-core["@vue/compiler-core"]
|
||||
vue["vue"]
|
||||
runtime-dom["@vue/runtime-dom"]
|
||||
runtime-vapor["@vue/runtime-vapor"]
|
||||
runtime-core["@vue/runtime-core"]
|
||||
reactivity["@vue/reactivity"]
|
||||
|
||||
subgraph "Runtime Packages"
|
||||
runtime-dom --> runtime-core
|
||||
runtime-vapor --> runtime-core
|
||||
runtime-core --> reactivity
|
||||
end
|
||||
|
||||
subgraph "Compiler Packages"
|
||||
compiler-sfc --> compiler-core
|
||||
compiler-sfc --> compiler-dom
|
||||
compiler-sfc --> compiler-vapor
|
||||
compiler-dom --> compiler-core
|
||||
compiler-vapor --> compiler-core
|
||||
end
|
||||
|
||||
vue --> compiler-sfc
|
||||
vue ---> compiler-dom
|
||||
vue --> runtime-dom
|
||||
vue --> compiler-vapor
|
||||
vue --> runtime-vapor
|
||||
|
||||
%% Highlight class
|
||||
classDef highlight stroke:#35eb9a,stroke-width:3px;
|
||||
class compiler-vapor,runtime-vapor highlight;
|
||||
```
|
||||
|
||||
There are some rules to follow when importing across package boundaries:
|
||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -1,3 +1,41 @@
|
|||
## [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)
|
||||
|
||||
|
||||
|
@ -8,7 +46,7 @@
|
|||
* **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)
|
||||
* **reactiivty:** 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:** 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))
|
||||
|
|
|
@ -35,6 +35,7 @@ Please make sure to respect issue requirements and use [the new issue helper](ht
|
|||
## Stay In Touch
|
||||
|
||||
- [X](https://x.com/vuejs)
|
||||
- [Bluesky](https://bsky.app/profile/vuejs.org)
|
||||
- [Blog](https://blog.vuejs.org/)
|
||||
- [Job Board](https://vuejobs.com/?ref=vuejs)
|
||||
|
||||
|
|
49
package.json
49
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"version": "3.5.13",
|
||||
"packageManager": "pnpm@10.7.0",
|
||||
"version": "3.5.14",
|
||||
"packageManager": "pnpm@10.9.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev.js",
|
||||
|
@ -69,24 +69,24 @@
|
|||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-replace": "5.0.4",
|
||||
"@swc/core": "^1.11.13",
|
||||
"@swc/core": "^1.11.24",
|
||||
"@types/hash-sum": "^1.0.2",
|
||||
"@types/node": "^22.13.14",
|
||||
"@types/node": "^22.14.1",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/serve-handler": "^6.1.4",
|
||||
"@vitest/ui": "^3.0.2",
|
||||
"@vitest/coverage-v8": "^3.0.9",
|
||||
"@vitest/eslint-plugin": "^1.1.38",
|
||||
"@vitest/coverage-v8": "^3.1.3",
|
||||
"@vitest/eslint-plugin": "^1.1.44",
|
||||
"@vue/consolidate": "1.0.0",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"enquirer": "^2.4.1",
|
||||
"esbuild": "^0.25.2",
|
||||
"esbuild": "^0.25.4",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-import-x": "^4.9.4",
|
||||
"eslint": "^9.25.1",
|
||||
"eslint-plugin-import-x": "^4.11.0",
|
||||
"estree-walker": "catalog:",
|
||||
"jsdom": "^26.0.0",
|
||||
"lint-staged": "^15.5.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"lint-staged": "^15.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"magic-string": "^0.30.17",
|
||||
"markdown-table": "^3.0.4",
|
||||
|
@ -96,38 +96,21 @@
|
|||
"prettier": "^3.5.3",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"pug": "^3.0.3",
|
||||
"puppeteer": "~24.4.0",
|
||||
"puppeteer": "~24.8.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.38.0",
|
||||
"rollup": "^4.40.2",
|
||||
"rollup-plugin-dts": "^6.2.1",
|
||||
"rollup-plugin-esbuild": "^6.2.1",
|
||||
"rollup-plugin-polyfill-node": "^0.13.0",
|
||||
"semver": "^7.7.1",
|
||||
"serve": "^14.2.4",
|
||||
"serve-handler": "^6.1.6",
|
||||
"simple-git-hooks": "^2.12.1",
|
||||
"simple-git-hooks": "^2.13.0",
|
||||
"todomvc-app-css": "^2.4.3",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.28.0",
|
||||
"typescript-eslint": "^8.31.1",
|
||||
"vite": "catalog:",
|
||||
"vitest": "^3.0.9"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"onlyBuiltDependencies": [
|
||||
"@swc/core",
|
||||
"esbuild",
|
||||
"puppeteer",
|
||||
"simple-git-hooks"
|
||||
]
|
||||
"vitest": "^3.1.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"isolatedDeclarations": false,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["./**/*", "../packages/*/src"]
|
||||
"include": ["./**/*", "../../packages/*/src"]
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"vue": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"vite": "^6.2.3"
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]' },
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-core",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/compiler-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-core.esm-bundler.js",
|
||||
|
|
|
@ -17,6 +17,7 @@ export {
|
|||
createTransformContext,
|
||||
traverseNode,
|
||||
createStructuralDirectiveTransform,
|
||||
getSelfName,
|
||||
type NodeTransform,
|
||||
type StructuralDirectiveTransform,
|
||||
type DirectiveTransform,
|
||||
|
|
|
@ -123,6 +123,11 @@ export interface TransformContext
|
|||
filters?: Set<string>
|
||||
}
|
||||
|
||||
export function getSelfName(filename: string): string | null {
|
||||
const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
|
||||
return nameMatch ? capitalize(camelize(nameMatch[1])) : null
|
||||
}
|
||||
|
||||
export function createTransformContext(
|
||||
root: RootNode,
|
||||
{
|
||||
|
@ -150,11 +155,10 @@ export function createTransformContext(
|
|||
compatConfig,
|
||||
}: TransformOptions,
|
||||
): TransformContext {
|
||||
const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
|
||||
const context: TransformContext = {
|
||||
// options
|
||||
filename,
|
||||
selfName: nameMatch && capitalize(camelize(nameMatch[1])),
|
||||
selfName: getSelfName(filename),
|
||||
prefixIdentifiers,
|
||||
hoistStatic,
|
||||
hmr,
|
||||
|
|
|
@ -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'
|
||||
|
@ -140,6 +143,7 @@ function walk(
|
|||
}
|
||||
|
||||
let cachedAsArray = false
|
||||
const slotCacheKeys = []
|
||||
if (toCache.length === children.length && node.type === NodeTypes.ELEMENT) {
|
||||
if (
|
||||
node.tagType === ElementTypes.ELEMENT &&
|
||||
|
@ -163,6 +167,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 +191,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 +202,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
|
||||
|
|
|
@ -342,7 +342,6 @@ export function buildSlots(
|
|||
: hasForwardedSlots(node.children)
|
||||
? SlotFlags.FORWARDED
|
||||
: SlotFlags.STABLE
|
||||
|
||||
let slots = createObjectExpression(
|
||||
slotsProperties.concat(
|
||||
createObjectProperty(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-dom",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/compiler-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-dom.esm-bundler.js",
|
||||
|
|
|
@ -196,6 +196,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 {
|
||||
|
|
|
@ -360,6 +360,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>
|
||||
|
|
|
@ -214,7 +214,8 @@ color: red
|
|||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:where(:hover) { color: blue;
|
||||
}"`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileScoped(`.div { color: red; } .div:is(:hover) { color: blue; }`),
|
||||
|
@ -222,7 +223,8 @@ color: red
|
|||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:is(:hover) { color: blue;
|
||||
}"`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileScoped(
|
||||
|
@ -232,7 +234,8 @@ color: red
|
|||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:where(.foo:hover) { color: blue;
|
||||
}"`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileScoped(
|
||||
|
@ -242,7 +245,8 @@ color: red
|
|||
".div[data-v-test] { color: red;
|
||||
}
|
||||
.div[data-v-test]:is(.foo:hover) { color: blue;
|
||||
}"`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('media query', () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-sfc",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/compiler-sfc",
|
||||
"main": "dist/compiler-sfc.cjs.js",
|
||||
"module": "dist/compiler-sfc.esm-browser.js",
|
||||
|
@ -63,6 +63,6 @@
|
|||
"postcss-modules": "^6.0.1",
|
||||
"postcss-selector-parser": "^7.1.0",
|
||||
"pug": "^3.0.3",
|
||||
"sass": "^1.86.0"
|
||||
"sass": "^1.86.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1120,6 +1120,7 @@ function walkDeclaration(
|
|||
m === userImportAliases['shallowRef'] ||
|
||||
m === userImportAliases['customRef'] ||
|
||||
m === userImportAliases['toRef'] ||
|
||||
m === userImportAliases['useTemplateRef'] ||
|
||||
m === DEFINE_MODEL,
|
||||
)
|
||||
) {
|
||||
|
|
|
@ -291,7 +291,8 @@ export function transformDestructuredProps(
|
|||
parent && parentStack.pop()
|
||||
if (
|
||||
(node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
|
||||
isFunctionType(node)
|
||||
isFunctionType(node) ||
|
||||
node.type === 'CatchClause'
|
||||
) {
|
||||
popScope()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-ssr",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/compiler-ssr",
|
||||
"main": "dist/compiler-ssr.cjs.js",
|
||||
"types": "dist/compiler-ssr.d.ts",
|
||||
|
|
|
@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
|||
const _component_Comp = _resolveComponent("Comp")
|
||||
const n0 = t0()
|
||||
const n3 = t1()
|
||||
const n2 = _child(n3)
|
||||
_setInsertionState(n3, 0)
|
||||
const n1 = _createComponentWithFallback(_component_Comp)
|
||||
const n2 = _child(n3)
|
||||
_renderEffect(() => {
|
||||
_setProp(n3, "id", _ctx.foo)
|
||||
_setText(n2, _toDisplayString(_ctx.bar))
|
||||
|
@ -264,6 +264,30 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>")
|
||||
const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
const n6 = t1()
|
||||
const n5 = _next(_child(n6))
|
||||
const n7 = _nthChild(n6, 3)
|
||||
const p0 = _next(n7)
|
||||
const n4 = _child(p0)
|
||||
_setInsertionState(n6, n5)
|
||||
const n0 = _createComponentWithFallback(_component_Comp)
|
||||
_setInsertionState(n6, n7)
|
||||
const n1 = _createIf(() => (true), () => {
|
||||
const n3 = t0()
|
||||
return n3
|
||||
})
|
||||
_renderEffect(() => _setProp(n4, "disabled", _ctx.foo))
|
||||
return n6
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > static + dynamic root 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
|
|
@ -221,6 +221,23 @@ describe('compile', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('setInsertionState', () => {
|
||||
test('next, child and nthChild should be above the setInsertionState', () => {
|
||||
const code = compile(`
|
||||
<div>
|
||||
<div />
|
||||
<Comp />
|
||||
<div />
|
||||
<div v-if="true" />
|
||||
<div>
|
||||
<button :disabled="foo" />
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('execution order', () => {
|
||||
test('basic', () => {
|
||||
const code = compile(`<div :id="foo">{{ bar }}</div>`)
|
||||
|
|
|
@ -77,6 +77,16 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: element transform > component > resolve implicitly self-referencing component 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Example__self = _resolveComponent("Example", true)
|
||||
const n0 = _createComponentWithFallback(_component_Example__self, null, null, true)
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: element transform > component > resolve namespaced component from props bindings (inline) 1`] = `
|
||||
"
|
||||
const n0 = _createComponent(Foo.Example, null, null, true)
|
||||
|
|
|
@ -13,6 +13,19 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: template ref transform > function ref 1`] = `
|
||||
"import { createTemplateRefSetter as _createTemplateRefSetter, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const _setTemplateRef = _createTemplateRefSetter()
|
||||
const n0 = t0()
|
||||
let r0
|
||||
_renderEffect(() => r0 = _setTemplateRef(n0, bar => _ctx.foo = bar, r0))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: template ref transform > ref + v-for 1`] = `
|
||||
"import { createTemplateRefSetter as _createTemplateRefSetter, createFor as _createFor, template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>", true)
|
||||
|
|
|
@ -74,6 +74,17 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`cache multiple access > not cache variable in function expression 1`] = `
|
||||
"import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = t0()
|
||||
_renderEffect(() => _setDynamicProps(n0, [{ foo: bar => _ctx.foo = bar }], true))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`cache multiple access > not cache variable only used in property shorthand 1`] = `
|
||||
"import { setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>", true)
|
||||
|
|
|
@ -134,3 +134,29 @@ export function render(_ctx) {
|
|||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: v-if > v-if + v-if / v-else[-if] 1`] = `
|
||||
"import { setInsertionState as _setInsertionState, createIf as _createIf, template as _template } from 'vue';
|
||||
const t0 = _template("<span>foo</span>")
|
||||
const t1 = _template("<span>bar</span>")
|
||||
const t2 = _template("<span>baz</span>")
|
||||
const t3 = _template("<div></div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const n8 = t3()
|
||||
_setInsertionState(n8)
|
||||
const n0 = _createIf(() => (_ctx.foo), () => {
|
||||
const n2 = t0()
|
||||
return n2
|
||||
})
|
||||
_setInsertionState(n8)
|
||||
const n3 = _createIf(() => (_ctx.bar), () => {
|
||||
const n5 = t1()
|
||||
return n5
|
||||
}, () => {
|
||||
const n7 = t2()
|
||||
return n7
|
||||
})
|
||||
return n8
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -252,3 +252,25 @@ export function render(_ctx) {
|
|||
return n1
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: transform slot > slot + v-if / v-else[-if] should not cause error 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createSlot as _createSlot, createComponentWithFallback as _createComponentWithFallback, createIf as _createIf, template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Foo = _resolveComponent("Foo")
|
||||
const _component_Bar = _resolveComponent("Bar")
|
||||
const n6 = t0()
|
||||
_setInsertionState(n6)
|
||||
const n0 = _createSlot("foo", null)
|
||||
_setInsertionState(n6)
|
||||
const n1 = _createIf(() => (true), () => {
|
||||
const n3 = _createComponentWithFallback(_component_Foo)
|
||||
return n3
|
||||
}, () => {
|
||||
const n5 = _createComponentWithFallback(_component_Bar)
|
||||
return n5
|
||||
})
|
||||
return n6
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -69,8 +69,8 @@ describe('compiler: children transform', () => {
|
|||
</div>`,
|
||||
)
|
||||
// ensure the insertion anchor is generated before the insertion statement
|
||||
expect(code).toMatch(`const n3 = _next(_child(n4))
|
||||
_setInsertionState(n4, n3)`)
|
||||
expect(code).toMatch(`const n3 = _next(_child(n4))`)
|
||||
expect(code).toMatch(`_setInsertionState(n4, n3)`)
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -39,11 +39,12 @@ describe('compiler: element transform', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test.todo('resolve implicitly self-referencing component', () => {
|
||||
test('resolve implicitly self-referencing component', () => {
|
||||
const { code, helpers } = compileWithElementTransform(`<Example/>`, {
|
||||
filename: `/foo/bar/Example.vue?vue&type=template`,
|
||||
})
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(code).toContain('_resolveComponent("Example", true)')
|
||||
expect(helpers).toContain('resolveComponent')
|
||||
})
|
||||
|
||||
|
|
|
@ -81,6 +81,40 @@ describe('compiler: template ref transform', () => {
|
|||
expect(code).contains('_setTemplateRef(n0, _ctx.foo, r0)')
|
||||
})
|
||||
|
||||
test('function ref', () => {
|
||||
const { ir, code } = compileWithTransformRef(
|
||||
`<div :ref="bar => foo = bar" />`,
|
||||
)
|
||||
expect(ir.block.dynamic.children[0]).toMatchObject({
|
||||
id: 0,
|
||||
flags: DynamicFlag.REFERENCED,
|
||||
})
|
||||
expect(ir.template).toEqual(['<div></div>'])
|
||||
expect(ir.block.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.DECLARE_OLD_REF,
|
||||
id: 0,
|
||||
},
|
||||
])
|
||||
expect(ir.block.effect).toMatchObject([
|
||||
{
|
||||
operations: [
|
||||
{
|
||||
type: IRNodeTypes.SET_TEMPLATE_REF,
|
||||
element: 0,
|
||||
value: {
|
||||
content: 'bar => foo = bar',
|
||||
isStatic: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()')
|
||||
expect(code).contains('_setTemplateRef(n0, bar => _ctx.foo = bar, r0)')
|
||||
})
|
||||
|
||||
test('ref + v-if', () => {
|
||||
const { ir, code } = compileWithTransformRef(
|
||||
`<div ref="foo" v-if="true" />`,
|
||||
|
|
|
@ -809,4 +809,12 @@ describe('cache multiple access', () => {
|
|||
expect(code).matchSnapshot()
|
||||
expect(code).not.contains('const _bar = _ctx.bar')
|
||||
})
|
||||
|
||||
test('not cache variable in function expression', () => {
|
||||
const { code } = compileWithVBind(`
|
||||
<div v-bind="{ foo: bar => foo = bar }"></div>
|
||||
`)
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).not.contains('const _bar = _ctx.bar')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -215,6 +215,17 @@ describe('compiler: v-if', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('v-if + v-if / v-else[-if]', () => {
|
||||
const { code } = compileWithVIf(
|
||||
`<div>
|
||||
<span v-if="foo">foo</span>
|
||||
<span v-if="bar">bar</span>
|
||||
<span v-else>baz</span>
|
||||
</div>`,
|
||||
)
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('comment between branches', () => {
|
||||
const { code, ir } = compileWithVIf(`
|
||||
<div v-if="ok"/>
|
||||
|
|
|
@ -371,6 +371,17 @@ describe('compiler: transform slot', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('slot + v-if / v-else[-if] should not cause error', () => {
|
||||
const { code } = compileWithSlots(
|
||||
`<div>
|
||||
<slot name="foo"></slot>
|
||||
<Foo v-if="true"></Foo>
|
||||
<Bar v-else />
|
||||
</div>`,
|
||||
)
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('quote slot name', () => {
|
||||
const { code } = compileWithSlots(
|
||||
`<Comp><template #nav-bar-title-before></template></Comp>`,
|
||||
|
|
|
@ -44,7 +44,21 @@ export function genBlockContent(
|
|||
const resetBlock = context.enterBlock(block)
|
||||
|
||||
if (root) {
|
||||
genResolveAssets('component', 'resolveComponent')
|
||||
for (let name of context.ir.component) {
|
||||
const id = toValidAssetId(name, 'component')
|
||||
const maybeSelfReference = name.endsWith('__self')
|
||||
if (maybeSelfReference) name = name.slice(0, -6)
|
||||
push(
|
||||
NEWLINE,
|
||||
`const ${id} = `,
|
||||
...genCall(
|
||||
context.helper('resolveComponent'),
|
||||
JSON.stringify(name),
|
||||
// pass additional `maybeSelfReference` flag
|
||||
maybeSelfReference ? 'true' : undefined,
|
||||
),
|
||||
)
|
||||
}
|
||||
genResolveAssets('directive', 'resolveDirective')
|
||||
}
|
||||
|
||||
|
@ -52,7 +66,7 @@ export function genBlockContent(
|
|||
push(...genSelf(child, context))
|
||||
}
|
||||
for (const child of dynamic.children) {
|
||||
push(...genChildren(child, context, `n${child.id!}`))
|
||||
push(...genChildren(child, context, push, `n${child.id!}`))
|
||||
}
|
||||
|
||||
push(...genOperations(operation, context))
|
||||
|
|
|
@ -20,7 +20,6 @@ import type { Identifier, Node } from '@babel/types'
|
|||
import type { CodegenContext } from '../generate'
|
||||
import { isConstantExpression } from '../utils'
|
||||
import { type CodeFragment, NEWLINE, buildCodeFragment } from './utils'
|
||||
import { walk } from 'estree-walker'
|
||||
import { type ParserOptions, parseExpression } from '@babel/parser'
|
||||
|
||||
export function genExpression(
|
||||
|
@ -295,33 +294,15 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
|
|||
continue
|
||||
}
|
||||
|
||||
walk(exp.ast, {
|
||||
enter(currentNode: Node, parent: Node | null) {
|
||||
if (currentNode.type === 'MemberExpression') {
|
||||
const memberExp = extractMemberExpression(
|
||||
currentNode,
|
||||
(name: string) => {
|
||||
walkIdentifiers(exp.ast, (currentNode, parent, parentStack) => {
|
||||
if (parent && isMemberExpression(parent)) {
|
||||
const memberExp = extractMemberExpression(parent, name => {
|
||||
registerVariable(name, exp, true)
|
||||
},
|
||||
)
|
||||
})
|
||||
registerVariable(memberExp, exp, false)
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
// skip shorthand or non-computed property keys
|
||||
if (
|
||||
parent &&
|
||||
parent.type === 'ObjectProperty' &&
|
||||
parent.key === currentNode &&
|
||||
(parent.shorthand || !parent.computed)
|
||||
) {
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
if (currentNode.type === 'Identifier') {
|
||||
} else if (!parentStack.some(isMemberExpression)) {
|
||||
registerVariable(currentNode.name, exp, true)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -580,3 +561,9 @@ function extractMemberExpression(
|
|||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const isMemberExpression = (node: Node) => {
|
||||
return (
|
||||
node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression'
|
||||
)
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ export function genOperationWithInsertionState(
|
|||
): CodeFragment[] {
|
||||
const [frag, push] = buildCodeFragment()
|
||||
if (isBlockOperation(oper) && oper.parent) {
|
||||
push(...genInsertionstate(oper, context))
|
||||
push(...genInsertionState(oper, context))
|
||||
}
|
||||
push(...genOperation(oper, context))
|
||||
return frag
|
||||
|
@ -150,7 +150,7 @@ export function genEffect(
|
|||
return frag
|
||||
}
|
||||
|
||||
function genInsertionstate(
|
||||
function genInsertionState(
|
||||
operation: InsertionStateTypes,
|
||||
context: CodegenContext,
|
||||
): CodeFragment[] {
|
||||
|
|
|
@ -41,6 +41,7 @@ export function genSelf(
|
|||
export function genChildren(
|
||||
dynamic: IRDynamicInfo,
|
||||
context: CodegenContext,
|
||||
pushBlock: (...items: CodeFragment[]) => number,
|
||||
from: string = `n${dynamic.id}`,
|
||||
): CodeFragment[] {
|
||||
const { helper } = context
|
||||
|
@ -72,17 +73,17 @@ export function genChildren(
|
|||
// p for "placeholder" variables that are meant for possible reuse by
|
||||
// other access paths
|
||||
const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
|
||||
push(NEWLINE, `const ${variable} = `)
|
||||
pushBlock(NEWLINE, `const ${variable} = `)
|
||||
|
||||
if (prev) {
|
||||
if (elementIndex - prev[1] === 1) {
|
||||
push(...genCall(helper('next'), prev[0]))
|
||||
pushBlock(...genCall(helper('next'), prev[0]))
|
||||
} else {
|
||||
push(...genCall(helper('nthChild'), from, String(elementIndex)))
|
||||
pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
|
||||
}
|
||||
} else {
|
||||
if (elementIndex === 0) {
|
||||
push(...genCall(helper('child'), from))
|
||||
pushBlock(...genCall(helper('child'), from))
|
||||
} else {
|
||||
// check if there's a node that we can reuse from
|
||||
let init = genCall(helper('child'), from)
|
||||
|
@ -91,7 +92,7 @@ export function genChildren(
|
|||
} else if (elementIndex > 1) {
|
||||
init = genCall(helper('nthChild'), from, String(elementIndex))
|
||||
}
|
||||
push(...init)
|
||||
pushBlock(...init)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,7 @@ export function genChildren(
|
|||
|
||||
if (childrenToGen.length) {
|
||||
for (const [child, from] of childrenToGen) {
|
||||
push(...genChildren(child, context, from))
|
||||
push(...genChildren(child, context, pushBlock, from))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
type TemplateChildNode,
|
||||
defaultOnError,
|
||||
defaultOnWarn,
|
||||
getSelfName,
|
||||
isVSlot,
|
||||
} from '@vue/compiler-dom'
|
||||
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
|
||||
|
@ -61,6 +62,7 @@ export type StructuralDirectiveTransform = (
|
|||
export type TransformOptions = HackOptions<BaseTransformOptions>
|
||||
|
||||
export class TransformContext<T extends AllNode = AllNode> {
|
||||
selfName: string | null = null
|
||||
parent: TransformContext<RootNode | ElementNode> | null = null
|
||||
root: TransformContext<RootNode>
|
||||
index: number = 0
|
||||
|
@ -92,6 +94,7 @@ export class TransformContext<T extends AllNode = AllNode> {
|
|||
) {
|
||||
this.options = extend({}, defaultOptions, options)
|
||||
this.root = this as TransformContext<RootNode>
|
||||
if (options.filename) this.selfName = getSelfName(options.filename)
|
||||
}
|
||||
|
||||
enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void {
|
||||
|
|
|
@ -132,6 +132,13 @@ function transformComponentElement(
|
|||
}
|
||||
|
||||
if (asset) {
|
||||
// self referencing component (inferred from filename)
|
||||
if (context.selfName && capitalize(camelize(tag)) === context.selfName) {
|
||||
// generators/block.ts has special check for __self postfix when generating
|
||||
// component imports, which will pass additional `maybeSelfReference` flag
|
||||
// to `resolveComponent`.
|
||||
tag += `__self`
|
||||
}
|
||||
context.component.add(tag)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,13 @@ export function processIf(
|
|||
if (siblings) {
|
||||
let i = siblings.length
|
||||
while (i--) {
|
||||
if (siblings[i].operation) lastIfNode = siblings[i].operation
|
||||
if (
|
||||
siblings[i].operation &&
|
||||
siblings[i].operation!.type === IRNodeTypes.IF
|
||||
) {
|
||||
lastIfNode = siblings[i].operation
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1017,6 +1017,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)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/reactivity",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/reactivity",
|
||||
"main": "index.js",
|
||||
"module": "dist/reactivity.esm-bundler.js",
|
||||
|
|
|
@ -324,4 +324,98 @@ describe('component: slots', () => {
|
|||
'Slot "default" invoked outside of the render function',
|
||||
).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('basic warn', () => {
|
||||
const Comp = {
|
||||
setup(_: any, { slots }: any) {
|
||||
slots.default && slots.default()
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
return () => h(Comp, () => h('div'))
|
||||
},
|
||||
}
|
||||
|
||||
createApp(App).mount(nodeOps.createElement('div'))
|
||||
expect(
|
||||
'Slot "default" invoked outside of the render function',
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('basic warn when mounting another app in setup', () => {
|
||||
const Comp = {
|
||||
setup(_: any, { slots }: any) {
|
||||
slots.default?.()
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
|
||||
const mountComp = () => {
|
||||
createApp({
|
||||
setup() {
|
||||
return () => h(Comp, () => 'msg')
|
||||
},
|
||||
}).mount(nodeOps.createElement('div'))
|
||||
}
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
mountComp()
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
|
||||
createApp(App).mount(nodeOps.createElement('div'))
|
||||
expect(
|
||||
'Slot "default" invoked outside of the render function',
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should not warn when render in setup', () => {
|
||||
const container = {
|
||||
setup(_: any, { slots }: any) {
|
||||
return () => slots.default && slots.default()
|
||||
},
|
||||
}
|
||||
|
||||
const comp = h(container, null, () => h('div'))
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
render(h(comp), nodeOps.createElement('div'))
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
|
||||
createApp(App).mount(nodeOps.createElement('div'))
|
||||
expect(
|
||||
'Slot "default" invoked outside of the render function',
|
||||
).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('basic warn when render in setup', () => {
|
||||
const container = {
|
||||
setup(_: any, { slots }: any) {
|
||||
slots.default && slots.default()
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
|
||||
const comp = h(container, null, () => h('div'))
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
render(h(comp), nodeOps.createElement('div'))
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
|
||||
createApp(App).mount(nodeOps.createElement('div'))
|
||||
expect(
|
||||
'Slot "default" invoked outside of the render function',
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1198,4 +1198,51 @@ describe('BaseTransition', () => {
|
|||
test('should not error on KeepAlive w/ function children', () => {
|
||||
expect(() => mount({}, () => () => h('div'), true)).not.toThrow()
|
||||
})
|
||||
|
||||
// #12465
|
||||
test('mode: "out-in" w/ KeepAlive + fallthrough attrs (prod mode)', async () => {
|
||||
__DEV__ = false
|
||||
async function testOutIn({ trueBranch, falseBranch }: ToggleOptions) {
|
||||
const toggle = ref(true)
|
||||
const { props, cbs } = mockProps({ mode: 'out-in' }, true)
|
||||
const root = nodeOps.createElement('div')
|
||||
const App = {
|
||||
render() {
|
||||
return h(
|
||||
BaseTransition,
|
||||
{
|
||||
...props,
|
||||
class: 'test',
|
||||
},
|
||||
() =>
|
||||
h(KeepAlive, null, toggle.value ? trueBranch() : falseBranch()),
|
||||
)
|
||||
},
|
||||
}
|
||||
render(h(App), root)
|
||||
|
||||
expect(serializeInner(root)).toBe(`<div class="test">0</div>`)
|
||||
|
||||
// trigger toggle
|
||||
toggle.value = false
|
||||
await nextTick()
|
||||
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
||||
expect(serialize((props.onBeforeLeave as any).mock.calls[0][0])).toBe(
|
||||
`<div class="test">0</div>`,
|
||||
)
|
||||
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
||||
expect(serialize((props.onLeave as any).mock.calls[0][0])).toBe(
|
||||
`<div class="test">0</div>`,
|
||||
)
|
||||
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
||||
// enter should not have started
|
||||
expect(props.onBeforeEnter).not.toHaveBeenCalled()
|
||||
expect(props.onEnter).not.toHaveBeenCalled()
|
||||
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||
cbs.doneLeave[`<div class="test">0</div>`]()
|
||||
expect(serializeInner(root)).toBe(`<span class="test">0</span>`)
|
||||
}
|
||||
await runTestWithKeepAlive(testOutIn)
|
||||
__DEV__ = true
|
||||
})
|
||||
})
|
||||
|
|
|
@ -10,14 +10,28 @@ import {
|
|||
markRaw,
|
||||
nextTick,
|
||||
nodeOps,
|
||||
onMounted,
|
||||
h as originalH,
|
||||
ref,
|
||||
render,
|
||||
serialize,
|
||||
serializeInner,
|
||||
withDirectives,
|
||||
} from '@vue/runtime-test'
|
||||
import { Fragment, createCommentVNode, createVNode } from '../../src/vnode'
|
||||
import {
|
||||
Fragment,
|
||||
createBlock,
|
||||
createCommentVNode,
|
||||
createTextVNode,
|
||||
createVNode,
|
||||
openBlock,
|
||||
} from '../../src/vnode'
|
||||
import { toDisplayString } from '@vue/shared'
|
||||
import { compile, createApp as createDOMApp, render as domRender } from 'vue'
|
||||
import type { HMRRuntime } from '../../src/hmr'
|
||||
|
||||
declare var __VUE_HMR_RUNTIME__: HMRRuntime
|
||||
const { rerender, createRecord } = __VUE_HMR_RUNTIME__
|
||||
|
||||
describe('renderer: teleport', () => {
|
||||
describe('eager mode', () => {
|
||||
|
@ -243,6 +257,39 @@ describe('renderer: teleport', () => {
|
|||
expect(serializeInner(target)).toBe(`teleported`)
|
||||
})
|
||||
|
||||
test('should traverse comment node after updating in optimize mode', async () => {
|
||||
const target = nodeOps.createElement('div')
|
||||
const root = nodeOps.createElement('div')
|
||||
const count = ref(0)
|
||||
let teleport
|
||||
|
||||
__DEV__ = false
|
||||
render(
|
||||
h(() => {
|
||||
teleport =
|
||||
(openBlock(),
|
||||
createBlock(Teleport, { to: target }, [
|
||||
createCommentVNode('comment in teleport'),
|
||||
]))
|
||||
return h('div', null, [
|
||||
createTextVNode(toDisplayString(count.value)),
|
||||
teleport,
|
||||
])
|
||||
}),
|
||||
root,
|
||||
)
|
||||
const commentNode = teleport!.children[0].el
|
||||
expect(serializeInner(root)).toBe(`<div>0</div>`)
|
||||
expect(serializeInner(target)).toBe(`<!--comment in teleport-->`)
|
||||
expect(serialize(commentNode)).toBe(`<!--comment in teleport-->`)
|
||||
|
||||
count.value = 1
|
||||
await nextTick()
|
||||
__DEV__ = true
|
||||
expect(serializeInner(root)).toBe(`<div>1</div>`)
|
||||
expect(teleport!.children[0].el).toBe(commentNode)
|
||||
})
|
||||
|
||||
test('should remove children when unmounted', () => {
|
||||
const target = nodeOps.createElement('div')
|
||||
const root = nodeOps.createElement('div')
|
||||
|
@ -269,6 +316,34 @@ describe('renderer: teleport', () => {
|
|||
testUnmount({ to: null, disabled: true })
|
||||
})
|
||||
|
||||
// #10747
|
||||
test('should unmount correctly when using top level comment in teleport', async () => {
|
||||
const target = nodeOps.createElement('div')
|
||||
const root = nodeOps.createElement('div')
|
||||
const count = ref(0)
|
||||
|
||||
__DEV__ = false
|
||||
render(
|
||||
h(() => {
|
||||
return h('div', null, [
|
||||
createTextVNode(toDisplayString(count.value)),
|
||||
(openBlock(),
|
||||
createBlock(Teleport, { to: target }, [
|
||||
createCommentVNode('comment in teleport'),
|
||||
])),
|
||||
])
|
||||
}),
|
||||
root,
|
||||
)
|
||||
|
||||
count.value = 1
|
||||
|
||||
await nextTick()
|
||||
__DEV__ = true
|
||||
render(null, root)
|
||||
expect(root.children.length).toBe(0)
|
||||
})
|
||||
|
||||
test('component with multi roots should be removed when unmounted', () => {
|
||||
const target = nodeOps.createElement('div')
|
||||
const root = nodeOps.createElement('div')
|
||||
|
@ -741,4 +816,56 @@ describe('renderer: teleport', () => {
|
|||
expect(tRefInMounted).toBe(target.children[1])
|
||||
})
|
||||
}
|
||||
|
||||
test('handle update and hmr rerender', async () => {
|
||||
const target = document.createElement('div')
|
||||
const root = document.createElement('div')
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
const cls = ref('foo')
|
||||
onMounted(() => {
|
||||
// trigger update
|
||||
cls.value = 'bar'
|
||||
})
|
||||
return { cls, target }
|
||||
},
|
||||
template: `
|
||||
<Teleport :to="target">
|
||||
<div :class="cls">
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
`,
|
||||
}
|
||||
|
||||
const appId = 'test-app-id'
|
||||
const App = {
|
||||
__hmrId: appId,
|
||||
components: { Comp },
|
||||
render() {
|
||||
return originalH(Comp, null, { default: () => originalH('div', 'foo') })
|
||||
},
|
||||
}
|
||||
createRecord(appId, App)
|
||||
|
||||
domRender(originalH(App), root)
|
||||
expect(target.innerHTML).toBe(
|
||||
'<div class="foo"><div><div>foo</div></div></div>',
|
||||
)
|
||||
await nextTick()
|
||||
expect(target.innerHTML).toBe(
|
||||
'<div class="bar"><div><div>foo</div></div></div>',
|
||||
)
|
||||
|
||||
rerender(appId, () =>
|
||||
originalH(Comp, null, { default: () => originalH('div', 'bar') }),
|
||||
)
|
||||
await nextTick()
|
||||
expect(target.innerHTML).toBe(
|
||||
'<div class="bar"><div><div>bar</div></div></div>',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { isReactive, reactive, shallowReactive } from '../../src/index'
|
||||
import {
|
||||
effect,
|
||||
isReactive,
|
||||
reactive,
|
||||
readonly,
|
||||
shallowReactive,
|
||||
} from '../../src/index'
|
||||
import { renderList } from '../../src/helpers/renderList'
|
||||
|
||||
describe('renderList', () => {
|
||||
|
@ -65,4 +71,31 @@ describe('renderList', () => {
|
|||
const shallowReactiveArray = shallowReactive([{ foo: 1 }])
|
||||
expect(renderList(shallowReactiveArray, isReactive)).toEqual([false])
|
||||
})
|
||||
|
||||
it('should not allow mutation', () => {
|
||||
const arr = readonly(reactive([{ foo: 1 }]))
|
||||
expect(
|
||||
renderList(arr, item => {
|
||||
;(item as any).foo = 0
|
||||
return item.foo
|
||||
}),
|
||||
).toEqual([1])
|
||||
expect(
|
||||
`Set operation on key "foo" failed: target is readonly.`,
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('should trigger effect for deep mutations in readonly reactive arrays', () => {
|
||||
const arr = reactive([{ foo: 1 }])
|
||||
const readonlyArr = readonly(arr)
|
||||
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = renderList(readonlyArr, item => item.foo)
|
||||
})
|
||||
expect(dummy).toEqual([1])
|
||||
|
||||
arr[0].foo = 2
|
||||
expect(dummy).toEqual([2])
|
||||
})
|
||||
})
|
||||
|
|
|
@ -32,10 +32,13 @@ import {
|
|||
withCtx,
|
||||
withDirectives,
|
||||
} from '@vue/runtime-dom'
|
||||
import type { HMRRuntime } from '../src/hmr'
|
||||
import { type SSRContext, renderToString } from '@vue/server-renderer'
|
||||
import { PatchFlags, normalizeStyle } from '@vue/shared'
|
||||
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
|
||||
import { expect } from 'vitest'
|
||||
|
||||
declare var __VUE_HMR_RUNTIME__: HMRRuntime
|
||||
const { createRecord, reload } = __VUE_HMR_RUNTIME__
|
||||
|
||||
function mountWithHydration(html: string, render: () => any) {
|
||||
const container = document.createElement('div')
|
||||
|
@ -1843,6 +1846,60 @@ describe('SSR hydration', () => {
|
|||
}
|
||||
})
|
||||
|
||||
test('hmr reload child wrapped in KeepAlive', async () => {
|
||||
const id = 'child-reload'
|
||||
const Child = {
|
||||
__hmrId: id,
|
||||
template: `<div>foo</div>`,
|
||||
}
|
||||
createRecord(id, Child)
|
||||
|
||||
const appId = 'test-app-id'
|
||||
const App = {
|
||||
__hmrId: appId,
|
||||
components: { Child },
|
||||
template: `
|
||||
<div>
|
||||
<KeepAlive>
|
||||
<Child />
|
||||
</KeepAlive>
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
|
||||
const root = document.createElement('div')
|
||||
root.innerHTML = await renderToString(h(App))
|
||||
createSSRApp(App).mount(root)
|
||||
expect(root.innerHTML).toBe('<div><div>foo</div></div>')
|
||||
|
||||
reload(id, {
|
||||
__hmrId: id,
|
||||
template: `<div>bar</div>`,
|
||||
})
|
||||
await nextTick()
|
||||
expect(root.innerHTML).toBe('<div><div>bar</div></div>')
|
||||
})
|
||||
|
||||
test('hmr root reload', async () => {
|
||||
const appId = 'test-app-id'
|
||||
const App = {
|
||||
__hmrId: appId,
|
||||
template: `<div>foo</div>`,
|
||||
}
|
||||
|
||||
const root = document.createElement('div')
|
||||
root.innerHTML = await renderToString(h(App))
|
||||
createSSRApp(App).mount(root)
|
||||
expect(root.innerHTML).toBe('<div>foo</div>')
|
||||
|
||||
reload(appId, {
|
||||
__hmrId: appId,
|
||||
template: `<div>bar</div>`,
|
||||
})
|
||||
await nextTick()
|
||||
expect(root.innerHTML).toBe('<div>bar</div>')
|
||||
})
|
||||
|
||||
describe('mismatch handling', () => {
|
||||
test('text node', () => {
|
||||
const { container } = mountWithHydration(`foo`, () => 'bar')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-core",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/runtime-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-core.esm-bundler.js",
|
||||
|
|
|
@ -837,7 +837,7 @@ export function setupComponent(
|
|||
vi(instance)
|
||||
} else {
|
||||
initProps(instance, props, isStateful, isSSR)
|
||||
initSlots(instance, children, optimized)
|
||||
initSlots(instance, children, optimized || isSSR)
|
||||
}
|
||||
|
||||
const setupResult = isStateful
|
||||
|
|
|
@ -17,7 +17,11 @@ import {
|
|||
} from '@vue/shared'
|
||||
import { warn } from './warning'
|
||||
import { isKeepAlive } from './components/KeepAlive'
|
||||
import { type ContextualRenderFn, withCtx } from './componentRenderContext'
|
||||
import {
|
||||
type ContextualRenderFn,
|
||||
currentRenderingInstance,
|
||||
withCtx,
|
||||
} from './componentRenderContext'
|
||||
import { isHmrUpdating } from './hmr'
|
||||
import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
|
||||
import { TriggerOpTypes, trigger } from '@vue/reactivity'
|
||||
|
@ -75,6 +79,11 @@ export type RawSlots = {
|
|||
* @internal
|
||||
*/
|
||||
_?: SlotFlags
|
||||
/**
|
||||
* cache indexes for slot content
|
||||
* @internal
|
||||
*/
|
||||
__?: number[]
|
||||
}
|
||||
|
||||
const isInternalKey = (key: string) => key[0] === '_' || key === '$stable'
|
||||
|
@ -98,7 +107,8 @@ const normalizeSlot = (
|
|||
__DEV__ &&
|
||||
currentInstance &&
|
||||
!currentInstance.vapor &&
|
||||
(!ctx || ctx.root === (currentInstance as ComponentInternalInstance).root)
|
||||
!(ctx === null && currentRenderingInstance) &&
|
||||
!(ctx && ctx.root !== currentInstance.root)
|
||||
) {
|
||||
warn(
|
||||
`Slot "${key}" invoked outside of the render function: ` +
|
||||
|
@ -171,7 +181,7 @@ const assignSlots = (
|
|||
// when rendering the optimized slots by manually written render function,
|
||||
// do not copy the `slots._` compiler flag so that `renderSlot` creates
|
||||
// slot Fragment with BAIL patchFlag to force full updates
|
||||
if (optimized || key !== '_') {
|
||||
if (optimized || !isInternalKey(key)) {
|
||||
slots[key] = children[key]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -501,9 +501,8 @@ function getInnerChild(vnode: VNode): VNode | undefined {
|
|||
|
||||
return vnode
|
||||
}
|
||||
// #7121 ensure get the child component subtree in case
|
||||
// it's been replaced during HMR
|
||||
if (__DEV__ && vnode.component) {
|
||||
// #7121,#12465 get the component subtree if it's been mounted
|
||||
if (vnode.component) {
|
||||
return vnode.component.subTree
|
||||
}
|
||||
|
||||
|
|
|
@ -220,7 +220,8 @@ export const TeleportImpl = {
|
|||
// even in block tree mode we need to make sure all root-level nodes
|
||||
// in the teleport inherit previous DOM references so that they can
|
||||
// be moved in future patches.
|
||||
traverseStaticChildren(n1, n2, true)
|
||||
// in dev mode, deep traversal is necessary for HMR
|
||||
traverseStaticChildren(n1, n2, !__DEV__)
|
||||
} else if (!optimized) {
|
||||
patchChildren(
|
||||
n1,
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import type { VNode, VNodeChild } from '../vnode'
|
||||
import {
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isShallow,
|
||||
shallowReadArray,
|
||||
toReactive,
|
||||
toReadonly,
|
||||
} from '@vue/reactivity'
|
||||
import { isArray, isObject, isString } from '@vue/shared'
|
||||
import { warn } from '../warning'
|
||||
|
@ -69,14 +71,20 @@ export function renderList(
|
|||
if (sourceIsArray || isString(source)) {
|
||||
const sourceIsReactiveArray = sourceIsArray && isReactive(source)
|
||||
let needsWrap = false
|
||||
let isReadonlySource = false
|
||||
if (sourceIsReactiveArray) {
|
||||
needsWrap = !isShallow(source)
|
||||
isReadonlySource = isReadonly(source)
|
||||
source = shallowReadArray(source)
|
||||
}
|
||||
ret = new Array(source.length)
|
||||
for (let i = 0, l = source.length; i < l; i++) {
|
||||
ret[i] = renderItem(
|
||||
needsWrap ? toReactive(source[i]) : source[i],
|
||||
needsWrap
|
||||
? isReadonlySource
|
||||
? toReadonly(toReactive(source[i]))
|
||||
: toReactive(source[i])
|
||||
: source[i],
|
||||
i,
|
||||
undefined,
|
||||
cached && cached[i],
|
||||
|
|
|
@ -1248,12 +1248,12 @@ function baseCreateRenderer(
|
|||
}
|
||||
}
|
||||
|
||||
// setup() is async. This component relies on async logic to be resolved
|
||||
// before proceeding
|
||||
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
|
||||
// avoid hydration for hmr updating
|
||||
if (__DEV__ && isHmrUpdating) initialVNode.el = null
|
||||
|
||||
// setup() is async. This component relies on async logic to be resolved
|
||||
// before proceeding
|
||||
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
|
||||
parentSuspense &&
|
||||
parentSuspense.registerDep(instance, setupRenderEffect, optimized)
|
||||
|
||||
|
@ -2169,7 +2169,9 @@ function baseCreateRenderer(
|
|||
|
||||
// unset ref
|
||||
if (ref != null) {
|
||||
pauseTracking()
|
||||
setRef(ref, null, parentSuspense, vnode, true)
|
||||
resetTracking()
|
||||
}
|
||||
|
||||
// #6593 should clean memo cache when unmount
|
||||
|
@ -2343,7 +2345,17 @@ function baseCreateRenderer(
|
|||
unregisterHMR(instance)
|
||||
}
|
||||
|
||||
const { bum, scope, job, subTree, um, m, a } = instance
|
||||
const {
|
||||
bum,
|
||||
scope,
|
||||
job,
|
||||
subTree,
|
||||
um,
|
||||
m,
|
||||
a,
|
||||
parent,
|
||||
slots: { __: slotCacheKeys },
|
||||
} = instance
|
||||
invalidateMount(m)
|
||||
invalidateMount(a)
|
||||
|
||||
|
@ -2352,6 +2364,13 @@ function baseCreateRenderer(
|
|||
invokeArrayFns(bum)
|
||||
}
|
||||
|
||||
// remove slots content from parent renderCache
|
||||
if (parent && isArray(slotCacheKeys)) {
|
||||
slotCacheKeys.forEach(v => {
|
||||
;(parent as ComponentInternalInstance).renderCache[v] = undefined
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
__COMPAT__ &&
|
||||
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
|
||||
|
@ -2502,9 +2521,12 @@ function baseCreateRenderer(
|
|||
// HMR root reload
|
||||
if (__DEV__) {
|
||||
app._context.reload = () => {
|
||||
const cloned = cloneVNode(vnode)
|
||||
// avoid hydration for hmr updating
|
||||
cloned.el = null
|
||||
// casting to ElementNamespace because TS doesn't guarantee type narrowing
|
||||
// over function boundaries
|
||||
render(cloneVNode(vnode), container, namespace as ElementNamespace)
|
||||
render(cloned, container, namespace as ElementNamespace)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2610,11 +2632,15 @@ export function traverseStaticChildren(
|
|||
if (c2.type === Text) {
|
||||
c2.el = c1.el
|
||||
}
|
||||
// also inherit for comment nodes, but not placeholders (e.g. v-if which
|
||||
// #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which
|
||||
// would have received .el during block patch)
|
||||
if (__DEV__ && c2.type === Comment && !c2.el) {
|
||||
if (c2.type === Comment && !c2.el) {
|
||||
c2.el = c1.el
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
c2.el && (c2.el.__vnode = c2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-dom",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/runtime-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-dom.esm-bundler.js",
|
||||
|
|
|
@ -82,6 +82,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
|
|||
moveClass,
|
||||
)
|
||||
) {
|
||||
prevChildren = []
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -111,6 +112,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
|
|||
})
|
||||
el.addEventListener('transitionend', cb)
|
||||
})
|
||||
prevChildren = []
|
||||
})
|
||||
|
||||
return () => {
|
||||
|
|
|
@ -322,4 +322,43 @@ describe('attribute fallthrough', () => {
|
|||
expect(el.getAttribute('aria-x')).toBe(parentVal.value)
|
||||
expect(el.getAttribute('aria-y')).toBe(parentVal.value)
|
||||
})
|
||||
|
||||
it('empty string should not be passed to classList.add', async () => {
|
||||
const t0 = template('<div>', true /* root */)
|
||||
const Child = defineVaporComponent({
|
||||
setup() {
|
||||
const n = t0() as Element
|
||||
renderEffect(() => {
|
||||
setClass(n, {
|
||||
foo: false,
|
||||
})
|
||||
})
|
||||
return n
|
||||
},
|
||||
})
|
||||
|
||||
const Parent = defineVaporComponent({
|
||||
setup() {
|
||||
return createComponent(
|
||||
Child,
|
||||
{
|
||||
class: () => ({
|
||||
bar: false,
|
||||
}),
|
||||
},
|
||||
null,
|
||||
true,
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const { host } = define({
|
||||
setup() {
|
||||
return createComponent(Parent)
|
||||
},
|
||||
}).render()
|
||||
|
||||
const el = host.children[0]
|
||||
expect(el.classList.length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import type { NodeRef } from '../../src/apiTemplateRef'
|
||||
import {
|
||||
child,
|
||||
createComponent,
|
||||
createDynamicComponent,
|
||||
createFor,
|
||||
createIf,
|
||||
createSlot,
|
||||
createTemplateRefSetter,
|
||||
defineVaporComponent,
|
||||
insert,
|
||||
renderEffect,
|
||||
template,
|
||||
|
@ -19,7 +22,8 @@ import {
|
|||
useTemplateRef,
|
||||
watchEffect,
|
||||
} from '@vue/runtime-dom'
|
||||
import { setElementText } from '../../src/dom/prop'
|
||||
import { setElementText, setText } from '../../src/dom/prop'
|
||||
import type { VaporComponent } from '../../src/component'
|
||||
|
||||
const define = makeRender()
|
||||
|
||||
|
@ -676,6 +680,39 @@ describe('api: template ref', () => {
|
|||
expect(r!.value).toBe(n)
|
||||
})
|
||||
|
||||
test('work with dynamic component', async () => {
|
||||
const Child = defineVaporComponent({
|
||||
setup(_, { expose }) {
|
||||
const msg = ref('one')
|
||||
expose({ setMsg: (m: string) => (msg.value = m) })
|
||||
const n0 = template(`<div> </div>`)() as any
|
||||
const x0 = child(n0) as any
|
||||
renderEffect(() => setText(x0, msg.value))
|
||||
return n0
|
||||
},
|
||||
})
|
||||
|
||||
const views: Record<string, VaporComponent> = { child: Child }
|
||||
const view = ref('child')
|
||||
const refKey = ref<any>(null)
|
||||
|
||||
const { html } = define({
|
||||
setup() {
|
||||
const setRef = createTemplateRefSetter()
|
||||
const n0 = createDynamicComponent(() => views[view.value]) as any
|
||||
setRef(n0, refKey)
|
||||
return n0
|
||||
},
|
||||
}).render()
|
||||
|
||||
expect(html()).toBe('<div>one</div><!--dynamic-component-->')
|
||||
expect(refKey.value).toBeDefined()
|
||||
|
||||
refKey.value.setMsg('changed')
|
||||
await nextTick()
|
||||
expect(html()).toBe('<div>changed</div><!--dynamic-component-->')
|
||||
})
|
||||
|
||||
// TODO: can not reproduce in Vapor
|
||||
// // #2078
|
||||
// test('handling multiple merged refs', async () => {
|
||||
|
|
|
@ -4,7 +4,14 @@ import {
|
|||
getRestElement,
|
||||
renderEffect,
|
||||
} from '../src'
|
||||
import { nextTick, ref, shallowRef, triggerRef } from '@vue/runtime-dom'
|
||||
import {
|
||||
nextTick,
|
||||
reactive,
|
||||
readonly,
|
||||
ref,
|
||||
shallowRef,
|
||||
triggerRef,
|
||||
} from '@vue/runtime-dom'
|
||||
import { makeRender } from './_utils'
|
||||
|
||||
const define = makeRender()
|
||||
|
@ -674,4 +681,57 @@ describe('createFor', () => {
|
|||
await nextTick()
|
||||
expectCalledTimesToBe('Clear rows', 1, 0, 0, 0)
|
||||
})
|
||||
|
||||
describe('readonly source', () => {
|
||||
test('should not allow mutation', () => {
|
||||
const arr = readonly(reactive([{ foo: 1 }]))
|
||||
|
||||
const { host } = define(() => {
|
||||
const n1 = createFor(
|
||||
() => arr,
|
||||
(item, key, index) => {
|
||||
const span = document.createElement('li')
|
||||
renderEffect(() => {
|
||||
item.value.foo = 0
|
||||
span.innerHTML = `${item.value.foo}`
|
||||
})
|
||||
return span
|
||||
},
|
||||
idx => idx,
|
||||
)
|
||||
return n1
|
||||
}).render()
|
||||
|
||||
expect(host.innerHTML).toBe('<li>1</li><!--for-->')
|
||||
expect(
|
||||
`Set operation on key "foo" failed: target is readonly.`,
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should trigger effect for deep mutations', async () => {
|
||||
const arr = reactive([{ foo: 1 }])
|
||||
const readonlyArr = readonly(arr)
|
||||
|
||||
const { host } = define(() => {
|
||||
const n1 = createFor(
|
||||
() => readonlyArr,
|
||||
(item, key, index) => {
|
||||
const span = document.createElement('li')
|
||||
renderEffect(() => {
|
||||
span.innerHTML = `${item.value.foo}`
|
||||
})
|
||||
return span
|
||||
},
|
||||
idx => idx,
|
||||
)
|
||||
return n1
|
||||
}).render()
|
||||
|
||||
expect(host.innerHTML).toBe('<li>1</li><!--for-->')
|
||||
|
||||
arr[0].foo = 2
|
||||
await nextTick()
|
||||
expect(host.innerHTML).toBe('<li>2</li><!--for-->')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,12 +2,14 @@ import {
|
|||
EffectScope,
|
||||
type ShallowRef,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isShallow,
|
||||
pauseTracking,
|
||||
resetTracking,
|
||||
shallowReadArray,
|
||||
shallowRef,
|
||||
toReactive,
|
||||
toReadonly,
|
||||
} from '@vue/reactivity'
|
||||
import { getSequence, isArray, isObject, isString } from '@vue/shared'
|
||||
import { createComment, createTextNode } from './dom/node'
|
||||
|
@ -23,7 +25,11 @@ import type { DynamicSlot } from './componentSlots'
|
|||
import { renderEffect } from './renderEffect'
|
||||
import { VaporVForFlags } from '../../shared/src/vaporFlags'
|
||||
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
||||
import { insertionAnchor, insertionParent } from './insertionState'
|
||||
import {
|
||||
insertionAnchor,
|
||||
insertionParent,
|
||||
resetInsertionState,
|
||||
} from './insertionState'
|
||||
|
||||
class ForBlock extends VaporFragment {
|
||||
scope: EffectScope | undefined
|
||||
|
@ -55,6 +61,7 @@ type Source = any[] | Record<any, any> | number | Set<any> | Map<any, any>
|
|||
type ResolvedSource = {
|
||||
values: any[]
|
||||
needsWrap: boolean
|
||||
isReadonlySource: boolean
|
||||
keys?: string[]
|
||||
}
|
||||
|
||||
|
@ -72,6 +79,8 @@ export const createFor = (
|
|||
const _insertionAnchor = insertionAnchor
|
||||
if (isHydrating) {
|
||||
locateHydrationNode()
|
||||
} else {
|
||||
resetInsertionState()
|
||||
}
|
||||
|
||||
let isMounted = false
|
||||
|
@ -387,11 +396,13 @@ export function createForSlots(
|
|||
function normalizeSource(source: any): ResolvedSource {
|
||||
let values = source
|
||||
let needsWrap = false
|
||||
let isReadonlySource = false
|
||||
let keys
|
||||
if (isArray(source)) {
|
||||
if (isReactive(source)) {
|
||||
needsWrap = !isShallow(source)
|
||||
values = shallowReadArray(source)
|
||||
isReadonlySource = isReadonly(source)
|
||||
}
|
||||
} else if (isString(source)) {
|
||||
values = source.split('')
|
||||
|
@ -412,14 +423,23 @@ function normalizeSource(source: any): ResolvedSource {
|
|||
}
|
||||
}
|
||||
}
|
||||
return { values, needsWrap, keys }
|
||||
return {
|
||||
values,
|
||||
needsWrap,
|
||||
isReadonlySource,
|
||||
keys,
|
||||
}
|
||||
}
|
||||
|
||||
function getItem(
|
||||
{ keys, values, needsWrap }: ResolvedSource,
|
||||
{ keys, values, needsWrap, isReadonlySource }: ResolvedSource,
|
||||
idx: number,
|
||||
): [item: any, key: any, index?: number] {
|
||||
const value = needsWrap ? toReactive(values[idx]) : values[idx]
|
||||
const value = needsWrap
|
||||
? isReadonlySource
|
||||
? toReadonly(toReactive(values[idx]))
|
||||
: toReactive(values[idx])
|
||||
: values[idx]
|
||||
if (keys) {
|
||||
return [value, keys[idx], idx]
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { type Block, type BlockFn, DynamicFragment, insert } from './block'
|
||||
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
||||
import { insertionAnchor, insertionParent } from './insertionState'
|
||||
import {
|
||||
insertionAnchor,
|
||||
insertionParent,
|
||||
resetInsertionState,
|
||||
} from './insertionState'
|
||||
import { renderEffect } from './renderEffect'
|
||||
|
||||
export function createIf(
|
||||
|
@ -13,6 +17,8 @@ export function createIf(
|
|||
const _insertionAnchor = insertionAnchor
|
||||
if (isHydrating) {
|
||||
locateHydrationNode()
|
||||
} else {
|
||||
resetInsertionState()
|
||||
}
|
||||
|
||||
let frag: Block
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
isString,
|
||||
remove,
|
||||
} from '@vue/shared'
|
||||
import { DynamicFragment } from './block'
|
||||
|
||||
export type NodeRef = string | Ref | ((ref: Element) => void)
|
||||
export type RefEl = Element | VaporComponentInstance
|
||||
|
@ -49,7 +50,7 @@ export function setRef(
|
|||
if (!instance || instance.isUnmounted) return
|
||||
|
||||
const setupState: any = __DEV__ ? instance.setupState || {} : null
|
||||
const refValue = isVaporComponent(el) ? getExposed(el) || el : el
|
||||
const refValue = getRefValue(el)
|
||||
|
||||
const refs =
|
||||
instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs
|
||||
|
@ -143,3 +144,12 @@ export function setRef(
|
|||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
const getRefValue = (el: RefEl) => {
|
||||
if (isVaporComponent(el)) {
|
||||
return getExposed(el) || el
|
||||
} else if (el instanceof DynamicFragment) {
|
||||
return getRefValue(el.nodes as RefEl)
|
||||
}
|
||||
return el
|
||||
}
|
||||
|
|
|
@ -59,7 +59,11 @@ import {
|
|||
} from './componentSlots'
|
||||
import { hmrReload, hmrRerender } from './hmr'
|
||||
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
||||
import { insertionAnchor, insertionParent } from './insertionState'
|
||||
import {
|
||||
insertionAnchor,
|
||||
insertionParent,
|
||||
resetInsertionState,
|
||||
} from './insertionState'
|
||||
|
||||
export { currentInstance } from '@vue/runtime-dom'
|
||||
|
||||
|
@ -142,6 +146,8 @@ export function createComponent(
|
|||
const _insertionAnchor = insertionAnchor
|
||||
if (isHydrating) {
|
||||
locateHydrationNode()
|
||||
} else {
|
||||
resetInsertionState()
|
||||
}
|
||||
|
||||
// vdom interop enabled and component is not an explicit vapor component
|
||||
|
|
|
@ -4,7 +4,11 @@ import { rawPropsProxyHandlers } from './componentProps'
|
|||
import { currentInstance, isRef } from '@vue/runtime-dom'
|
||||
import type { LooseRawProps, VaporComponentInstance } from './component'
|
||||
import { renderEffect } from './renderEffect'
|
||||
import { insertionAnchor, insertionParent } from './insertionState'
|
||||
import {
|
||||
insertionAnchor,
|
||||
insertionParent,
|
||||
resetInsertionState,
|
||||
} from './insertionState'
|
||||
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
||||
|
||||
export type RawSlots = Record<string, VaporSlot> & {
|
||||
|
@ -96,6 +100,8 @@ export function createSlot(
|
|||
const _insertionAnchor = insertionAnchor
|
||||
if (isHydrating) {
|
||||
locateHydrationNode()
|
||||
} else {
|
||||
resetInsertionState()
|
||||
}
|
||||
|
||||
const instance = currentInstance as VaporComponentInstance
|
||||
|
|
|
@ -122,7 +122,9 @@ function setClassIncremental(el: any, value: any): void {
|
|||
const prev = el[cacheKey]
|
||||
if ((value = el[cacheKey] = normalizeClass(value)) !== prev) {
|
||||
const nextList = value.split(/\s+/)
|
||||
if (value) {
|
||||
el.classList.add(...nextList)
|
||||
}
|
||||
if (prev) {
|
||||
for (const cls of prev.split(/\s+/)) {
|
||||
if (!nextList.includes(cls)) el.classList.remove(cls)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createApp } from 'vue'
|
||||
import { createApp, defineAsyncComponent, h } from 'vue'
|
||||
import { renderToString } from '../src/renderToString'
|
||||
|
||||
const components = {
|
||||
|
@ -154,6 +154,38 @@ describe('ssr: slot', () => {
|
|||
).toBe(`<div><p>1</p><p>2</p></div>`)
|
||||
})
|
||||
|
||||
// #12438
|
||||
test('async component slot with v-if true', async () => {
|
||||
const Layout = defineAsyncComponent(() =>
|
||||
Promise.resolve({
|
||||
template: `<div><slot name="header">default header</slot></div>`,
|
||||
}),
|
||||
)
|
||||
const LayoutLoader = {
|
||||
setup(_: any, context: any) {
|
||||
return () => h(Layout, {}, context.slots)
|
||||
},
|
||||
}
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components: {
|
||||
LayoutLoader,
|
||||
},
|
||||
template: `
|
||||
<Suspense>
|
||||
<LayoutLoader>
|
||||
<template v-if="true" #header>
|
||||
new header
|
||||
</template>
|
||||
</LayoutLoader>
|
||||
</Suspense>
|
||||
`,
|
||||
}),
|
||||
),
|
||||
).toBe(`<div><!--[--> new header <!--]--></div>`)
|
||||
})
|
||||
|
||||
// #11326
|
||||
test('dynamic component slot', async () => {
|
||||
expect(
|
||||
|
|
|
@ -49,7 +49,7 @@ test('pipeToWebWritable', async () => {
|
|||
}
|
||||
|
||||
const { readable, writable } = new TransformStream()
|
||||
pipeToWebWritable(createApp(App), {}, writable)
|
||||
pipeToWebWritable(createApp(App), {}, writable as any)
|
||||
|
||||
const reader = readable.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/server-renderer",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "@vue/server-renderer",
|
||||
"main": "index.js",
|
||||
"module": "dist/server-renderer.esm-bundler.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/shared",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "internal utils shared across @vue packages",
|
||||
"main": "index.js",
|
||||
"module": "dist/shared.esm-bundler.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compat",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "Vue 3 compatibility build for Vue 2",
|
||||
"main": "index.js",
|
||||
"module": "dist/vue.runtime.esm-bundler.js",
|
||||
|
|
|
@ -647,4 +647,55 @@ describe('e2e: TransitionGroup', () => {
|
|||
},
|
||||
E2E_TIMEOUT,
|
||||
)
|
||||
|
||||
test(
|
||||
'not leaking after children unmounted',
|
||||
async () => {
|
||||
const client = await page().createCDPSession()
|
||||
await page().evaluate(async () => {
|
||||
const { createApp, ref, nextTick } = (window as any).Vue
|
||||
const show = ref(true)
|
||||
|
||||
createApp({
|
||||
components: {
|
||||
Child: {
|
||||
setup: () => {
|
||||
// Big arrays kick GC earlier
|
||||
const test = ref([...Array(3000)].map((_, i) => ({ i })))
|
||||
// @ts-expect-error - Custom property and same lib as runtime is used
|
||||
window.__REF__ = new WeakRef(test)
|
||||
|
||||
return { test }
|
||||
},
|
||||
template: `
|
||||
<p>{{ test.length }}</p>
|
||||
`,
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<transition-group>
|
||||
<Child v-if="show" />
|
||||
</transition-group>
|
||||
`,
|
||||
setup() {
|
||||
return { show }
|
||||
},
|
||||
}).mount('#app')
|
||||
|
||||
show.value = false
|
||||
await nextTick()
|
||||
})
|
||||
|
||||
const isCollected = async () =>
|
||||
// @ts-expect-error - Custom property
|
||||
await page().evaluate(() => window.__REF__.deref() === undefined)
|
||||
|
||||
while ((await isCollected()) === false) {
|
||||
await client.send('HeapProfiler.collectGarbage')
|
||||
}
|
||||
|
||||
expect(await isCollected()).toBe(true)
|
||||
},
|
||||
E2E_TIMEOUT,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vue",
|
||||
"version": "3.5.13",
|
||||
"version": "3.5.14",
|
||||
"description": "The progressive JavaScript framework for building modern web UI.",
|
||||
"main": "index.js",
|
||||
"module": "dist/vue.runtime.esm-bundler.js",
|
||||
|
|
3237
pnpm-lock.yaml
3237
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -3,10 +3,25 @@ packages:
|
|||
- 'packages-private/*'
|
||||
|
||||
catalog:
|
||||
'@babel/parser': ^7.27.0
|
||||
'@babel/types': ^7.27.0
|
||||
'@babel/parser': ^7.27.2
|
||||
'@babel/types': ^7.27.1
|
||||
'estree-walker': ^2.0.2
|
||||
'vite': ^6.1.0
|
||||
'@vitejs/plugin-vue': https://pkg.pr.new/@vitejs/plugin-vue@c156992
|
||||
'@vitejs/plugin-vue': ^5.2.4
|
||||
'magic-string': ^0.30.17
|
||||
'source-map-js': ^1.2.1
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- '@swc/core'
|
||||
- 'esbuild'
|
||||
- 'puppeteer'
|
||||
- 'simple-git-hooks'
|
||||
- 'unrs-resolver'
|
||||
|
||||
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'
|
||||
|
|
Loading…
Reference in New Issue