mirror of https://github.com/vuejs/core.git
chore: Merge branch 'main' into minor
This commit is contained in:
commit
7d06ca3a83
|
@ -16,11 +16,12 @@ jobs:
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.0.0
|
||||||
|
|
||||||
- name: Set node version to 18
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version-file: '.node-version'
|
||||||
cache: pnpm
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
- run: pnpm install
|
- run: pnpm install
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,10 @@ jobs:
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4.0.0
|
uses: pnpm/action-setup@v4.0.0
|
||||||
|
|
||||||
- name: Set node version to 18
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version-file: '.node-version'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,3 +1,24 @@
|
||||||
|
## [3.4.30](https://github.com/vuejs/core/compare/v3.4.29...v3.4.30) (2024-06-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-core:** should not remove slot node with `v-else` ([#11150](https://github.com/vuejs/core/issues/11150)) ([e102670](https://github.com/vuejs/core/commit/e102670bde00417c3a5b0262c855b297c0e4169e))
|
||||||
|
* **hydration:** fix css vars hydration mismatch false positive on attr-fallthrough ([#11190](https://github.com/vuejs/core/issues/11190)) ([7ad67ce](https://github.com/vuejs/core/commit/7ad67ced26e5f53a47cb42f4834496e4958cb53b)), closes [#11188](https://github.com/vuejs/core/issues/11188)
|
||||||
|
* **hydration:** skip prop mismatch check for directives that mutate DOM in created ([3169c91](https://github.com/vuejs/core/commit/3169c914939d02a013b2938aff30dac8525923f8)), closes [#11189](https://github.com/vuejs/core/issues/11189)
|
||||||
|
* **reactivity:** fix side effect computed dirty level ([#11183](https://github.com/vuejs/core/issues/11183)) ([3bd79e3](https://github.com/vuejs/core/commit/3bd79e3e5ed960fc42cbf77bc61a97d2c03557c0)), closes [#11181](https://github.com/vuejs/core/issues/11181) [#11169](https://github.com/vuejs/core/issues/11169)
|
||||||
|
* **runtime-core:** ensure unmount dynamic components in optimized mode ([#11171](https://github.com/vuejs/core/issues/11171)) ([220fe24](https://github.com/vuejs/core/commit/220fe247484209e62c7f4991902c5335e29c5007)), closes [#11168](https://github.com/vuejs/core/issues/11168)
|
||||||
|
* **runtime-core:** update devtool __vnode on patch, avoid memory leak during dev ([a959781](https://github.com/vuejs/core/commit/a959781dd6f609dcb6f16dd7fa47d3b16895e5ca)), closes [#11192](https://github.com/vuejs/core/issues/11192)
|
||||||
|
* **runtime-dom:** ensure only symbols are explicitly stringified during attribute patching ([#11182](https://github.com/vuejs/core/issues/11182)) ([a2e35d6](https://github.com/vuejs/core/commit/a2e35d682db15a592f4270bb0cde70a0e7bdc4a6)), closes [#11177](https://github.com/vuejs/core/issues/11177)
|
||||||
|
* **runtime-dom:** prevent setting state as attribute for custom elements ([#11165](https://github.com/vuejs/core/issues/11165)) ([8ae4c29](https://github.com/vuejs/core/commit/8ae4c293adcec28f18114cb6016230a86787e6a9)), closes [#11163](https://github.com/vuejs/core/issues/11163)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **reactivity:** cache tracking value ([#11145](https://github.com/vuejs/core/issues/11145)) ([7936dae](https://github.com/vuejs/core/commit/7936daebceab2ae9461c3b8f256e51020fb7d3ed))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [3.4.29](https://github.com/vuejs/core/compare/v3.4.28...v3.4.29) (2024-06-14)
|
## [3.4.29](https://github.com/vuejs/core/compare/v3.4.28...v3.4.29) (2024-06-14)
|
||||||
|
|
||||||
|
|
||||||
|
|
24
package.json
24
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "3.5.0-alpha.2",
|
"version": "3.5.0-alpha.2",
|
||||||
"packageManager": "pnpm@9.2.0",
|
"packageManager": "pnpm@9.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/dev.js",
|
"dev": "node scripts/dev.js",
|
||||||
|
@ -67,24 +67,24 @@
|
||||||
"@rollup/plugin-json": "^6.1.0",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@rollup/plugin-replace": "5.0.4",
|
"@rollup/plugin-replace": "5.0.4",
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@swc/core": "^1.6.1",
|
||||||
"@types/hash-sum": "^1.0.2",
|
"@types/hash-sum": "^1.0.2",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/node": "^20.14.2",
|
"@types/node": "^20.14.2",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.5.8",
|
||||||
"@vitest/coverage-istanbul": "^1.5.2",
|
"@vitest/coverage-istanbul": "^1.6.0",
|
||||||
"@vue/consolidate": "1.0.0",
|
"@vue/consolidate": "1.0.0",
|
||||||
"conventional-changelog-cli": "^4.1.0",
|
"conventional-changelog-cli": "^4.1.0",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
"esbuild": "^0.21.5",
|
"esbuild": "^0.21.5",
|
||||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||||
"eslint": "^9.4.0",
|
"eslint": "^9.5.0",
|
||||||
"eslint-plugin-import-x": "^0.5.1",
|
"eslint-plugin-import-x": "^0.5.1",
|
||||||
"eslint-plugin-vitest": "^0.5.4",
|
"eslint-plugin-vitest": "^0.5.4",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"execa": "^9.2.0",
|
"execa": "^9.2.0",
|
||||||
"jsdom": "^24.0.0",
|
"jsdom": "^24.1.0",
|
||||||
"lint-staged": "^15.2.5",
|
"lint-staged": "^15.2.7",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"magic-string": "^0.30.10",
|
"magic-string": "^0.30.10",
|
||||||
"markdown-table": "^3.0.3",
|
"markdown-table": "^3.0.3",
|
||||||
|
@ -92,10 +92,10 @@
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
"npm-run-all2": "^6.2.0",
|
"npm-run-all2": "^6.2.0",
|
||||||
"picocolors": "^1.0.1",
|
"picocolors": "^1.0.1",
|
||||||
"prettier": "^3.3.1",
|
"prettier": "^3.3.2",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"puppeteer": "~22.7.1",
|
"puppeteer": "~22.11.0",
|
||||||
"rimraf": "^5.0.7",
|
"rimraf": "^5.0.7",
|
||||||
"rollup": "^4.18.0",
|
"rollup": "^4.18.0",
|
||||||
"rollup-plugin-dts": "^6.1.1",
|
"rollup-plugin-dts": "^6.1.1",
|
||||||
|
@ -107,11 +107,11 @@
|
||||||
"terser": "^5.31.1",
|
"terser": "^5.31.1",
|
||||||
"todomvc-app-css": "^2.4.3",
|
"todomvc-app-css": "^2.4.3",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
"tsx": "^4.15.1",
|
"tsx": "^4.15.5",
|
||||||
"typescript": "~5.4.5",
|
"typescript": "~5.4.5",
|
||||||
"typescript-eslint": "^7.12.0",
|
"typescript-eslint": "^7.13.0",
|
||||||
"vite": "^5.2.13",
|
"vite": "^5.3.1",
|
||||||
"vitest": "^1.5.2"
|
"vitest": "^1.6.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
|
|
|
@ -696,6 +696,7 @@ describe('compiler: transform component slots', () => {
|
||||||
expect((root as any).children[0].codegenNode.patchFlag).toMatch(
|
expect((root as any).children[0].codegenNode.patchFlag).toMatch(
|
||||||
PatchFlags.DYNAMIC_SLOTS + '',
|
PatchFlags.DYNAMIC_SLOTS + '',
|
||||||
)
|
)
|
||||||
|
expect((root as any).children[0].children.length).toBe(3)
|
||||||
expect(generate(root).code).toMatchSnapshot()
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,7 @@ export const transformFilter: NodeTransform = (node, context) => {
|
||||||
// filter rewrite is applied before expression transform so only
|
// filter rewrite is applied before expression transform so only
|
||||||
// simple expressions are possible at this stage
|
// simple expressions are possible at this stage
|
||||||
rewriteFilter(node.content, context)
|
rewriteFilter(node.content, context)
|
||||||
}
|
} else if (node.type === NodeTypes.ELEMENT) {
|
||||||
|
|
||||||
if (node.type === NodeTypes.ELEMENT) {
|
|
||||||
node.props.forEach((prop: AttributeNode | DirectiveNode) => {
|
node.props.forEach((prop: AttributeNode | DirectiveNode) => {
|
||||||
if (
|
if (
|
||||||
prop.type === NodeTypes.DIRECTIVE &&
|
prop.type === NodeTypes.DIRECTIVE &&
|
||||||
|
|
|
@ -226,10 +226,7 @@ export function buildSlots(
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {
|
if (prev && isTemplateNode(prev) && findDir(prev, /^(else-)?if$/)) {
|
||||||
// remove node
|
|
||||||
children.splice(i, 1)
|
|
||||||
i--
|
|
||||||
__TEST__ && assert(dynamicSlots.length > 0)
|
__TEST__ && assert(dynamicSlots.length > 0)
|
||||||
// attach this slot to previous conditional
|
// attach this slot to previous conditional
|
||||||
let conditional = dynamicSlots[
|
let conditional = dynamicSlots[
|
||||||
|
|
|
@ -62,6 +62,6 @@
|
||||||
"postcss-modules": "^6.0.0",
|
"postcss-modules": "^6.0.0",
|
||||||
"postcss-selector-parser": "^6.1.0",
|
"postcss-selector-parser": "^6.1.0",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"sass": "^1.77.4"
|
"sass": "^1.77.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -853,6 +853,57 @@ describe('reactivity/computed', () => {
|
||||||
expect(calls).toMatchObject(['b eval', 'mounted', 'b eval'])
|
expect(calls).toMatchObject(['b eval', 'mounted', 'b eval'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should chained computeds keep reactivity when computed effect happens', async () => {
|
||||||
|
const v = ref('Hello')
|
||||||
|
const c = computed(() => {
|
||||||
|
v.value += ' World'
|
||||||
|
return v.value
|
||||||
|
})
|
||||||
|
const d = computed(() => c.value)
|
||||||
|
const e = computed(() => d.value)
|
||||||
|
const Comp = {
|
||||||
|
setup: () => {
|
||||||
|
return () => d.value + ' | ' + e.value
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
|
||||||
|
render(h(Comp), root)
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe('Hello World | Hello World')
|
||||||
|
|
||||||
|
v.value += ' World'
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(
|
||||||
|
'Hello World World World | Hello World World World',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should keep dirty level when side effect computed value changed', () => {
|
||||||
|
const v = ref(0)
|
||||||
|
const c = computed(() => {
|
||||||
|
v.value += 1
|
||||||
|
return v.value
|
||||||
|
})
|
||||||
|
const d = computed(() => {
|
||||||
|
return { d: c.value }
|
||||||
|
})
|
||||||
|
|
||||||
|
const Comp = {
|
||||||
|
setup: () => {
|
||||||
|
return () => {
|
||||||
|
return [d.value.d, d.value.d]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(Comp), root)
|
||||||
|
|
||||||
|
expect(d.value.d).toBe(1)
|
||||||
|
expect(serializeInner(root)).toBe('11')
|
||||||
|
})
|
||||||
|
|
||||||
it('debug: onTrigger (ref)', () => {
|
it('debug: onTrigger (ref)', () => {
|
||||||
let events: DebuggerEvent[] = []
|
let events: DebuggerEvent[] = []
|
||||||
const onTrigger = vi.fn((e: DebuggerEvent) => {
|
const onTrigger = vi.fn((e: DebuggerEvent) => {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
} from '../src/index'
|
} from '../src/index'
|
||||||
import { createApp, nodeOps, render, serialize } from '@vue/runtime-test'
|
import { createApp, nodeOps, render, serialize } from '@vue/runtime-test'
|
||||||
|
|
||||||
// reference: https://vue-composition-api-rfc.netlify.com/api.html#provide-inject
|
|
||||||
describe('api: provide/inject', () => {
|
describe('api: provide/inject', () => {
|
||||||
it('string keys', () => {
|
it('string keys', () => {
|
||||||
const Provider = {
|
const Provider = {
|
||||||
|
|
|
@ -24,8 +24,6 @@ import {
|
||||||
TriggerOpTypes,
|
TriggerOpTypes,
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
|
|
||||||
// reference: https://vue-composition-api-rfc.netlify.com/api.html#lifecycle-hooks
|
|
||||||
|
|
||||||
describe('api: lifecycle hooks', () => {
|
describe('api: lifecycle hooks', () => {
|
||||||
it('onBeforeMount', () => {
|
it('onBeforeMount', () => {
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
|
|
|
@ -12,8 +12,6 @@ import {
|
||||||
watchEffect,
|
watchEffect,
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
|
|
||||||
// reference: https://vue-composition-api-rfc.netlify.com/api.html#setup
|
|
||||||
|
|
||||||
describe('api: setup context', () => {
|
describe('api: setup context', () => {
|
||||||
it('should expose return values to template render context', () => {
|
it('should expose return values to template render context', () => {
|
||||||
const Comp = defineComponent({
|
const Comp = defineComponent({
|
||||||
|
|
|
@ -36,8 +36,6 @@ import {
|
||||||
triggerRef,
|
triggerRef,
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
|
|
||||||
// reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
|
|
||||||
|
|
||||||
describe('api: watch', () => {
|
describe('api: watch', () => {
|
||||||
it('effect', async () => {
|
it('effect', async () => {
|
||||||
const state = reactive({ count: 0 })
|
const state = reactive({ count: 0 })
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
type ObjectDirective,
|
||||||
Suspense,
|
Suspense,
|
||||||
Teleport,
|
Teleport,
|
||||||
Transition,
|
Transition,
|
||||||
|
@ -1674,5 +1675,45 @@ describe('SSR hydration', () => {
|
||||||
app.mount(container)
|
app.mount(container)
|
||||||
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
|
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #11188
|
||||||
|
test('css vars support fallthrough', () => {
|
||||||
|
const container = document.createElement('div')
|
||||||
|
container.innerHTML = `<div style="padding: 4px;--foo:red;"></div>`
|
||||||
|
const app = createSSRApp({
|
||||||
|
setup() {
|
||||||
|
useCssVars(() => ({
|
||||||
|
foo: 'red',
|
||||||
|
}))
|
||||||
|
return () => h(Child)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const Child = {
|
||||||
|
setup() {
|
||||||
|
return () => h('div', { style: 'padding: 4px' })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.mount(container)
|
||||||
|
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
|
||||||
|
// #11189
|
||||||
|
test('should not warn for directives that mutate DOM in created', () => {
|
||||||
|
const container = document.createElement('div')
|
||||||
|
container.innerHTML = `<div class="test red"></div>`
|
||||||
|
const vColor: ObjectDirective = {
|
||||||
|
created(el, binding) {
|
||||||
|
el.classList.add(binding.value)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const app = createSSRApp({
|
||||||
|
setup() {
|
||||||
|
return () =>
|
||||||
|
withDirectives(h('div', { class: 'test' }), [[vColor, 'red']])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
app.mount(container)
|
||||||
|
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -487,6 +487,32 @@ describe('renderer: optimized mode', () => {
|
||||||
expect(spy).toHaveBeenCalledTimes(1)
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should call onUnmounted hook for dynamic components receiving an existing vnode w/ component children', async () => {
|
||||||
|
const spy = vi.fn()
|
||||||
|
const show = ref(1)
|
||||||
|
const Child = {
|
||||||
|
setup() {
|
||||||
|
onUnmounted(spy)
|
||||||
|
return () => 'child'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const foo = h('div', null, h(Child))
|
||||||
|
const app = createApp({
|
||||||
|
render() {
|
||||||
|
return show.value
|
||||||
|
? (openBlock(),
|
||||||
|
createBlock('div', null, [(openBlock(), createBlock(foo))]))
|
||||||
|
: createCommentVNode('v-if', true)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
app.mount(root)
|
||||||
|
show.value = 0
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
// #2444
|
// #2444
|
||||||
// `KEYED_FRAGMENT` and `UNKEYED_FRAGMENT` always need to diff its children
|
// `KEYED_FRAGMENT` and `UNKEYED_FRAGMENT` always need to diff its children
|
||||||
test('non-stable Fragment always need to diff its children', () => {
|
test('non-stable Fragment always need to diff its children', () => {
|
||||||
|
|
|
@ -10,8 +10,6 @@ import {
|
||||||
shallowRef,
|
shallowRef,
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
|
|
||||||
// reference: https://vue-composition-api-rfc.netlify.com/api.html#template-refs
|
|
||||||
|
|
||||||
describe('api: template refs', () => {
|
describe('api: template refs', () => {
|
||||||
it('string ref mount', () => {
|
it('string ref mount', () => {
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
|
|
|
@ -123,7 +123,6 @@ export const SuspenseImpl = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hydrate: hydrateSuspense,
|
hydrate: hydrateSuspense,
|
||||||
create: createSuspenseBoundary,
|
|
||||||
normalize: normalizeSuspenseChildren,
|
normalize: normalizeSuspenseChildren,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -250,7 +250,6 @@ export const TeleportImpl = {
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
optimized: boolean,
|
|
||||||
{ um: unmount, o: { remove: hostRemove } }: RendererInternals,
|
{ um: unmount, o: { remove: hostRemove } }: RendererInternals,
|
||||||
doRemove: boolean,
|
doRemove: boolean,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { warn } from './warning'
|
||||||
import {
|
import {
|
||||||
PatchFlags,
|
PatchFlags,
|
||||||
ShapeFlags,
|
ShapeFlags,
|
||||||
|
def,
|
||||||
includeBooleanAttr,
|
includeBooleanAttr,
|
||||||
isBooleanAttr,
|
isBooleanAttr,
|
||||||
isKnownHtmlAttr,
|
isKnownHtmlAttr,
|
||||||
|
@ -141,18 +142,8 @@ export function createHydrationFunctions(
|
||||||
vnode.el = node
|
vnode.el = node
|
||||||
|
|
||||||
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
||||||
if (!('__vnode' in node)) {
|
def(node, '__vnode', vnode, true)
|
||||||
Object.defineProperty(node, '__vnode', {
|
def(node, '__vueParentComponent', parentComponent, true)
|
||||||
value: vnode,
|
|
||||||
enumerable: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (!('__vueParentComponent' in node)) {
|
|
||||||
Object.defineProperty(node, '__vueParentComponent', {
|
|
||||||
value: parentComponent,
|
|
||||||
enumerable: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patchFlag === PatchFlags.BAIL) {
|
if (patchFlag === PatchFlags.BAIL) {
|
||||||
|
@ -459,6 +450,9 @@ export function createHydrationFunctions(
|
||||||
// check hydration mismatch
|
// check hydration mismatch
|
||||||
if (
|
if (
|
||||||
(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
|
(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
|
||||||
|
// #11189 skip if this node has directives that have created hooks
|
||||||
|
// as it could have mutated the DOM in any possible way
|
||||||
|
!(dirs && dirs.some(d => d.dir.created)) &&
|
||||||
propHasMismatch(el, key, props[key], vnode, parentComponent)
|
propHasMismatch(el, key, props[key], vnode, parentComponent)
|
||||||
) {
|
) {
|
||||||
logMismatchError()
|
logMismatchError()
|
||||||
|
@ -766,18 +760,8 @@ function propHasMismatch(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
if (instance) {
|
||||||
const root = instance?.subTree
|
resolveCssVars(instance, vnode, expectedMap)
|
||||||
if (
|
|
||||||
vnode === root ||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
(root?.type === Fragment && (root.children as VNode[]).includes(vnode))
|
|
||||||
) {
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
const cssVars = instance?.getCssVars?.()
|
|
||||||
for (const key in cssVars) {
|
|
||||||
expectedMap.set(`--${key}`, String(cssVars[key]))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMapEqual(actualMap, expectedMap)) {
|
if (!isMapEqual(actualMap, expectedMap)) {
|
||||||
|
@ -854,10 +838,8 @@ function toStyleMap(str: string): Map<string, string> {
|
||||||
const styleMap: Map<string, string> = new Map()
|
const styleMap: Map<string, string> = new Map()
|
||||||
for (const item of str.split(';')) {
|
for (const item of str.split(';')) {
|
||||||
let [key, value] = item.split(':')
|
let [key, value] = item.split(':')
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
key = key.trim()
|
||||||
key = key?.trim()
|
value = value && value.trim()
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
value = value?.trim()
|
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
styleMap.set(key, value)
|
styleMap.set(key, value)
|
||||||
}
|
}
|
||||||
|
@ -876,3 +858,26 @@ function isMapEqual(a: Map<string, string>, b: Map<string, string>): boolean {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveCssVars(
|
||||||
|
instance: ComponentInternalInstance,
|
||||||
|
vnode: VNode,
|
||||||
|
expectedMap: Map<string, string>,
|
||||||
|
) {
|
||||||
|
const root = instance.subTree
|
||||||
|
if (
|
||||||
|
instance.getCssVars &&
|
||||||
|
(vnode === root ||
|
||||||
|
(root &&
|
||||||
|
root.type === Fragment &&
|
||||||
|
(root.children as VNode[]).includes(vnode)))
|
||||||
|
) {
|
||||||
|
const cssVars = instance.getCssVars()
|
||||||
|
for (const key in cssVars) {
|
||||||
|
expectedMap.set(`--${key}`, String(cssVars[key]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vnode === root && instance.parent) {
|
||||||
|
resolveCssVars(instance.parent, instance.vnode, expectedMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import {
|
||||||
NOOP,
|
NOOP,
|
||||||
PatchFlags,
|
PatchFlags,
|
||||||
ShapeFlags,
|
ShapeFlags,
|
||||||
|
def,
|
||||||
getGlobalThis,
|
getGlobalThis,
|
||||||
invokeArrayFns,
|
invokeArrayFns,
|
||||||
isArray,
|
isArray,
|
||||||
|
@ -702,15 +703,10 @@ function baseCreateRenderer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
||||||
Object.defineProperty(el, '__vnode', {
|
def(el, '__vnode', vnode, true)
|
||||||
value: vnode,
|
def(el, '__vueParentComponent', parentComponent, true)
|
||||||
enumerable: false,
|
|
||||||
})
|
|
||||||
Object.defineProperty(el, '__vueParentComponent', {
|
|
||||||
value: parentComponent,
|
|
||||||
enumerable: false,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirs) {
|
if (dirs) {
|
||||||
invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
|
invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
|
||||||
}
|
}
|
||||||
|
@ -811,6 +807,9 @@ function baseCreateRenderer(
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
) => {
|
) => {
|
||||||
const el = (n2.el = n1.el!)
|
const el = (n2.el = n1.el!)
|
||||||
|
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
||||||
|
el.__vnode = n2
|
||||||
|
}
|
||||||
let { patchFlag, dynamicChildren, dirs } = n2
|
let { patchFlag, dynamicChildren, dirs } = n2
|
||||||
// #1426 take the old vnode's patch flag into account since user may clone a
|
// #1426 take the old vnode's patch flag into account since user may clone a
|
||||||
// compiler-generated vnode, which de-opts to FULL_PROPS
|
// compiler-generated vnode, which de-opts to FULL_PROPS
|
||||||
|
@ -2113,6 +2112,11 @@ function baseCreateRenderer(
|
||||||
dirs,
|
dirs,
|
||||||
memoIndex,
|
memoIndex,
|
||||||
} = vnode
|
} = vnode
|
||||||
|
|
||||||
|
if (patchFlag === PatchFlags.BAIL) {
|
||||||
|
optimized = false
|
||||||
|
}
|
||||||
|
|
||||||
// unset ref
|
// unset ref
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
setRef(ref, null, parentSuspense, vnode, true)
|
setRef(ref, null, parentSuspense, vnode, true)
|
||||||
|
@ -2156,7 +2160,6 @@ function baseCreateRenderer(
|
||||||
vnode,
|
vnode,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
optimized,
|
|
||||||
internals,
|
internals,
|
||||||
doRemove,
|
doRemove,
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
inject,
|
inject,
|
||||||
nextTick,
|
nextTick,
|
||||||
ref,
|
ref,
|
||||||
|
render,
|
||||||
renderSlot,
|
renderSlot,
|
||||||
} from '../src'
|
} from '../src'
|
||||||
|
|
||||||
|
@ -141,6 +142,7 @@ describe('defineCustomElement', () => {
|
||||||
foo: [String, null],
|
foo: [String, null],
|
||||||
bar: Object,
|
bar: Object,
|
||||||
bazQux: null,
|
bazQux: null,
|
||||||
|
value: null,
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return [
|
return [
|
||||||
|
@ -151,6 +153,12 @@ describe('defineCustomElement', () => {
|
||||||
})
|
})
|
||||||
customElements.define('my-el-props', E)
|
customElements.define('my-el-props', E)
|
||||||
|
|
||||||
|
test('renders custom element w/ correct object prop value', () => {
|
||||||
|
render(h('my-el-props', { value: { x: 1 } }), container)
|
||||||
|
const el = container.children[0]
|
||||||
|
expect((el as any).value).toEqual({ x: 1 })
|
||||||
|
})
|
||||||
|
|
||||||
test('props via attribute', async () => {
|
test('props via attribute', async () => {
|
||||||
// bazQux should map to `baz-qux` attribute
|
// bazQux should map to `baz-qux` attribute
|
||||||
container.innerHTML = `<my-el-props foo="hello" baz-qux="bye"></my-el-props>`
|
container.innerHTML = `<my-el-props foo="hello" baz-qux="bye"></my-el-props>`
|
||||||
|
|
|
@ -69,4 +69,23 @@ describe('runtime-dom: attrs patching', () => {
|
||||||
patchProp(el, 'value', null, symbol)
|
patchProp(el, 'value', null, symbol)
|
||||||
expect(el.value).toBe(symbol.toString())
|
expect(el.value).toBe(symbol.toString())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #11177
|
||||||
|
test('should allow setting value to object, leaving stringification to the element/browser', () => {
|
||||||
|
// normal behavior
|
||||||
|
const el = document.createElement('div')
|
||||||
|
const obj = { toString: () => 'foo' }
|
||||||
|
patchProp(el, 'data-test', null, obj)
|
||||||
|
expect(el.dataset.test).toBe('foo')
|
||||||
|
|
||||||
|
const el2 = document.createElement('div')
|
||||||
|
let testvalue: null | typeof obj = null
|
||||||
|
// simulating a web component that implements its own setAttribute handler
|
||||||
|
el2.setAttribute = (name, value) => {
|
||||||
|
testvalue = value
|
||||||
|
}
|
||||||
|
patchProp(el2, 'data-test', null, obj)
|
||||||
|
expect(el2.dataset.test).toBe(undefined)
|
||||||
|
expect(testvalue).toBe(obj)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
NOOP,
|
NOOP,
|
||||||
includeBooleanAttr,
|
includeBooleanAttr,
|
||||||
isSpecialBooleanAttr,
|
isSpecialBooleanAttr,
|
||||||
|
isSymbol,
|
||||||
makeMap,
|
makeMap,
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
|
@ -37,7 +38,10 @@ export function patchAttr(
|
||||||
el.removeAttribute(key)
|
el.removeAttribute(key)
|
||||||
} else {
|
} else {
|
||||||
// attribute value is a string https://html.spec.whatwg.org/multipage/dom.html#attributes
|
// attribute value is a string https://html.spec.whatwg.org/multipage/dom.html#attributes
|
||||||
el.setAttribute(key, isBoolean ? '' : String(value))
|
el.setAttribute(
|
||||||
|
key,
|
||||||
|
isBoolean ? '' : isSymbol(value) ? String(value) : value,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,11 @@ export const patchProp: DOMRendererOptions['patchProp'] = (
|
||||||
)
|
)
|
||||||
// #6007 also set form state as attributes so they work with
|
// #6007 also set form state as attributes so they work with
|
||||||
// <input type="reset"> or libs / extensions that expect attributes
|
// <input type="reset"> or libs / extensions that expect attributes
|
||||||
if (key === 'value' || key === 'checked' || key === 'selected') {
|
// #11163 custom elements may use value as an prop and set it as object
|
||||||
|
if (
|
||||||
|
!el.tagName.includes('-') &&
|
||||||
|
(key === 'value' || key === 'checked' || key === 'selected')
|
||||||
|
) {
|
||||||
patchAttr(el, key, nextValue, isSVG, parentComponent, key !== 'value')
|
patchAttr(el, key, nextValue, isSVG, parentComponent, key !== 'value')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.0.5",
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
"vite": "^5.2.13"
|
"vite": "^5.3.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/repl": "^4.2.1",
|
"@vue/repl": "^4.2.1",
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.0.5",
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
"vite": "^5.2.13"
|
"vite": "^5.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
903
pnpm-lock.yaml
903
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -9,11 +9,11 @@ import pico from 'picocolors'
|
||||||
import commonJS from '@rollup/plugin-commonjs'
|
import commonJS from '@rollup/plugin-commonjs'
|
||||||
import polyfillNode from 'rollup-plugin-polyfill-node'
|
import polyfillNode from 'rollup-plugin-polyfill-node'
|
||||||
import { nodeResolve } from '@rollup/plugin-node-resolve'
|
import { nodeResolve } from '@rollup/plugin-node-resolve'
|
||||||
import terser from '@rollup/plugin-terser'
|
|
||||||
import esbuild from 'rollup-plugin-esbuild'
|
import esbuild from 'rollup-plugin-esbuild'
|
||||||
import alias from '@rollup/plugin-alias'
|
import alias from '@rollup/plugin-alias'
|
||||||
import { entries } from './scripts/aliases.js'
|
import { entries } from './scripts/aliases.js'
|
||||||
import { inlineEnums } from './scripts/inline-enums.js'
|
import { inlineEnums } from './scripts/inline-enums.js'
|
||||||
|
import { minify as minifySwc } from '@swc/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
|
@ -364,14 +364,25 @@ function createMinifiedConfig(/** @type {PackageFormat} */ format) {
|
||||||
format: outputConfigs[format].format,
|
format: outputConfigs[format].format,
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
terser({
|
{
|
||||||
module: /^esm/.test(format),
|
name: 'swc-minify',
|
||||||
compress: {
|
|
||||||
ecma: 2016,
|
async renderChunk(
|
||||||
pure_getters: true,
|
contents,
|
||||||
|
_,
|
||||||
|
{ format, sourcemap, sourcemapExcludeSources },
|
||||||
|
) {
|
||||||
|
const { code, map } = await minifySwc(contents, {
|
||||||
|
module: format === 'es',
|
||||||
|
compress: true,
|
||||||
|
mangle: true,
|
||||||
|
sourceMap: !!sourcemap,
|
||||||
|
inlineSourcesContent: !sourcemapExcludeSources,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { code, map: map || null }
|
||||||
|
},
|
||||||
},
|
},
|
||||||
safari10: true,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ execa('pnpm', ['build', 'vue', '-f', 'global-runtime']).then(() => {
|
||||||
errors.push(
|
errors.push(
|
||||||
'dev build contains unexpected esbuild object spread helper.\n' +
|
'dev build contains unexpected esbuild object spread helper.\n' +
|
||||||
'This means { ...obj } syntax is used in runtime code. This should be ' +
|
'This means { ...obj } syntax is used in runtime code. This should be ' +
|
||||||
'refactoed to use the `extend` helper to avoid the extra code.',
|
'refactored to use the `extend` helper to avoid the extra code.',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue