From 534593cfa8ae1f85558737104f139555b493870c Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 3 Oct 2022 15:30:43 +0800 Subject: [PATCH 0001/2232] chore: format --- packages/compiler-core/src/transforms/vOn.ts | 4 ++-- packages/compiler-sfc/__tests__/rewriteDefault.spec.ts | 8 ++++++-- packages/compiler-ssr/src/ssrCodegenTransform.ts | 3 +-- .../src/transforms/ssrTransformTransitionGroup.ts | 4 ++-- packages/runtime-core/__tests__/apiOptions.spec.ts | 1 - packages/runtime-core/src/helpers/withMemo.ts | 2 +- packages/shared/src/index.ts | 3 ++- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/compiler-core/src/transforms/vOn.ts b/packages/compiler-core/src/transforms/vOn.ts index a9dfe77eff7..ed39ecee12f 100644 --- a/packages/compiler-core/src/transforms/vOn.ts +++ b/packages/compiler-core/src/transforms/vOn.ts @@ -54,9 +54,9 @@ export const transformOn: DirectiveTransform = ( ? // for component and vnode lifecycle event listeners, auto convert // it to camelCase. See issue #2249 toHandlerKey(camelize(rawName)) - // preserve case for plain element listeners that have uppercase + : // preserve case for plain element listeners that have uppercase // letters, as these may be custom elements' custom events - : `on:${rawName}` + `on:${rawName}` eventName = createSimpleExpression(eventString, true, arg.loc) } else { // #2388 diff --git a/packages/compiler-sfc/__tests__/rewriteDefault.spec.ts b/packages/compiler-sfc/__tests__/rewriteDefault.spec.ts index 9fb4c64bbb3..40561da17db 100644 --- a/packages/compiler-sfc/__tests__/rewriteDefault.spec.ts +++ b/packages/compiler-sfc/__tests__/rewriteDefault.spec.ts @@ -206,7 +206,10 @@ describe('compiler sfc: rewriteDefault', () => { test('@Component\nexport default class w/ comments', async () => { expect( - rewriteDefault(`// export default\n@Component\nexport default class Foo {}`, 'script') + rewriteDefault( + `// export default\n@Component\nexport default class Foo {}`, + 'script' + ) ).toMatchInlineSnapshot(` "// export default @Component @@ -231,7 +234,8 @@ describe('compiler sfc: rewriteDefault', () => { test('@Component\nexport default class w/ comments 3', async () => { expect( rewriteDefault( - `/*\n@Component\nexport default class Foo {}*/\n` + `export default class Bar {}`, + `/*\n@Component\nexport default class Foo {}*/\n` + + `export default class Bar {}`, 'script' ) ).toMatchInlineSnapshot(` diff --git a/packages/compiler-ssr/src/ssrCodegenTransform.ts b/packages/compiler-ssr/src/ssrCodegenTransform.ts index 4d142162073..d7d1645a0e7 100644 --- a/packages/compiler-ssr/src/ssrCodegenTransform.ts +++ b/packages/compiler-ssr/src/ssrCodegenTransform.ts @@ -49,8 +49,7 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) { createCompoundExpression([`const _cssVars = { style: `, varsExp, `}`]) ) Array.from(cssContext.helpers.keys()).forEach(helper => { - if (!ast.helpers.includes(helper)) - ast.helpers.push(helper) + if (!ast.helpers.includes(helper)) ast.helpers.push(helper) }) } diff --git a/packages/compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts b/packages/compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts index dedf1f64075..00b0d9dd45a 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts @@ -33,8 +33,8 @@ export function ssrTransformTransitionGroup( node, context, otherProps, - true, /* isComponent */ - false, /* isDynamicComponent */ + true /* isComponent */, + false /* isDynamicComponent */, true /* ssr (skip event listeners) */ ) let propsExp = null diff --git a/packages/runtime-core/__tests__/apiOptions.spec.ts b/packages/runtime-core/__tests__/apiOptions.spec.ts index da5fe346c32..96601b7870f 100644 --- a/packages/runtime-core/__tests__/apiOptions.spec.ts +++ b/packages/runtime-core/__tests__/apiOptions.spec.ts @@ -1048,7 +1048,6 @@ describe('api: options', () => { expect(root.innerHTML).toBe(`

Foo

`) }) - test('options defined in component have higher priority', async () => { const Mixin = { msg1: 'base' diff --git a/packages/runtime-core/src/helpers/withMemo.ts b/packages/runtime-core/src/helpers/withMemo.ts index 002b9e27a88..76b0f055f39 100644 --- a/packages/runtime-core/src/helpers/withMemo.ts +++ b/packages/runtime-core/src/helpers/withMemo.ts @@ -23,7 +23,7 @@ export function isMemoSame(cached: VNode, memo: any[]) { if (prev.length != memo.length) { return false } - + for (let i = 0; i < prev.length; i++) { if (hasChanged(prev[i], memo[i])) { return false diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index f3fee4c45e7..c1b0773292c 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -52,7 +52,8 @@ export const isMap = (val: unknown): val is Map => export const isSet = (val: unknown): val is Set => toTypeString(val) === '[object Set]' -export const isDate = (val: unknown): val is Date => toTypeString(val) === '[object Date]' +export const isDate = (val: unknown): val is Date => + toTypeString(val) === '[object Date]' export const isFunction = (val: unknown): val is Function => typeof val === 'function' export const isString = (val: unknown): val is string => typeof val === 'string' From 3a130742929dd995bccd854074ad38290048e69d Mon Sep 17 00:00:00 2001 From: pengbo <57180744+PengBoUESTC@users.noreply.github.com> Date: Mon, 3 Oct 2022 15:37:57 +0800 Subject: [PATCH 0002/2232] chore: eslint rules overrides (#6729) Co-authored-by: pengbo43 Co-authored-by: Anthony Fu --- .eslintrc.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index 8485d76f53e..cdbb1dffd91 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -69,6 +69,14 @@ module.exports = { 'no-restricted-globals': ['error', ...NodeGlobals], 'no-restricted-syntax': 'off' } + }, + // Node scripts + { + files: ['scripts/**', './*.js', 'packages/**/index.js', 'packages/size-check/**'], + rules: { + 'no-restricted-globals': 'off', + 'no-restricted-syntax': 'off' + } } ] } From a9e3fa4e21c1f61479e2accde20087184ea6a355 Mon Sep 17 00:00:00 2001 From: webfansplz <308241863@qq.com> Date: Mon, 3 Oct 2022 16:37:54 +0800 Subject: [PATCH 0003/2232] refactor(types): improve of type assertion (#4177) Co-authored-by: webfansplz <> --- packages/runtime-core/src/components/BaseTransition.ts | 2 +- packages/runtime-core/src/components/Suspense.ts | 4 +++- packages/runtime-core/src/components/Teleport.ts | 6 +++--- packages/shared/src/index.ts | 2 +- packages/template-explorer/src/index.ts | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index a671bef6560..5e5e216d34a 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -274,7 +274,7 @@ if (__COMPAT__) { // export the public type for h/tsx inference // also to avoid inline import() in generated d.ts files -export const BaseTransition = BaseTransitionImpl as any as { +export const BaseTransition = BaseTransitionImpl as unknown as { new (): { $props: BaseTransitionProps } diff --git a/packages/runtime-core/src/components/Suspense.ts b/packages/runtime-core/src/components/Suspense.ts index 8408cab388d..baf57088626 100644 --- a/packages/runtime-core/src/components/Suspense.ts +++ b/packages/runtime-core/src/components/Suspense.ts @@ -89,7 +89,9 @@ export const SuspenseImpl = { } // Force-casted public typing for h and TSX props inference -export const Suspense = (__FEATURE_SUSPENSE__ ? SuspenseImpl : null) as any as { +export const Suspense = (__FEATURE_SUSPENSE__ + ? SuspenseImpl + : null) as unknown as { __isSuspense: true new (): { $props: VNodeProps & SuspenseProps } } diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index 06b69aff4ec..e519aa2bb3a 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -52,13 +52,13 @@ const resolveTarget = ( `ideally should be outside of the entire Vue component tree.` ) } - return target as any + return target as T } } else { if (__DEV__ && !targetSelector && !isTeleportDisabled(props)) { warn(`Invalid Teleport target: ${targetSelector}`) } - return targetSelector as any + return targetSelector as T } } @@ -388,7 +388,7 @@ function hydrateTeleport( } // Force-casted public typing for h and TSX props inference -export const Teleport = TeleportImpl as any as { +export const Teleport = TeleportImpl as unknown as { __isTeleport: true new (): { $props: VNodeProps & TeleportProps } } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index c1b0773292c..8e3020f9e37 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -100,7 +100,7 @@ const cacheStringFunction = string>(fn: T): T => { return ((str: string) => { const hit = cache[str] return hit || (cache[str] = fn(str)) - }) as any + }) as T } const camelizeRE = /-(\w)/g diff --git a/packages/template-explorer/src/index.ts b/packages/template-explorer/src/index.ts index d676c717373..3cf9c6b52cf 100644 --- a/packages/template-explorer/src/index.ts +++ b/packages/template-explorer/src/index.ts @@ -275,5 +275,5 @@ function debounce any>( fn(...args) prevTimer = null }, delay) - }) as any + }) as T } From 824174915f274cc30f10af2fbc7b323709ab34bd Mon Sep 17 00:00:00 2001 From: edison Date: Mon, 3 Oct 2022 16:40:37 +0800 Subject: [PATCH 0004/2232] types: creating Teleport with h and RawSlots #2613 (#2614) --- packages/runtime-core/src/h.ts | 2 +- test-dts/h.test-d.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index f22e4bb30d0..44f91340827 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -108,7 +108,7 @@ export function h( export function h( type: typeof Teleport, props: RawProps & TeleportProps, - children: RawChildren + children: RawChildren | RawSlots ): VNode // suspense diff --git a/test-dts/h.test-d.ts b/test-dts/h.test-d.ts index c71b54a2aa8..5318f71fe94 100644 --- a/test-dts/h.test-d.ts +++ b/test-dts/h.test-d.ts @@ -47,6 +47,7 @@ describe('h inference w/ Fragment', () => { describe('h inference w/ Teleport', () => { h(Teleport, { to: '#foo' }, 'hello') + h(Teleport, { to: '#foo' }, { default() {} }) // @ts-expect-error expectError(h(Teleport)) // @ts-expect-error From 35a113eda43a49e921a6eb60d45db81dc847d665 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 3 Oct 2022 11:29:34 +0200 Subject: [PATCH 0005/2232] fix(devtools): use cleanupBuffer instead of modifying _buffer (#6812) Co-authored-by: Anthony Fu --- packages/runtime-core/src/devtools.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/runtime-core/src/devtools.ts b/packages/runtime-core/src/devtools.ts index 83d7483df39..f05128d47aa 100644 --- a/packages/runtime-core/src/devtools.ts +++ b/packages/runtime-core/src/devtools.ts @@ -28,7 +28,11 @@ interface DevtoolsHook { once: (event: string, handler: Function) => void off: (event: string, handler: Function) => void appRecords: AppRecord[] - _buffer: any[][] + /** + * Added at https://github.com/vuejs/devtools/commit/f2ad51eea789006ab66942e5a27c0f0986a257f9 + * Returns wether the arg was buffered or not + */ + cleanupBuffer?: (matchArg: unknown) => boolean } export let devtools: DevtoolsHook @@ -109,18 +113,14 @@ const _devtoolsComponentRemoved = /*#__PURE__*/ createDevtoolsComponentHook( export const devtoolsComponentRemoved = ( component: ComponentInternalInstance ) => { - if (devtools && devtools._buffer.length) { - let wasBuffered = false - devtools._buffer = devtools._buffer.filter(item => { - if (item.some(arg => arg === component)) { - wasBuffered = true - return false - } - return true - }) - if (wasBuffered) return + if ( + devtools && + typeof devtools.cleanupBuffer === 'function' && + // remove the component if it wasn't buffered + !devtools.cleanupBuffer(component) + ) { + _devtoolsComponentRemoved(component) } - _devtoolsComponentRemoved(component) } function createDevtoolsComponentHook(hook: DevtoolsHooks) { From 45782df349fbfd88e9442ed76fa2b108ea5bf3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90?= Date: Tue, 4 Oct 2022 14:27:54 +0800 Subject: [PATCH 0006/2232] chore: add cache for prettier and eslint (#6813) --- .gitignore | 1 + package.json | 6 +++--- pnpm-lock.yaml | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 1487e3b7c21..75c8139bd9b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ explorations TODOs.md *.log .idea +.eslintcache diff --git a/package.json b/package.json index aeff54e501e..b0c4ad8859e 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "size": "run-s size-global size-baseline", "size-global": "node scripts/build.js vue runtime-dom -f global -p", "size-baseline": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler && cd packages/size-check && vite build && node brotli", - "lint": "eslint --ext .ts packages/*/{src,__tests__}/**.ts", - "format": "prettier --write --parser typescript \"packages/**/*.ts?(x)\"", + "lint": "eslint --cache --ext .ts packages/*/{src,__tests__}/**.ts", + "format": "prettier --write --cache --parser typescript \"packages/**/*.ts?(x)\"", "test": "run-s \"test-unit {@}\" \"test-e2e {@}\"", "test-unit": "jest --filter ./scripts/filter-unit.js", "test-e2e": "node scripts/build.js vue -f global -d && jest --filter ./scripts/filter-e2e.js --runInBand", @@ -80,7 +80,7 @@ "marked": "^4.0.10", "minimist": "^1.2.0", "npm-run-all": "^4.1.5", - "prettier": "^2.3.1", + "prettier": "^2.7.1", "puppeteer": "^10.0.0", "rollup": "~2.38.5", "rollup-plugin-node-builtins": "^2.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56b391f98fc..f05f6568e49 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,7 +35,7 @@ importers: marked: ^4.0.10 minimist: ^1.2.0 npm-run-all: ^4.1.5 - prettier: ^2.3.1 + prettier: ^2.7.1 puppeteer: ^10.0.0 rollup: ~2.38.5 rollup-plugin-node-builtins: ^2.1.2 @@ -84,7 +84,7 @@ importers: marked: 4.0.10 minimist: 1.2.5 npm-run-all: 4.1.5 - prettier: 2.5.1 + prettier: 2.7.1 puppeteer: 10.4.0 rollup: 2.38.5 rollup-plugin-node-builtins: 2.1.2 @@ -5841,8 +5841,8 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier/2.5.1: - resolution: {integrity: sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==} + /prettier/2.7.1: + resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} engines: {node: '>=10.13.0'} hasBin: true dev: true From c454aa5cd069d1ed56aca958a7dc8adb14ab7f72 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 5 Oct 2022 15:48:37 +0800 Subject: [PATCH 0007/2232] chore: more pr tips in contribution guide [ci skip] --- .github/contributing.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/contributing.md b/.github/contributing.md index 5f4e64b7e1e..fe2ac7c114b 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -40,6 +40,20 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before - No need to worry about code style as long as you have installed the dev dependencies - modified files are automatically formatted with Prettier on commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [yorkie](https://github.com/yyx990803/yorkie)). +### Advanced Pull Request Tips + +- The PR should fix the intended bug **only** and not introduce unrelated changes. This includes unnecessary refactors - a PR should focus on the fix and not code style, this makes it easier to trace changes in the future. + +- Consider the performance / size impact of the changes, and whether the bug being fixes justifies the cost. If the bug being fixed is a very niche edge case, we should try to minimize the size / perf cost to make it worthwhile. + + - Is the code perf-sensitive (e.g. in "hot paths" like component updates or the vdom patch function?) + - If the branch is dev-only, performance is less of a concern. + + - Check how much extra bundle size the change introduces. + - Make sure to put dev-only code in `__DEV__` branches so they are tree-shakable. + - Runtime code is more sensitive to size increase than compiler code. + - Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in `@vue/shared` are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`. + ## Development Setup You will need [Node.js](https://nodejs.org) **version 16+**, and [PNPM](https://pnpm.io) **version 7+**. From 82e3f2dc3036b553261f302538701769fa92536e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E6=96=B0?= <73146951+4xii@users.noreply.github.com> Date: Thu, 6 Oct 2022 05:26:16 +0800 Subject: [PATCH 0008/2232] chore: symbols as helperNameMap index type (#6622) --- packages/compiler-core/src/runtimeHelpers.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts index 3bfe73935b8..3f5ef024797 100644 --- a/packages/compiler-core/src/runtimeHelpers.ts +++ b/packages/compiler-core/src/runtimeHelpers.ts @@ -42,8 +42,7 @@ export const IS_MEMO_SAME = Symbol(__DEV__ ? `isMemoSame` : ``) // Name mapping for runtime helpers that need to be imported from 'vue' in // generated code. Make sure these are correctly exported in the runtime! -// Using `any` here because TS doesn't allow symbols as index type. -export const helperNameMap: any = { +export const helperNameMap: Record = { [FRAGMENT]: `Fragment`, [TELEPORT]: `Teleport`, [SUSPENSE]: `Suspense`, @@ -85,7 +84,7 @@ export const helperNameMap: any = { [IS_MEMO_SAME]: `isMemoSame` } -export function registerRuntimeHelpers(helpers: any) { +export function registerRuntimeHelpers(helpers: Record) { Object.getOwnPropertySymbols(helpers).forEach(s => { helperNameMap[s] = helpers[s] }) From 35dc2bbe7d22a4b551f912612f8d4f04788005a7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2022 17:54:22 +0800 Subject: [PATCH 0009/2232] build: enforce LF line break for built files --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index 506f4a07ad9..518f13edfcd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "dist", "sourceMap": false, "target": "es2016", + "newLine": "LF", "useDefineForClassFields": false, "module": "esnext", "moduleResolution": "node", From a71f9ac41af464fdb69220e69c50739dd3a8f365 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 14 Oct 2022 10:53:23 +0800 Subject: [PATCH 0010/2232] fix(effectScope): calling off() of a detached scope should not break currentScope --- packages/reactivity/__tests__/effectScope.spec.ts | 11 +++++++++++ packages/reactivity/src/effectScope.ts | 7 ++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/__tests__/effectScope.spec.ts b/packages/reactivity/__tests__/effectScope.spec.ts index af92c97f94c..28579fef028 100644 --- a/packages/reactivity/__tests__/effectScope.spec.ts +++ b/packages/reactivity/__tests__/effectScope.spec.ts @@ -277,4 +277,15 @@ describe('reactivity/effect/scope', () => { expect(getCurrentScope()).toBe(currentScope) }) }) + + it('calling .off() of a detached scope inside an active scope should not break currentScope', () => { + const parentScope = new EffectScope() + + parentScope.run(() => { + const childScope = new EffectScope(true) + childScope.on() + childScope.off() + expect(getCurrentScope()).toBe(parentScope) + }) + }) }) diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index f4396e8f3ab..557a1379492 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -34,9 +34,9 @@ export class EffectScope { */ private index: number | undefined - constructor(detached = false) { + constructor(public detached = false) { + this.parent = activeEffectScope if (!detached && activeEffectScope) { - this.parent = activeEffectScope this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( this @@ -89,7 +89,7 @@ export class EffectScope { } } // nested scope, dereference from parent to avoid memory leaks - if (this.parent && !fromParent) { + if (!this.detached && this.parent && !fromParent) { // optimized O(1) removal const last = this.parent.scopes!.pop() if (last && last !== this) { @@ -97,6 +97,7 @@ export class EffectScope { last.index = this.index! } } + this.parent = undefined this.active = false } } From 5ee40532a63e0b792e0c1eccf3cf68546a4e23e9 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 14 Oct 2022 16:00:03 +0800 Subject: [PATCH 0011/2232] fix(runtime-dom): fix event timestamp check in iframes fix #2513 fix #3933 close #5474 --- .../runtime-dom/__tests__/patchEvents.spec.ts | 73 +++++++++++-------- packages/runtime-dom/src/modules/events.ts | 72 +++++++----------- 2 files changed, 70 insertions(+), 75 deletions(-) diff --git a/packages/runtime-dom/__tests__/patchEvents.spec.ts b/packages/runtime-dom/__tests__/patchEvents.spec.ts index 9c30616a24a..32466f29a7b 100644 --- a/packages/runtime-dom/__tests__/patchEvents.spec.ts +++ b/packages/runtime-dom/__tests__/patchEvents.spec.ts @@ -5,30 +5,28 @@ const timeout = () => new Promise(r => setTimeout(r)) describe(`runtime-dom: events patching`, () => { it('should assign event handler', async () => { const el = document.createElement('div') - const event = new Event('click') const fn = jest.fn() patchProp(el, 'onClick', null, fn) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(fn).toHaveBeenCalledTimes(3) }) it('should update event handler', async () => { const el = document.createElement('div') - const event = new Event('click') const prevFn = jest.fn() const nextFn = jest.fn() patchProp(el, 'onClick', null, prevFn) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) patchProp(el, 'onClick', prevFn, nextFn) await timeout() - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(prevFn).toHaveBeenCalledTimes(1) expect(nextFn).toHaveBeenCalledTimes(2) @@ -36,11 +34,10 @@ describe(`runtime-dom: events patching`, () => { it('should support multiple event handlers', async () => { const el = document.createElement('div') - const event = new Event('click') const fn1 = jest.fn() const fn2 = jest.fn() patchProp(el, 'onClick', null, [fn1, fn2]) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(fn1).toHaveBeenCalledTimes(1) expect(fn2).toHaveBeenCalledTimes(1) @@ -48,58 +45,55 @@ describe(`runtime-dom: events patching`, () => { it('should unassign event handler', async () => { const el = document.createElement('div') - const event = new Event('click') const fn = jest.fn() patchProp(el, 'onClick', null, fn) patchProp(el, 'onClick', fn, null) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(fn).not.toHaveBeenCalled() }) it('should support event option modifiers', async () => { const el = document.createElement('div') - const event = new Event('click') const fn = jest.fn() patchProp(el, 'onClickOnceCapture', null, fn) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(fn).toHaveBeenCalledTimes(1) }) it('should unassign event handler with options', async () => { const el = document.createElement('div') - const event = new Event('click') const fn = jest.fn() patchProp(el, 'onClickCapture', null, fn) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(fn).toHaveBeenCalledTimes(1) patchProp(el, 'onClickCapture', fn, null) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(fn).toHaveBeenCalledTimes(1) }) it('should support native onclick', async () => { const el = document.createElement('div') - const event = new Event('click') // string should be set as attribute const fn = ((window as any).__globalSpy = jest.fn()) patchProp(el, 'onclick', null, '__globalSpy(1)') - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() delete (window as any).__globalSpy expect(fn).toHaveBeenCalledWith(1) const fn2 = jest.fn() patchProp(el, 'onclick', '__globalSpy(1)', fn2) + const event = new Event('click') el.dispatchEvent(event) await timeout() expect(fn).toHaveBeenCalledTimes(1) @@ -108,13 +102,12 @@ describe(`runtime-dom: events patching`, () => { it('should support stopImmediatePropagation on multiple listeners', async () => { const el = document.createElement('div') - const event = new Event('click') const fn1 = jest.fn((e: Event) => { e.stopImmediatePropagation() }) const fn2 = jest.fn() patchProp(el, 'onClick', null, [fn1, fn2]) - el.dispatchEvent(event) + el.dispatchEvent(new Event('click')) await timeout() expect(fn1).toHaveBeenCalledTimes(1) expect(fn2).toHaveBeenCalledTimes(0) @@ -125,15 +118,15 @@ describe(`runtime-dom: events patching`, () => { const el1 = document.createElement('div') const el2 = document.createElement('div') - const event = new Event('click') + // const event = new Event('click') const prevFn = jest.fn() const nextFn = jest.fn() patchProp(el1, 'onClick', null, prevFn) patchProp(el2, 'onClick', null, prevFn) - el1.dispatchEvent(event) - el2.dispatchEvent(event) + el1.dispatchEvent(new Event('click')) + el2.dispatchEvent(new Event('click')) await timeout() expect(prevFn).toHaveBeenCalledTimes(2) expect(nextFn).toHaveBeenCalledTimes(0) @@ -141,19 +134,39 @@ describe(`runtime-dom: events patching`, () => { patchProp(el1, 'onClick', prevFn, nextFn) patchProp(el2, 'onClick', prevFn, nextFn) - el1.dispatchEvent(event) - el2.dispatchEvent(event) + el1.dispatchEvent(new Event('click')) + el2.dispatchEvent(new Event('click')) await timeout() expect(prevFn).toHaveBeenCalledTimes(2) expect(nextFn).toHaveBeenCalledTimes(2) - el1.dispatchEvent(event) - el2.dispatchEvent(event) + el1.dispatchEvent(new Event('click')) + el2.dispatchEvent(new Event('click')) await timeout() expect(prevFn).toHaveBeenCalledTimes(2) expect(nextFn).toHaveBeenCalledTimes(4) }) + // vuejs/vue#6566 + it('should not fire handler attached by the event itself', async () => { + const el = document.createElement('div') + const child = document.createElement('div') + el.appendChild(child) + document.body.appendChild(el) + const childFn = jest.fn() + const parentFn = jest.fn() + + patchProp(child, 'onClick', null, () => { + childFn() + patchProp(el, 'onClick', null, parentFn) + }) + child.dispatchEvent(new Event('click', { bubbles: true })) + + await timeout() + expect(childFn).toHaveBeenCalled() + expect(parentFn).not.toHaveBeenCalled() + }) + // #2841 test('should patch event correctly in web-components', async () => { class TestElement extends HTMLElement { diff --git a/packages/runtime-dom/src/modules/events.ts b/packages/runtime-dom/src/modules/events.ts index d0f8d364a29..8dbccadef1a 100644 --- a/packages/runtime-dom/src/modules/events.ts +++ b/packages/runtime-dom/src/modules/events.ts @@ -12,38 +12,6 @@ interface Invoker extends EventListener { type EventValue = Function | Function[] -// Async edge case fix requires storing an event listener's attach timestamp. -const [_getNow, skipTimestampCheck] = /*#__PURE__*/ (() => { - let _getNow = Date.now - let skipTimestampCheck = false - if (typeof window !== 'undefined') { - // Determine what event timestamp the browser is using. Annoyingly, the - // timestamp can either be hi-res (relative to page load) or low-res - // (relative to UNIX epoch), so in order to compare time we have to use the - // same timestamp type when saving the flush timestamp. - if (Date.now() > document.createEvent('Event').timeStamp) { - // if the low-res timestamp which is bigger than the event timestamp - // (which is evaluated AFTER) it means the event is using a hi-res timestamp, - // and we need to use the hi-res version for event listeners as well. - _getNow = performance.now.bind(performance) - } - // #3485: Firefox <= 53 has incorrect Event.timeStamp implementation - // and does not fire microtasks in between event propagation, so safe to exclude. - const ffMatch = navigator.userAgent.match(/firefox\/(\d+)/i) - skipTimestampCheck = !!(ffMatch && Number(ffMatch[1]) <= 53) - } - return [_getNow, skipTimestampCheck] -})() - -// To avoid the overhead of repeatedly calling performance.now(), we cache -// and use the same timestamp for all event listeners attached in the same tick. -let cachedNow: number = 0 -const p = /*#__PURE__*/ Promise.resolve() -const reset = () => { - cachedNow = 0 -} -const getNow = () => cachedNow || (p.then(reset), (cachedNow = _getNow())) - export function addEventListener( el: Element, event: string, @@ -105,27 +73,41 @@ function parseName(name: string): [string, EventListenerOptions | undefined] { return [event, options] } +// To avoid the overhead of repeatedly calling Date.now(), we cache +// and use the same timestamp for all event listeners attached in the same tick. +let cachedNow: number = 0 +const p = /*#__PURE__*/ Promise.resolve() +const getNow = () => + cachedNow || (p.then(() => (cachedNow = 0)), (cachedNow = Date.now())) + function createInvoker( initialValue: EventValue, instance: ComponentInternalInstance | null ) { - const invoker: Invoker = (e: Event) => { - // async edge case #6566: inner click event triggers patch, event handler + const invoker: Invoker = (e: Event & { _vts?: number }) => { + // async edge case vuejs/vue#6566 + // inner click event triggers patch, event handler // attached to outer element during patch, and triggered again. This // happens because browsers fire microtask ticks between event propagation. - // the solution is simple: we save the timestamp when a handler is attached, - // and the handler would only fire if the event passed to it was fired + // this no longer happens for templates in Vue 3, but could still be + // theoretically possible for hand-written render functions. + // the solution: we save the timestamp when a handler is attached, + // and also attach the timestamp to any event that was handled by vue + // for the first time (to avoid inconsistent event timestamp implementations + // or events fired from iframes, e.g. #2513) + // The handler would only fire if the event passed to it was fired // AFTER it was attached. - const timeStamp = e.timeStamp || _getNow() - - if (skipTimestampCheck || timeStamp >= invoker.attached - 1) { - callWithAsyncErrorHandling( - patchStopImmediatePropagation(e, invoker.value), - instance, - ErrorCodes.NATIVE_EVENT_HANDLER, - [e] - ) + if (!e._vts) { + e._vts = Date.now() + } else if (e._vts <= invoker.attached) { + return } + callWithAsyncErrorHandling( + patchStopImmediatePropagation(e, invoker.value), + instance, + ErrorCodes.NATIVE_EVENT_HANDLER, + [e] + ) } invoker.value = initialValue invoker.attached = getNow() From 82a73da351bb5d26735f734ae2540a3033c00c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=BC=E6=B0=B4=E5=BE=AE=E5=AF=92?= Date: Fri, 14 Oct 2022 16:08:32 +0800 Subject: [PATCH 0012/2232] fix(runtime-core): ensure that errors in slot function execution do not affect block tracking (#5670) fix #5657 --- packages/runtime-core/__tests__/vnode.spec.ts | 28 +++++++++++++++++-- .../src/componentRenderContext.ts | 12 +++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/runtime-core/__tests__/vnode.spec.ts b/packages/runtime-core/__tests__/vnode.spec.ts index fdd23797b10..54cdc725b4d 100644 --- a/packages/runtime-core/__tests__/vnode.spec.ts +++ b/packages/runtime-core/__tests__/vnode.spec.ts @@ -8,11 +8,12 @@ import { cloneVNode, mergeProps, normalizeVNode, - transformVNodeArgs + transformVNodeArgs, + isBlockTreeEnabled } from '../src/vnode' import { Data } from '../src/component' import { ShapeFlags, PatchFlags } from '@vue/shared' -import { h, reactive, isReactive, setBlockTracking, ref } from '../src' +import { h, reactive, isReactive, setBlockTracking, ref, withCtx } from '../src' import { createApp, nodeOps, serializeInner } from '@vue/runtime-test' import { setCurrentRenderingInstance } from '../src/componentRenderContext' @@ -614,6 +615,29 @@ describe('vnode', () => { ])) expect(vnode.dynamicChildren).toStrictEqual([]) }) + // #5657 + test('error of slot function execution should not affect block tracking', () => { + expect(isBlockTreeEnabled).toStrictEqual(1) + const slotFn = withCtx( + () => { + throw new Error('slot execution error') + }, + { type: {}, appContext: {} } as any + ) + const Parent = { + setup(_: any, { slots }: any) { + return () => { + try { + slots.default() + } catch (e) {} + } + } + } + const vnode = + (openBlock(), createBlock(Parent, null, { default: slotFn })) + createApp(vnode).mount(nodeOps.createElement('div')) + expect(isBlockTreeEnabled).toStrictEqual(1) + }) }) describe('transformVNodeArgs', () => { diff --git a/packages/runtime-core/src/componentRenderContext.ts b/packages/runtime-core/src/componentRenderContext.ts index 8f49ec251d5..8097dc5fe2b 100644 --- a/packages/runtime-core/src/componentRenderContext.ts +++ b/packages/runtime-core/src/componentRenderContext.ts @@ -89,10 +89,14 @@ export function withCtx( setBlockTracking(-1) } const prevInstance = setCurrentRenderingInstance(ctx) - const res = fn(...args) - setCurrentRenderingInstance(prevInstance) - if (renderFnWithContext._d) { - setBlockTracking(1) + let res + try { + res = fn(...args) + } finally { + setCurrentRenderingInstance(prevInstance) + if (renderFnWithContext._d) { + setBlockTracking(1) + } } if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { From 9ae796d1567f1b0acb08659a2363a54b525a9ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Fri, 14 Oct 2022 17:23:01 +0800 Subject: [PATCH 0013/2232] fix(runtime-core): fix v-for ref reactivity behavior difference between prod and dev (#6714) fix #6697 --- .../__tests__/rendererTemplateRef.spec.ts | 46 +++++++++++++++++++ .../runtime-core/src/rendererTemplateRef.ts | 6 ++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts b/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts index 6a03e7a8eb5..668391c9185 100644 --- a/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts +++ b/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts @@ -493,4 +493,50 @@ describe('api: template refs', () => { await nextTick() expect(mapRefs()).toMatchObject(['2', '3', '4']) }) + + // #6697 v-for ref behaves differently under production and development + test('named ref in v-for , should be responsive when rendering', async () => { + const list = ref([1, 2, 3]) + const listRefs = ref([]) + const App = { + setup() { + return { listRefs } + }, + render() { + return h('div', null, [ + h('div', null, String(listRefs.value)), + h( + 'ul', + list.value.map(i => + h( + 'li', + { + ref: 'listRefs', + ref_for: true + }, + i + ) + ) + ) + ]) + } + } + const root = nodeOps.createElement('div') + render(h(App), root) + + await nextTick() + expect(String(listRefs.value)).toBe( + '[object Object],[object Object],[object Object]' + ) + expect(serializeInner(root)).toBe( + '
[object Object],[object Object],[object Object]
  • 1
  • 2
  • 3
' + ) + + list.value.splice(0, 1) + await nextTick() + expect(String(listRefs.value)).toBe('[object Object],[object Object]') + expect(serializeInner(root)).toBe( + '
[object Object],[object Object]
  • 2
  • 3
' + ) + }) }) diff --git a/packages/runtime-core/src/rendererTemplateRef.ts b/packages/runtime-core/src/rendererTemplateRef.ts index 1fe432d5019..30f96228d1f 100644 --- a/packages/runtime-core/src/rendererTemplateRef.ts +++ b/packages/runtime-core/src/rendererTemplateRef.ts @@ -84,7 +84,11 @@ export function setRef( if (_isString || _isRef) { const doSet = () => { if (rawRef.f) { - const existing = _isString ? refs[ref] : ref.value + const existing = _isString + ? hasOwn(setupState, ref) + ? setupState[ref] + : refs[ref] + : ref.value if (isUnmount) { isArray(existing) && remove(existing, refValue) } else { From 9617dd4b2abc07a5dc40de6e5b759e851b4d0da1 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 14 Oct 2022 18:41:15 +0800 Subject: [PATCH 0014/2232] release: v3.2.41 --- CHANGELOG.md | 14 ++++++ package.json | 2 +- packages/compiler-core/package.json | 4 +- packages/compiler-dom/package.json | 6 +-- packages/compiler-sfc/package.json | 12 ++--- packages/compiler-ssr/package.json | 6 +-- packages/reactivity-transform/package.json | 6 +-- packages/reactivity/package.json | 4 +- packages/runtime-core/package.json | 6 +-- packages/runtime-dom/package.json | 6 +-- packages/runtime-test/package.json | 6 +-- packages/server-renderer/package.json | 8 ++-- packages/sfc-playground/package.json | 4 +- packages/shared/package.json | 2 +- packages/size-check/package.json | 2 +- packages/template-explorer/package.json | 2 +- packages/vue-compat/package.json | 4 +- packages/vue/package.json | 12 ++--- pnpm-lock.yaml | 54 +++++++++++----------- 19 files changed, 87 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af5bb244e1a..2fef732631a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## [3.2.41](https://github.com/vuejs/core/compare/v3.2.40...v3.2.41) (2022-10-14) + + +### Bug Fixes + +* **devtools:** avoid memory leak caused by devtools event buffer ([24f4c47](https://github.com/vuejs/core/commit/24f4c479d661698afd967cf428f9439be4578a04)), closes [#6591](https://github.com/vuejs/core/issues/6591) +* **devtools:** use cleanupBuffer instead of modifying _buffer ([#6812](https://github.com/vuejs/core/issues/6812)) ([35a113e](https://github.com/vuejs/core/commit/35a113eda43a49e921a6eb60d45db81dc847d665)) +* **effectScope:** calling off() of a detached scope should not break currentScope ([a71f9ac](https://github.com/vuejs/core/commit/a71f9ac41af464fdb69220e69c50739dd3a8f365)) +* **runtime-core:** ensure that errors in slot function execution do not affect block tracking ([#5670](https://github.com/vuejs/core/issues/5670)) ([82a73da](https://github.com/vuejs/core/commit/82a73da351bb5d26735f734ae2540a3033c00c9e)), closes [#5657](https://github.com/vuejs/core/issues/5657) +* **runtime-core:** fix v-for ref reactivity behavior difference between prod and dev ([#6714](https://github.com/vuejs/core/issues/6714)) ([9ae796d](https://github.com/vuejs/core/commit/9ae796d1567f1b0acb08659a2363a54b525a9ee4)), closes [#6697](https://github.com/vuejs/core/issues/6697) +* **runtime-dom:** fix event timestamp check in iframes ([5ee4053](https://github.com/vuejs/core/commit/5ee40532a63e0b792e0c1eccf3cf68546a4e23e9)), closes [#2513](https://github.com/vuejs/core/issues/2513) [#3933](https://github.com/vuejs/core/issues/3933) [#5474](https://github.com/vuejs/core/issues/5474) + + + ## [3.2.40](https://github.com/vuejs/core/compare/v3.2.39...v3.2.40) (2022-09-28) diff --git a/package.json b/package.json index b0c4ad8859e..f6dff7e7d7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "3.2.40", + "version": "3.2.41", "packageManager": "pnpm@7.1.0", "scripts": { "dev": "node scripts/dev.js", diff --git a/packages/compiler-core/package.json b/packages/compiler-core/package.json index 1e2eb6c25f7..f9415296ace 100644 --- a/packages/compiler-core/package.json +++ b/packages/compiler-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-core", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/compiler-core", "main": "index.js", "module": "dist/compiler-core.esm-bundler.js", @@ -32,7 +32,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme", "dependencies": { - "@vue/shared": "3.2.40", + "@vue/shared": "3.2.41", "@babel/parser": "^7.16.4", "estree-walker": "^2.0.2", "source-map": "^0.6.1" diff --git a/packages/compiler-dom/package.json b/packages/compiler-dom/package.json index cc1c3607563..359be350e3a 100644 --- a/packages/compiler-dom/package.json +++ b/packages/compiler-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-dom", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/compiler-dom", "main": "index.js", "module": "dist/compiler-dom.esm-bundler.js", @@ -37,7 +37,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-dom#readme", "dependencies": { - "@vue/shared": "3.2.40", - "@vue/compiler-core": "3.2.40" + "@vue/shared": "3.2.41", + "@vue/compiler-core": "3.2.41" } } diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index edf8d2d901b..ca50683bbe4 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-sfc", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/compiler-sfc", "main": "dist/compiler-sfc.cjs.js", "module": "dist/compiler-sfc.esm-browser.js", @@ -33,11 +33,11 @@ "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme", "dependencies": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.40", - "@vue/compiler-dom": "3.2.40", - "@vue/compiler-ssr": "3.2.40", - "@vue/reactivity-transform": "3.2.40", - "@vue/shared": "3.2.40", + "@vue/compiler-core": "3.2.41", + "@vue/compiler-dom": "3.2.41", + "@vue/compiler-ssr": "3.2.41", + "@vue/reactivity-transform": "3.2.41", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "magic-string": "^0.25.7", "source-map": "^0.6.1", diff --git a/packages/compiler-ssr/package.json b/packages/compiler-ssr/package.json index 2a1d8b77ca2..f7f40544f2a 100644 --- a/packages/compiler-ssr/package.json +++ b/packages/compiler-ssr/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-ssr", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/compiler-ssr", "main": "dist/compiler-ssr.cjs.js", "types": "dist/compiler-ssr.d.ts", @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-ssr#readme", "dependencies": { - "@vue/shared": "3.2.40", - "@vue/compiler-dom": "3.2.40" + "@vue/shared": "3.2.41", + "@vue/compiler-dom": "3.2.41" } } diff --git a/packages/reactivity-transform/package.json b/packages/reactivity-transform/package.json index 37d321b4124..0cf8340031d 100644 --- a/packages/reactivity-transform/package.json +++ b/packages/reactivity-transform/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity-transform", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/reactivity-transform", "main": "dist/reactivity-transform.cjs.js", "files": [ @@ -29,8 +29,8 @@ "homepage": "https://github.com/vuejs/core/tree/dev/packages/reactivity-transform#readme", "dependencies": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.40", - "@vue/shared": "3.2.40", + "@vue/compiler-core": "3.2.41", + "@vue/shared": "3.2.41", "estree-walker": "^2.0.2", "magic-string": "^0.25.7" }, diff --git a/packages/reactivity/package.json b/packages/reactivity/package.json index b9d0ed6ee8c..df1b379801e 100644 --- a/packages/reactivity/package.json +++ b/packages/reactivity/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/reactivity", "main": "index.js", "module": "dist/reactivity.esm-bundler.js", @@ -36,6 +36,6 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/reactivity#readme", "dependencies": { - "@vue/shared": "3.2.40" + "@vue/shared": "3.2.41" } } diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json index 8cd94e0ca62..7cb199b7b22 100644 --- a/packages/runtime-core/package.json +++ b/packages/runtime-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-core", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/runtime-core", "main": "index.js", "module": "dist/runtime-core.esm-bundler.js", @@ -32,7 +32,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-core#readme", "dependencies": { - "@vue/shared": "3.2.40", - "@vue/reactivity": "3.2.40" + "@vue/shared": "3.2.41", + "@vue/reactivity": "3.2.41" } } diff --git a/packages/runtime-dom/package.json b/packages/runtime-dom/package.json index 6971fb76fae..d623453072e 100644 --- a/packages/runtime-dom/package.json +++ b/packages/runtime-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-dom", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/runtime-dom", "main": "index.js", "module": "dist/runtime-dom.esm-bundler.js", @@ -35,8 +35,8 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-dom#readme", "dependencies": { - "@vue/shared": "3.2.40", - "@vue/runtime-core": "3.2.40", + "@vue/shared": "3.2.41", + "@vue/runtime-core": "3.2.41", "csstype": "^2.6.8" } } diff --git a/packages/runtime-test/package.json b/packages/runtime-test/package.json index dccef271b55..bac2bd9c434 100644 --- a/packages/runtime-test/package.json +++ b/packages/runtime-test/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-test", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/runtime-test", "private": true, "main": "index.js", @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-test#readme", "dependencies": { - "@vue/shared": "3.2.40", - "@vue/runtime-core": "3.2.40" + "@vue/shared": "3.2.41", + "@vue/runtime-core": "3.2.41" } } diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json index 36c555b5e26..d310cead06f 100644 --- a/packages/server-renderer/package.json +++ b/packages/server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/server-renderer", - "version": "3.2.40", + "version": "3.2.41", "description": "@vue/server-renderer", "main": "index.js", "module": "dist/server-renderer.esm-bundler.js", @@ -32,10 +32,10 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/server-renderer#readme", "peerDependencies": { - "vue": "3.2.40" + "vue": "3.2.41" }, "dependencies": { - "@vue/shared": "3.2.40", - "@vue/compiler-ssr": "3.2.40" + "@vue/shared": "3.2.41", + "@vue/compiler-ssr": "3.2.41" } } diff --git a/packages/sfc-playground/package.json b/packages/sfc-playground/package.json index da01a74f484..c361b00b2b2 100644 --- a/packages/sfc-playground/package.json +++ b/packages/sfc-playground/package.json @@ -1,6 +1,6 @@ { "name": "@vue/sfc-playground", - "version": "3.2.40", + "version": "3.2.41", "private": true, "scripts": { "dev": "vite", @@ -12,7 +12,7 @@ "vite": "^3.0.0" }, "dependencies": { - "vue": "3.2.40", + "vue": "3.2.41", "@vue/repl": "^1.3.0", "file-saver": "^2.0.5", "jszip": "^3.6.0" diff --git a/packages/shared/package.json b/packages/shared/package.json index b315979d62b..46e8673bd34 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@vue/shared", - "version": "3.2.40", + "version": "3.2.41", "description": "internal utils shared across @vue packages", "main": "index.js", "module": "dist/shared.esm-bundler.js", diff --git a/packages/size-check/package.json b/packages/size-check/package.json index b1c847b5f63..a9f2de07995 100644 --- a/packages/size-check/package.json +++ b/packages/size-check/package.json @@ -1,6 +1,6 @@ { "name": "@vue/size-check", - "version": "3.2.40", + "version": "3.2.41", "private": true, "scripts": { "build": "vite build" diff --git a/packages/template-explorer/package.json b/packages/template-explorer/package.json index 47edf7a3654..f8d3892e79e 100644 --- a/packages/template-explorer/package.json +++ b/packages/template-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/template-explorer", - "version": "3.2.40", + "version": "3.2.41", "private": true, "buildOptions": { "formats": [ diff --git a/packages/vue-compat/package.json b/packages/vue-compat/package.json index a4724ab73c3..df789070bf8 100644 --- a/packages/vue-compat/package.json +++ b/packages/vue-compat/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compat", - "version": "3.2.40", + "version": "3.2.41", "description": "Vue 3 compatibility build for Vue 2", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", @@ -43,6 +43,6 @@ "source-map": "^0.6.1" }, "peerDependencies": { - "vue": "3.2.40" + "vue": "3.2.41" } } diff --git a/packages/vue/package.json b/packages/vue/package.json index 7a81b97fcd0..4749c984f61 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "3.2.40", + "version": "3.2.41", "description": "The progressive JavaScript framework for building modern web UI.", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", @@ -68,10 +68,10 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/vue#readme", "dependencies": { - "@vue/shared": "3.2.40", - "@vue/compiler-dom": "3.2.40", - "@vue/runtime-dom": "3.2.40", - "@vue/compiler-sfc": "3.2.40", - "@vue/server-renderer": "3.2.40" + "@vue/shared": "3.2.41", + "@vue/compiler-dom": "3.2.41", + "@vue/runtime-dom": "3.2.41", + "@vue/compiler-sfc": "3.2.41", + "@vue/server-renderer": "3.2.41" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f05f6568e49..b13c39b66b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,7 +106,7 @@ importers: specifiers: '@babel/parser': ^7.16.4 '@babel/types': ^7.16.0 - '@vue/shared': 3.2.40 + '@vue/shared': 3.2.41 estree-walker: ^2.0.2 source-map: ^0.6.1 dependencies: @@ -119,8 +119,8 @@ importers: packages/compiler-dom: specifiers: - '@vue/compiler-core': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/compiler-core': 3.2.41 + '@vue/shared': 3.2.41 dependencies: '@vue/compiler-core': link:../compiler-core '@vue/shared': link:../shared @@ -131,12 +131,12 @@ importers: '@babel/types': ^7.16.0 '@types/estree': ^0.0.48 '@types/lru-cache': ^5.1.0 - '@vue/compiler-core': 3.2.40 - '@vue/compiler-dom': 3.2.40 - '@vue/compiler-ssr': 3.2.40 + '@vue/compiler-core': 3.2.41 + '@vue/compiler-dom': 3.2.41 + '@vue/compiler-ssr': 3.2.41 '@vue/consolidate': ^0.17.3 - '@vue/reactivity-transform': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/reactivity-transform': 3.2.41 + '@vue/shared': 3.2.41 estree-walker: ^2.0.2 hash-sum: ^2.0.0 lru-cache: ^5.1.1 @@ -174,15 +174,15 @@ importers: packages/compiler-ssr: specifiers: - '@vue/compiler-dom': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/compiler-dom': 3.2.41 + '@vue/shared': 3.2.41 dependencies: '@vue/compiler-dom': link:../compiler-dom '@vue/shared': link:../shared packages/reactivity: specifiers: - '@vue/shared': 3.2.40 + '@vue/shared': 3.2.41 dependencies: '@vue/shared': link:../shared @@ -191,8 +191,8 @@ importers: '@babel/core': ^7.16.0 '@babel/parser': ^7.16.4 '@babel/types': ^7.16.0 - '@vue/compiler-core': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/compiler-core': 3.2.41 + '@vue/shared': 3.2.41 estree-walker: ^2.0.2 magic-string: ^0.25.7 dependencies: @@ -207,16 +207,16 @@ importers: packages/runtime-core: specifiers: - '@vue/reactivity': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/reactivity': 3.2.41 + '@vue/shared': 3.2.41 dependencies: '@vue/reactivity': link:../reactivity '@vue/shared': link:../shared packages/runtime-dom: specifiers: - '@vue/runtime-core': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/runtime-core': 3.2.41 + '@vue/shared': 3.2.41 csstype: ^2.6.8 dependencies: '@vue/runtime-core': link:../runtime-core @@ -225,16 +225,16 @@ importers: packages/runtime-test: specifiers: - '@vue/runtime-core': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/runtime-core': 3.2.41 + '@vue/shared': 3.2.41 dependencies: '@vue/runtime-core': link:../runtime-core '@vue/shared': link:../shared packages/server-renderer: specifiers: - '@vue/compiler-ssr': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/compiler-ssr': 3.2.41 + '@vue/shared': 3.2.41 dependencies: '@vue/compiler-ssr': link:../compiler-ssr '@vue/shared': link:../shared @@ -246,7 +246,7 @@ importers: file-saver: ^2.0.5 jszip: ^3.6.0 vite: ^3.0.0 - vue: 3.2.40 + vue: 3.2.41 dependencies: '@vue/repl': 1.3.0_vue@packages+vue file-saver: 2.0.5 @@ -272,11 +272,11 @@ importers: packages/vue: specifiers: - '@vue/compiler-dom': 3.2.40 - '@vue/compiler-sfc': 3.2.40 - '@vue/runtime-dom': 3.2.40 - '@vue/server-renderer': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/compiler-dom': 3.2.41 + '@vue/compiler-sfc': 3.2.41 + '@vue/runtime-dom': 3.2.41 + '@vue/server-renderer': 3.2.41 + '@vue/shared': 3.2.41 dependencies: '@vue/compiler-dom': link:../compiler-dom '@vue/compiler-sfc': link:../compiler-sfc From bc167b5c6c7c5756f3b7720a7d3ddcdb2c7f717f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Thu, 20 Oct 2022 21:45:05 +0200 Subject: [PATCH 0015/2232] fix(runtime-core): watching multiple values - handle `undefined` as initial values (fix: #5032) (#5033) --- .../runtime-core/__tests__/apiWatch.spec.ts | 17 +++++++++++++++++ packages/runtime-core/src/apiWatch.ts | 9 +++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 86ad948adf6..c9db3657367 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -177,6 +177,23 @@ describe('api: watch', () => { ]) }) + it('watching multiple sources: undefined initial values and immediate: true', async () => { + const a = ref() + const b = ref() + let called = false + watch( + [a, b], + (newVal, oldVal) => { + called = true + expect(newVal).toMatchObject([undefined, undefined]) + expect(oldVal).toBeUndefined() + }, + { immediate: true } + ) + await nextTick() + expect(called).toBe(true) + }) + it('watching multiple sources: readonly array', async () => { const state = reactive({ count: 1 }) const status = ref(false) diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 27215f342b3..19026cf645d 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -296,7 +296,9 @@ function doWatch( return NOOP } - let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE + let oldValue: any = isMultiSource + ? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE) + : INITIAL_WATCHER_VALUE const job: SchedulerJob = () => { if (!effect.active) { return @@ -323,7 +325,10 @@ function doWatch( callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [ newValue, // pass undefined as the old value when it's changed for the first time - oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue, + oldValue === INITIAL_WATCHER_VALUE || + (isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE) + ? undefined + : oldValue, onCleanup ]) oldValue = newValue From e5fc7dcc02f2dd3fa8172958259049031626375f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Thu, 20 Oct 2022 21:45:51 +0200 Subject: [PATCH 0016/2232] fix(types): ensure createBlock() helper accepts Teleport and Supsense types (fix: #2855) (#5458) Co-authored-by: Carlos Rodrigues --- packages/runtime-core/src/vnode.ts | 5 ++++- test-dts/compiler.test-d.ts | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test-dts/compiler.test-d.ts diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index b731dc0b51f..2122f9f855a 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -24,6 +24,7 @@ import { RawSlots } from './componentSlots' import { isProxy, Ref, toRaw, ReactiveFlags, isRef } from '@vue/reactivity' import { AppContext } from './apiCreateApp' import { + Suspense, SuspenseImpl, isSuspense, SuspenseBoundary @@ -31,7 +32,7 @@ import { import { DirectiveBinding } from './directives' import { TransitionHooks } from './components/BaseTransition' import { warn } from './warning' -import { TeleportImpl, isTeleport } from './components/Teleport' +import { Teleport, TeleportImpl, isTeleport } from './components/Teleport' import { currentRenderingInstance, currentScopeId @@ -63,7 +64,9 @@ export type VNodeTypes = | typeof Static | typeof Comment | typeof Fragment + | typeof Teleport | typeof TeleportImpl + | typeof Suspense | typeof SuspenseImpl export type VNodeRef = diff --git a/test-dts/compiler.test-d.ts b/test-dts/compiler.test-d.ts new file mode 100644 index 00000000000..974b49492a4 --- /dev/null +++ b/test-dts/compiler.test-d.ts @@ -0,0 +1,20 @@ +import { + expectType, + createBlock, + VNode, + Teleport, + Text, + Static, + Comment, + Fragment, + Suspense, + defineComponent +} from './index' + +expectType(createBlock(Teleport)) +expectType(createBlock(Text)) +expectType(createBlock(Static)) +expectType(createBlock(Comment)) +expectType(createBlock(Fragment)) +expectType(createBlock(Suspense)) +expectType(createBlock(defineComponent({}))) From e6224f4256be2fdac3651ade87f9f91ccf6def71 Mon Sep 17 00:00:00 2001 From: bcq028 <108849949+bcq028@users.noreply.github.com> Date: Fri, 21 Oct 2022 15:25:06 +0800 Subject: [PATCH 0017/2232] fix(reactivity): enable trigger when use str to set length of arr (#6810) Co-authored-by: Anthony Fu --- packages/reactivity/__tests__/effect.spec.ts | 16 ++++++++++++++++ packages/reactivity/src/effect.ts | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index a66aff278a4..a322c7209f4 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -922,6 +922,22 @@ describe('reactivity/effect', () => { expect(fnSpy2).toHaveBeenCalledTimes(1) }) + it('should be triggered when set length with string', () => { + let ret1 = 'idle' + let ret2 = 'idle' + const arr1 = reactive(new Array(11).fill(0)) + const arr2 = reactive(new Array(11).fill(0)) + effect(() => { + ret1 = arr1[10] === undefined ? 'arr[10] is set to empty' : 'idle' + }) + effect(() => { + ret2 = arr2[10] === undefined ? 'arr[10] is set to empty' : 'idle' + }) + arr1.length = 2 + arr2.length = '2' as any + expect(ret1).toBe(ret2) + }) + describe('readonly + reactive for Map', () => { test('should work with readonly(reactive(Map))', () => { const m = reactive(new Map()) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 34b53eb8fef..8a54372cd5b 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -1,5 +1,5 @@ import { TrackOpTypes, TriggerOpTypes } from './operations' -import { extend, isArray, isIntegerKey, isMap } from '@vue/shared' +import { extend, isArray, isIntegerKey, isMap, toNumber } from '@vue/shared' import { EffectScope, recordEffectScope } from './effectScope' import { createDep, @@ -277,7 +277,7 @@ export function trigger( deps = [...depsMap.values()] } else if (key === 'length' && isArray(target)) { depsMap.forEach((dep, key) => { - if (key === 'length' || key >= (newValue as number)) { + if (key === 'length' || key >= toNumber(newValue)) { deps.push(dep) } }) From 54b6ba32cafcc41fa9b7b85f1f1a306923204177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Sat, 22 Oct 2022 11:20:02 +0200 Subject: [PATCH 0018/2232] fix(runtime-core): ensure props definition objects are not mutated during props normalization (close: #6915) (#6916) --- .../__tests__/componentProps.spec.ts | 19 +++++++++++++++++++ packages/runtime-core/src/componentProps.ts | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index 40725dfcd13..cdb77838e31 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -595,4 +595,23 @@ describe('component props', () => { JSON.stringify(attrs) + Object.keys(attrs) ) }) + + // #691ef + test('should not mutate original props long-form definition object', () => { + const props = { + msg: { + type: String + } + } + const Comp = defineComponent({ + props, + render() {} + }) + + const root = nodeOps.createElement('div') + + render(h(Comp, { msg: 'test' }), root) + + expect(Object.keys(props.msg).length).toBe(1) + }) }) diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 09b487811b5..d59a4e94699 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -522,7 +522,7 @@ export function normalizePropsOptions( if (validatePropName(normalizedKey)) { const opt = raw[key] const prop: NormalizedProp = (normalized[normalizedKey] = - isArray(opt) || isFunction(opt) ? { type: opt } : opt) + isArray(opt) || isFunction(opt) ? { type: opt } : { ...opt }) if (prop) { const booleanIndex = getTypeIndex(Boolean, prop.type) const stringIndex = getTypeIndex(String, prop.type) From b0b74a160c08941fa9a7a5460f36a1f2fccbf423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Sat, 22 Oct 2022 11:20:46 +0200 Subject: [PATCH 0019/2232] fix(runtime-core): custom-element: ensure number casting of camelCase props. (fix: #5374) (#5377) --- packages/runtime-dom/__tests__/customElement.spec.ts | 10 +++++----- packages/runtime-dom/src/apiCustomElement.ts | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index e29c36123f3..300cc2322ce 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -135,14 +135,14 @@ describe('defineCustomElement', () => { test('attribute -> prop type casting', async () => { const E = defineCustomElement({ props: { - foo: Number, + fooBar: Number, // test casting of camelCase prop names bar: Boolean, baz: String }, render() { return [ - this.foo, - typeof this.foo, + this.fooBar, + typeof this.fooBar, this.bar, typeof this.bar, this.baz, @@ -151,7 +151,7 @@ describe('defineCustomElement', () => { } }) customElements.define('my-el-props-cast', E) - container.innerHTML = `` + container.innerHTML = `` const e = container.childNodes[0] as VueElement expect(e.shadowRoot!.innerHTML).toBe( `1 number false boolean 12345 string` @@ -161,7 +161,7 @@ describe('defineCustomElement', () => { await nextTick() expect(e.shadowRoot!.innerHTML).toBe(`1 number true boolean 12345 string`) - e.setAttribute('foo', '2e1') + e.setAttribute('foo-bar', '2e1') await nextTick() expect(e.shadowRoot!.innerHTML).toBe( `20 number true boolean 12345 string` diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index eabe83b6b9f..5ff45d652f2 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -268,10 +268,11 @@ export class VueElement extends BaseClass { protected _setAttr(key: string) { let value = this.getAttribute(key) - if (this._numberProps && this._numberProps[key]) { + const camelKey = camelize(key) + if (this._numberProps && this._numberProps[camelKey]) { value = toNumber(value) } - this._setProp(camelize(key), value, false) + this._setProp(camelKey, value, false) } /** From cbc3e67c375ed75fb4ef605c6d5a823890a29712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=95=E8=AF=81?= Date: Wed, 26 Oct 2022 15:01:37 +0800 Subject: [PATCH 0020/2232] types(shared): Improve LooseRequired (#6244) --- packages/shared/src/typeUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/typeUtils.ts b/packages/shared/src/typeUtils.ts index 8730d7f38bf..3f3620c6672 100644 --- a/packages/shared/src/typeUtils.ts +++ b/packages/shared/src/typeUtils.ts @@ -5,7 +5,7 @@ export type UnionToIntersection = ( : never // make keys required but keep undefined values -export type LooseRequired = { [P in string & keyof T]: T[P] } +export type LooseRequired = { [P in keyof (T & Required)]: T[P] } // If the the type T accepts type "any", output type Y, otherwise output type N. // https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360 From 8e792d93a8062e29d9da279eee2202f38897f42f Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Wed, 26 Oct 2022 08:07:44 +0100 Subject: [PATCH 0021/2232] types(h): Support passing `props` to `Component` when using `h` (#3219) --- packages/runtime-core/src/h.ts | 6 +++--- test-dts/h.test-d.ts | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index 44f91340827..520f568e7a8 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -141,9 +141,9 @@ export function h

( ): VNode // component without props -export function h( - type: Component, - props: null, +export function h

( + type: Component

, + props?: (RawProps & P) | null, children?: RawChildren | RawSlots ): VNode diff --git a/test-dts/h.test-d.ts b/test-dts/h.test-d.ts index 5318f71fe94..6116fff74a9 100644 --- a/test-dts/h.test-d.ts +++ b/test-dts/h.test-d.ts @@ -145,12 +145,11 @@ describe('h inference w/ defineComponent', () => { // expectError(h(Foo, { bar: 1, foo: 1 })) // }) -// #922 +// #922 and #3218 describe('h support for generic component type', () => { function foo(bar: Component) { h(bar) h(bar, 'hello') - // @ts-expect-error h(bar, { id: 'ok' }, 'hello') } foo({}) From d9de6caecdada62b29ad6836c0580b119f47523c Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 26 Oct 2022 15:17:25 +0800 Subject: [PATCH 0022/2232] refactor: move toNumber call out of loop --- packages/reactivity/src/effect.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 8a54372cd5b..f1799a62d3a 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -276,8 +276,9 @@ export function trigger( // trigger all effects for target deps = [...depsMap.values()] } else if (key === 'length' && isArray(target)) { + const newLength = toNumber(newValue) depsMap.forEach((dep, key) => { - if (key === 'length' || key >= toNumber(newValue)) { + if (key === 'length' || key >= newLength) { deps.push(dep) } }) From 09bb3e996ef17967022243a519d7dcc2921dd049 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 26 Oct 2022 16:18:19 +0800 Subject: [PATCH 0023/2232] fix(compiler-ssr): fix invalid codegen when v-slot name is explicit empty attr (#3326) squashed from fix by @tjk --- packages/compiler-core/src/index.ts | 3 ++- .../compiler-core/src/transforms/transformExpression.ts | 2 +- packages/compiler-ssr/__tests__/ssrComponent.spec.ts | 5 +++++ .../compiler-ssr/src/transforms/ssrTransformComponent.ts | 6 ++++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/compiler-core/src/index.ts b/packages/compiler-core/src/index.ts index 6ed7aa5b897..6a1f8b63b57 100644 --- a/packages/compiler-core/src/index.ts +++ b/packages/compiler-core/src/index.ts @@ -43,7 +43,8 @@ export { processIf } from './transforms/vIf' export { processFor, createForLoopParams } from './transforms/vFor' export { transformExpression, - processExpression + processExpression, + stringifyExpression } from './transforms/transformExpression' export { buildSlots, diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts index e4311ad4f88..43c69559688 100644 --- a/packages/compiler-core/src/transforms/transformExpression.ts +++ b/packages/compiler-core/src/transforms/transformExpression.ts @@ -361,7 +361,7 @@ function canPrefix(id: Identifier) { return true } -function stringifyExpression(exp: ExpressionNode | string): string { +export function stringifyExpression(exp: ExpressionNode | string): string { if (isString(exp)) { return exp } else if (exp.type === NodeTypes.SIMPLE_EXPRESSION) { diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts index 5d5191ffb44..cd21e48cb9a 100644 --- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts @@ -104,6 +104,11 @@ describe('ssr: components', () => { `) }) + test('empty attribute should not produce syntax error', () => { + // previously this would produce syntax error `default: _withCtx((, _push, ...)` + expect(compile(`foo`).code).not.toMatch(`(,`) + }) + test('named slots', () => { expect( compile(` diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index df190c768a8..dc8c6a4ae4f 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -36,7 +36,8 @@ import { CallExpression, JSChildNode, RESOLVE_DYNAMIC_COMPONENT, - TRANSITION + TRANSITION, + stringifyExpression } from '@vue/compiler-dom' import { SSR_RENDER_COMPONENT, SSR_RENDER_VNODE } from '../runtimeHelpers' import { @@ -145,8 +146,9 @@ export const ssrTransformComponent: NodeTransform = (node, context) => { wipMap.set(node, wipEntries) const buildSSRSlotFn: SlotFnBuilder = (props, children, loc) => { + const param0 = (props && stringifyExpression(props)) || `_` const fn = createFunctionExpression( - [props || `_`, `_push`, `_parent`, `_scopeId`], + [param0, `_push`, `_parent`, `_scopeId`], undefined, // no return, assign body later true, // newline true, // isSlot From e816812f10b9e3a375eef8dffd617d7f08b23c00 Mon Sep 17 00:00:00 2001 From: HeYunfei Date: Wed, 26 Oct 2022 16:27:42 +0800 Subject: [PATCH 0024/2232] fix(types): should unwrap tuple correctly (#3820) fix #3819 --- packages/reactivity/src/ref.ts | 13 ++-- test-dts/reactivity.test-d.ts | 135 ++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 70 deletions(-) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 5843b3f63ae..2632160b318 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -280,13 +280,10 @@ export interface RefUnwrapBailTypes {} export type ShallowUnwrapRef = { [K in keyof T]: T[K] extends Ref - ? V - : // if `V` is `unknown` that means it does not extend `Ref` and is undefined - T[K] extends Ref | undefined - ? unknown extends V - ? undefined - : V | undefined - : T[K] + ? V // if `V` is `unknown` that means it does not extend `Ref` and is undefined + : T[K] extends Ref | undefined + ? unknown extends V ? undefined : V | undefined + : T[K] } export type UnwrapRef = T extends ShallowRef @@ -303,7 +300,7 @@ export type UnwrapRefSimple = T extends | RefUnwrapBailTypes[keyof RefUnwrapBailTypes] | { [RawSymbol]?: true } ? T - : T extends Array + : T extends ReadonlyArray ? { [K in keyof T]: UnwrapRefSimple } : T extends object & { [ShallowReactiveMarker]?: never } ? { diff --git a/test-dts/reactivity.test-d.ts b/test-dts/reactivity.test-d.ts index 0499c6da287..8722441ec14 100644 --- a/test-dts/reactivity.test-d.ts +++ b/test-dts/reactivity.test-d.ts @@ -1,62 +1,73 @@ -import { - ref, - readonly, - shallowReadonly, - describe, - expectError, - expectType, - Ref, - reactive, - markRaw -} from './index' - -describe('should support DeepReadonly', () => { - const r = readonly({ obj: { k: 'v' } }) - // @ts-expect-error - expectError((r.obj = {})) - // @ts-expect-error - expectError((r.obj.k = 'x')) -}) - -// #4180 -describe('readonly ref', () => { - const r = readonly(ref({ count: 1 })) - expectType(r) -}) - -describe('should support markRaw', () => { - class Test { - item = {} as Ref - } - const test = new Test() - const plain = { - ref: ref(1) - } - - const r = reactive({ - class: { - raw: markRaw(test), - reactive: test - }, - plain: { - raw: markRaw(plain), - reactive: plain - } - }) - - expectType>(r.class.raw) - // @ts-expect-error it should unwrap - expectType>(r.class.reactive) - - expectType>(r.plain.raw.ref) - // @ts-expect-error it should unwrap - expectType>(r.plain.reactive.ref) -}) - -describe('shallowReadonly ref unwrap', () => { - const r = shallowReadonly({ count: { n: ref(1) } }) - // @ts-expect-error - r.count = 2 - expectType(r.count.n) - r.count.n.value = 123 -}) +import { + ref, + readonly, + shallowReadonly, + describe, + expectError, + expectType, + Ref, + reactive, + markRaw +} from './index' + +describe('should support DeepReadonly', () => { + const r = readonly({ obj: { k: 'v' } }) + // @ts-expect-error + expectError((r.obj = {})) + // @ts-expect-error + expectError((r.obj.k = 'x')) +}) + +// #4180 +describe('readonly ref', () => { + const r = readonly(ref({ count: 1 })) + expectType(r) +}) + +describe('should support markRaw', () => { + class Test { + item = {} as Ref + } + const test = new Test() + const plain = { + ref: ref(1) + } + + const r = reactive({ + class: { + raw: markRaw(test), + reactive: test + }, + plain: { + raw: markRaw(plain), + reactive: plain + } + }) + + expectType>(r.class.raw) + // @ts-expect-error it should unwrap + expectType>(r.class.reactive) + + expectType>(r.plain.raw.ref) + // @ts-expect-error it should unwrap + expectType>(r.plain.reactive.ref) +}) + +describe('shallowReadonly ref unwrap', () => { + const r = shallowReadonly({ count: { n: ref(1) } }) + // @ts-expect-error + r.count = 2 + expectType(r.count.n) + r.count.n.value = 123 +}) + +// #3819 +describe('should unwrap tuple correctly', () => { + const readonlyTuple = [ref(0)] as const + const reactiveReadonlyTuple = reactive(readonlyTuple) + expectType>(reactiveReadonlyTuple[0]) + + const tuple: [Ref] = [ref(0)] + const reactiveTuple = reactive(tuple) + expectType>(reactiveTuple[0]) +}) From 183e4e61523b35edc3064a033734825c87a400d7 Mon Sep 17 00:00:00 2001 From: webfansplz <308241863@qq.com> Date: Wed, 26 Oct 2022 16:31:09 +0800 Subject: [PATCH 0025/2232] refactor(types): use template literal types insteads of any (#4166) --- .../runtime-dom/src/components/Transition.ts | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/runtime-dom/src/components/Transition.ts b/packages/runtime-dom/src/components/Transition.ts index 0c1a30f6b95..2c483c76e72 100644 --- a/packages/runtime-dom/src/components/Transition.ts +++ b/packages/runtime-dom/src/components/Transition.ts @@ -12,9 +12,11 @@ import { isObject, toNumber, extend, isArray } from '@vue/shared' const TRANSITION = 'transition' const ANIMATION = 'animation' +type AnimationTypes = typeof TRANSITION | typeof ANIMATION + export interface TransitionProps extends BaseTransitionProps { name?: string - type?: typeof TRANSITION | typeof ANIMATION + type?: AnimationTypes css?: boolean duration?: number | { enter: number; leave: number } // custom transition classes @@ -368,24 +370,33 @@ function whenTransitionEnds( } interface CSSTransitionInfo { - type: typeof TRANSITION | typeof ANIMATION | null + type: AnimationTypes | null propCount: number timeout: number hasTransform: boolean } +type AnimationProperties = 'Delay' | 'Duration' +type StylePropertiesKey = + | `${AnimationTypes}${AnimationProperties}` + | `${typeof TRANSITION}Property` + export function getTransitionInfo( el: Element, expectedType?: TransitionProps['type'] ): CSSTransitionInfo { - const styles: any = window.getComputedStyle(el) + const styles = window.getComputedStyle(el) as Pick< + CSSStyleDeclaration, + StylePropertiesKey + > // JSDOM may return undefined for transition properties - const getStyleProperties = (key: string) => (styles[key] || '').split(', ') - const transitionDelays = getStyleProperties(TRANSITION + 'Delay') - const transitionDurations = getStyleProperties(TRANSITION + 'Duration') + const getStyleProperties = (key: StylePropertiesKey) => + (styles[key] || '').split(', ') + const transitionDelays = getStyleProperties(`${TRANSITION}Delay`) + const transitionDurations = getStyleProperties(`${TRANSITION}Duration`) const transitionTimeout = getTimeout(transitionDelays, transitionDurations) - const animationDelays = getStyleProperties(ANIMATION + 'Delay') - const animationDurations = getStyleProperties(ANIMATION + 'Duration') + const animationDelays = getStyleProperties(`${ANIMATION}Delay`) + const animationDurations = getStyleProperties(`${ANIMATION}Duration`) const animationTimeout = getTimeout(animationDelays, animationDurations) let type: CSSTransitionInfo['type'] = null @@ -420,7 +431,9 @@ export function getTransitionInfo( } const hasTransform = type === TRANSITION && - /\b(transform|all)(,|$)/.test(styles[TRANSITION + 'Property']) + /\b(transform|all)(,|$)/.test( + getStyleProperties(`${TRANSITION}Property`).toString() + ) return { type, timeout, From 83f7e6f8a688e823274379fe79f58b90ea58892d Mon Sep 17 00:00:00 2001 From: edison Date: Wed, 26 Oct 2022 16:44:30 +0800 Subject: [PATCH 0026/2232] fix(compiler-sfc): support using extends interface with defineProps() (#4512) fix #4498 --- .../__snapshots__/compileScript.spec.ts.snap | 27 +++++ .../__tests__/compileScript.spec.ts | 25 ++++ packages/compiler-sfc/src/compileScript.ts | 109 ++++++++++++++---- 3 files changed, 141 insertions(+), 20 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index b7fc9304b26..aa5e2d04926 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -1467,6 +1467,33 @@ export default /*#__PURE__*/_defineComponent({ +return { } +} + +})" +`; + +exports[`SFC compile + + `) + assertCode(content) + expect(content).toMatch(`z: { type: Number, required: true }`) + expect(content).toMatch(`y: { type: String, required: true }`) + expect(content).toMatch(`x: { type: Number, required: false }`) + expect(bindings).toStrictEqual({ + x: BindingTypes.PROPS, + y: BindingTypes.PROPS, + z: BindingTypes.PROPS + }) + }) + test('defineProps w/ exported interface', () => { const { content, bindings } = compile(` + `) + assertCode(content) + expect(content).toMatch(`setup(__props, { expose, emit }) {`) + expect(content).toMatch(`emits: ['foo']`) + }) + test('runtime Enum', () => { const { content, bindings } = compile( ` + `) + assertCode(content) + expect(content).toMatch(`const a = 1;`) // test correct removal + expect(content).toMatch(`props: ['item'],`) + expect(content).toMatch(`emits: ['a'],`) + }) + test('defineProps/defineEmits in multi-variable declaration (full removal)', () => { const { content } = compile(` + `) + expect(bindings).toStrictEqual({ + ref: BindingTypes.SETUP_MAYBE_REF, + reactive: BindingTypes.SETUP_MAYBE_REF, + foo: BindingTypes.SETUP_MAYBE_REF, + bar: BindingTypes.SETUP_MAYBE_REF + }) + }) + + test('import w/ alias', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + _reactive: BindingTypes.SETUP_MAYBE_REF, + _ref: BindingTypes.SETUP_MAYBE_REF, + foo: BindingTypes.SETUP_REF, + bar: BindingTypes.SETUP_REACTIVE_CONST + }) + }) + }) }) // in dev mode, declared bindings are returned as an object from setup() diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 09222396fc8..adc22ab6820 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -129,6 +129,7 @@ export interface SFCScriptCompileOptions { export interface ImportBinding { isType: boolean imported: string + local: string source: string isFromSetup: boolean isUsedInTemplate: boolean @@ -272,7 +273,6 @@ export function compileScript( const bindingMetadata: BindingMetadata = {} const helperImports: Set = new Set() const userImports: Record = Object.create(null) - const userImportAlias: Record = Object.create(null) const scriptBindings: Record = Object.create(null) const setupBindings: Record = Object.create(null) @@ -362,10 +362,6 @@ export function compileScript( isFromSetup: boolean, needTemplateUsageCheck: boolean ) { - if (source === 'vue' && imported) { - userImportAlias[imported] = local - } - // template usage check is only needed in non-inline mode, so we can skip // the work if inlineTemplate is true. let isUsedInTemplate = needTemplateUsageCheck @@ -382,6 +378,7 @@ export function compileScript( userImports[local] = { isType, imported: imported || 'default', + local, source, isFromSetup, isUsedInTemplate @@ -990,7 +987,7 @@ export function compileScript( } } if (node.declaration) { - walkDeclaration(node.declaration, scriptBindings, userImportAlias) + walkDeclaration(node.declaration, scriptBindings, userImports) } } else if ( (node.type === 'VariableDeclaration' || @@ -999,7 +996,7 @@ export function compileScript( node.type === 'TSEnumDeclaration') && !node.declare ) { - walkDeclaration(node, scriptBindings, userImportAlias) + walkDeclaration(node, scriptBindings, userImports) } } @@ -1199,7 +1196,7 @@ export function compileScript( node.type === 'ClassDeclaration') && !node.declare ) { - walkDeclaration(node, setupBindings, userImportAlias) + walkDeclaration(node, setupBindings, userImports) } // walk statements & named exports / variable declarations for top level @@ -1654,8 +1651,17 @@ function registerBinding( function walkDeclaration( node: Declaration, bindings: Record, - userImportAlias: Record + userImports: Record ) { + function getUserBinding(name: string) { + const binding = Object.values(userImports).find( + binding => binding.source === 'vue' && binding.imported === name + ) + if (binding) return binding.local + else if (!userImports[name]) return name + return undefined + } + if (node.type === 'VariableDeclaration') { const isConst = node.kind === 'const' // export const foo = ... @@ -1669,7 +1675,7 @@ function walkDeclaration( ) if (id.type === 'Identifier') { let bindingType - const userReactiveBinding = userImportAlias['reactive'] || 'reactive' + const userReactiveBinding = getUserBinding('reactive') if (isCallOf(init, userReactiveBinding)) { // treat reactive() calls as let since it's meant to be mutable bindingType = isConst @@ -1685,7 +1691,7 @@ function walkDeclaration( ? BindingTypes.SETUP_REACTIVE_CONST : BindingTypes.SETUP_CONST } else if (isConst) { - if (isCallOf(init, userImportAlias['ref'] || 'ref')) { + if (isCallOf(init, getUserBinding('ref'))) { bindingType = BindingTypes.SETUP_REF } else { bindingType = BindingTypes.SETUP_MAYBE_REF @@ -1982,10 +1988,11 @@ function genRuntimeEmits(emits: Set) { function isCallOf( node: Node | null | undefined, - test: string | ((id: string) => boolean) + test: string | ((id: string) => boolean) | null | undefined ): node is CallExpression { return !!( node && + test && node.type === 'CallExpression' && node.callee.type === 'Identifier' && (typeof test === 'string' @@ -1994,7 +2001,7 @@ function isCallOf( ) } -function canNeverBeRef(node: Node, userReactiveImport: string): boolean { +function canNeverBeRef(node: Node, userReactiveImport?: string): boolean { if (isCallOf(node, userReactiveImport)) { return true } From 8d1f526174db277ae5aa9297a43f20a43e991294 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 8 Nov 2022 17:19:45 +0800 Subject: [PATCH 0063/2232] fix(compiler-sfc): fix binding analysis for aliased late import --- .../__tests__/compileScript.spec.ts | 13 +++ packages/compiler-sfc/src/compileScript.ts | 80 +++++++++++-------- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index 5c03ac17fdb..96160e272d5 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -391,6 +391,19 @@ defineExpose({ foo: 123 }) bar: BindingTypes.SETUP_REACTIVE_CONST }) }) + + test('aliased usage before import site', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + bar: BindingTypes.SETUP_REACTIVE_CONST, + x: BindingTypes.SETUP_CONST + }) + }) }) }) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index adc22ab6820..ccf670725ec 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -354,6 +354,25 @@ export function compileScript( ) } + function hoistNode(node: Statement) { + const start = node.start! + startOffset + let end = node.end! + startOffset + // locate comment + if (node.trailingComments && node.trailingComments.length > 0) { + const lastCommentNode = + node.trailingComments[node.trailingComments.length - 1] + end = lastCommentNode.end + startOffset + } + // locate the end of whitespace between this statement and the next + while (end <= source.length) { + if (!/\s/.test(source.charAt(end))) { + break + } + end++ + } + s.move(start, end, 0) + } + function registerUserImport( source: string, local: string, @@ -891,6 +910,7 @@ export function compileScript( scriptStartOffset! ) + // walk import declarations first for (const node of scriptAst.body) { if (node.type === 'ImportDeclaration') { // record imports for dedupe @@ -910,7 +930,11 @@ export function compileScript( !options.inlineTemplate ) } - } else if (node.type === 'ExportDefaultDeclaration') { + } + } + + for (const node of scriptAst.body) { + if (node.type === 'ExportDefaultDeclaration') { // export default defaultExport = node @@ -1040,39 +1064,9 @@ export function compileScript( ) for (const node of scriptSetupAst.body) { - const start = node.start! + startOffset - let end = node.end! + startOffset - // locate comment - if (node.trailingComments && node.trailingComments.length > 0) { - const lastCommentNode = - node.trailingComments[node.trailingComments.length - 1] - end = lastCommentNode.end + startOffset - } - // locate the end of whitespace between this statement and the next - while (end <= source.length) { - if (!/\s/.test(source.charAt(end))) { - break - } - end++ - } - - // (Dropped) `ref: x` bindings - if ( - node.type === 'LabeledStatement' && - node.label.name === 'ref' && - node.body.type === 'ExpressionStatement' - ) { - error( - `ref sugar using the label syntax was an experimental proposal and ` + - `has been dropped based on community feedback. Please check out ` + - `the new proposal at https://github.com/vuejs/rfcs/discussions/369`, - node - ) - } - if (node.type === 'ImportDeclaration') { // import declarations are moved to top - s.move(start, end, 0) + hoistNode(node) // dedupe imports let removed = 0 @@ -1137,6 +1131,26 @@ export function compileScript( s.remove(node.start! + startOffset, node.end! + startOffset) } } + } + + for (const node of scriptSetupAst.body) { + // already processed + if (node.type === 'ImportDeclaration') continue + + // (Dropped) `ref: x` bindings + // TODO remove when out of experimental + if ( + node.type === 'LabeledStatement' && + node.label.name === 'ref' && + node.body.type === 'ExpressionStatement' + ) { + error( + `ref sugar using the label syntax was an experimental proposal and ` + + `has been dropped based on community feedback. Please check out ` + + `the new proposal at https://github.com/vuejs/rfcs/discussions/369`, + node + ) + } if (node.type === 'ExpressionStatement') { // process `defineProps` and `defineEmit(s)` calls @@ -1268,7 +1282,7 @@ export function compileScript( (node.type === 'VariableDeclaration' && node.declare) ) { recordType(node, declaredTypes) - s.move(start, end, 0) + hoistNode(node) } } } From 6861d2380b87566cbc479e065fb67dfeb8c86c46 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 8 Nov 2022 17:47:47 +0800 Subject: [PATCH 0064/2232] refactor(compiler-sfc): optimize import alias check for binding analysis --- .../__snapshots__/compileScript.spec.ts.snap | 33 +-- .../__tests__/compileScript.spec.ts | 4 +- packages/compiler-sfc/src/compileScript.ts | 214 +++++++++--------- 3 files changed, 127 insertions(+), 124 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index 209bbe1d518..ec94872c560 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -44,9 +44,9 @@ return { a } `; exports[`SFC compile `) @@ -1056,7 +1058,10 @@ const emit = defineEmits(['a', 'b']) `qux: { type: Function, required: false, default() { return 1 } }` ) expect(content).toMatch( - `{ foo: string, bar?: number, baz: boolean, qux(): number }` + `quux: { type: Function, required: false, default() { } }` + ) + expect(content).toMatch( + `{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void }` ) expect(content).toMatch(`const props = __props`) expect(bindings).toStrictEqual({ @@ -1064,6 +1069,7 @@ const emit = defineEmits(['a', 'b']) bar: BindingTypes.PROPS, baz: BindingTypes.PROPS, qux: BindingTypes.PROPS, + quux: BindingTypes.PROPS, props: BindingTypes.SETUP_CONST }) }) diff --git a/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts b/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts index 25fb4bed280..7d35f91abf2 100644 --- a/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts @@ -184,6 +184,24 @@ describe('sfc props transform', () => { assertCode(content) }) + // #6960 + test('computed static key', () => { + const { content, bindings } = compile(` + + + `) + expect(content).not.toMatch(`const { foo } =`) + expect(content).toMatch(`console.log(__props.foo)`) + expect(content).toMatch(`_toDisplayString(__props.foo)`) + assertCode(content) + expect(bindings).toStrictEqual({ + foo: BindingTypes.PROPS + }) + }) + describe('errors', () => { test('should error on deep destructure', () => { expect(() => diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index aba5a22ec8d..d55cb795c77 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -447,18 +447,15 @@ export function compileScript( // props destructure - handle compilation sugar for (const prop of declId.properties) { if (prop.type === 'ObjectProperty') { - if (prop.computed) { + const propKey = resolveObjectKey(prop.key, prop.computed) + + if (!propKey) { error( `${DEFINE_PROPS}() destructure cannot use computed key.`, prop.key ) } - const propKey = - prop.key.type === 'StringLiteral' - ? prop.key.value - : (prop.key as Identifier).name - if (prop.value.type === 'AssignmentPattern') { // default value { foo = 123 } const { left, right } = prop.value @@ -774,7 +771,8 @@ export function compileScript( propsRuntimeDefaults.type === 'ObjectExpression' && propsRuntimeDefaults.properties.every( node => - (node.type === 'ObjectProperty' && !node.computed) || + (node.type === 'ObjectProperty' && + (!node.computed || node.key.type.endsWith('Literal'))) || node.type === 'ObjectMethod' ) ) @@ -795,9 +793,10 @@ export function compileScript( if (destructured) { defaultString = `default: ${destructured}` } else if (hasStaticDefaults) { - const prop = propsRuntimeDefaults!.properties.find( - (node: any) => node.key.name === key - ) as ObjectProperty | ObjectMethod + const prop = propsRuntimeDefaults!.properties.find(node => { + if (node.type === 'SpreadElement') return false + return resolveObjectKey(node.key, node.computed) === key + }) as ObjectProperty | ObjectMethod if (prop) { if (prop.type === 'ObjectProperty') { // prop has corresponding static default value @@ -874,9 +873,13 @@ export function compileScript( m.key.type === 'Identifier' ) { if ( - propsRuntimeDefaults!.properties.some( - (p: any) => p.key.name === (m.key as Identifier).name - ) + propsRuntimeDefaults!.properties.some(p => { + if (p.type === 'SpreadElement') return false + return ( + resolveObjectKey(p.key, p.computed) === + (m.key as Identifier).name + ) + }) ) { res += m.key.name + @@ -2139,16 +2142,9 @@ function analyzeBindingsFromOptions(node: ObjectExpression): BindingMetadata { function getObjectExpressionKeys(node: ObjectExpression): string[] { const keys = [] for (const prop of node.properties) { - if ( - (prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') && - !prop.computed - ) { - if (prop.key.type === 'Identifier') { - keys.push(prop.key.name) - } else if (prop.key.type === 'StringLiteral') { - keys.push(prop.key.value) - } - } + if (prop.type === 'SpreadElement') continue + const key = resolveObjectKey(prop.key, prop.computed) + if (key) keys.push(String(key)) } return keys } @@ -2297,3 +2293,14 @@ export function hmrShouldReload( return false } + +export function resolveObjectKey(node: Node, computed: boolean) { + switch (node.type) { + case 'StringLiteral': + case 'NumericLiteral': + return node.value + case 'Identifier': + if (!computed) return node.name + } + return undefined +} From 5bfe438ef391522bddbe43cd2669061c6a88b03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Wed, 9 Nov 2022 10:58:22 +0800 Subject: [PATCH 0072/2232] fix(compiler-core/v-on): only apply case preservation on native elements (#6902) fix #6900 --- .../__tests__/transforms/vSlot.spec.ts | 53 ++++++++++++++++++- packages/compiler-core/src/transforms/vOn.ts | 4 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts index 93dafe9a25b..c166f8d160a 100644 --- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts @@ -11,7 +11,8 @@ import { VNodeCall, SlotsExpression, ObjectExpression, - SimpleExpressionNode + SimpleExpressionNode, + RenderSlotCall } from '../../src' import { transformElement } from '../../src/transforms/transformElement' import { transformOn } from '../../src/transforms/vOn' @@ -788,6 +789,56 @@ describe('compiler: transform component slots', () => { const { slots } = parseWithSlots(``) expect(slots).toMatchObject(toMatch) }) + + // # fix: #6900 + test('consistent behavior of @xxx:modelValue and @xxx:model-value', () => { + const { root: rootUpper } = parseWithSlots( + `

` + ) + const slotNodeUpper = (rootUpper.codegenNode! as VNodeCall) + .children as ElementNode[] + const propertiesObjUpper = ( + slotNodeUpper[0].codegenNode! as RenderSlotCall + ).arguments[2] + expect(propertiesObjUpper).toMatchObject({ + properties: [ + { + key: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'onFoo:modelValue' + }, + value: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `handler`, + isStatic: false + } + } + ] + }) + + const { root } = parseWithSlots( + `
` + ) + const slotNode = (root.codegenNode! as VNodeCall) + .children as ElementNode[] + const propertiesObj = (slotNode[0].codegenNode! as RenderSlotCall) + .arguments[2] + expect(propertiesObj).toMatchObject({ + properties: [ + { + key: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'onFoo:modelValue' + }, + value: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `handler`, + isStatic: false + } + } + ] + }) + }) }) describe('errors', () => { diff --git a/packages/compiler-core/src/transforms/vOn.ts b/packages/compiler-core/src/transforms/vOn.ts index b4d2e851ca9..9fe8b6ab61c 100644 --- a/packages/compiler-core/src/transforms/vOn.ts +++ b/packages/compiler-core/src/transforms/vOn.ts @@ -48,10 +48,10 @@ export const transformOn: DirectiveTransform = ( rawName = `vnode-${rawName.slice(4)}` } const eventString = - node.tagType === ElementTypes.COMPONENT || + node.tagType !== ElementTypes.ELEMENT || rawName.startsWith('vnode') || !/[A-Z]/.test(rawName) - ? // for component and vnode lifecycle event listeners, auto convert + ? // for non-element and vnode lifecycle event listeners, auto convert // it to camelCase. See issue #2249 toHandlerKey(camelize(rawName)) : // preserve case for plain element listeners that have uppercase From 8a882ce0a10bfa5482d6b8a9b68fd49cff4f6937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Wed, 9 Nov 2022 11:12:54 +0800 Subject: [PATCH 0073/2232] fix(compiler-sfc): handle method shorthand syntax in withDefaults (#6972) fix #6971 --- .../__snapshots__/compileScript.spec.ts.snap | 6 ++++-- .../compiler-sfc/__tests__/compileScript.spec.ts | 16 ++++++++++++++-- packages/compiler-sfc/src/compileScript.ts | 4 +++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index 55659cb93d3..d553fb04b3b 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -1744,12 +1744,14 @@ export default /*#__PURE__*/_defineComponent({ bar: { type: Number, required: false }, baz: { type: Boolean, required: true }, qux: { type: Function, required: false, default() { return 1 } }, - quux: { type: Function, required: false, default() { } } + quux: { type: Function, required: false, default() { } }, + quuxx: { type: Promise, required: false, async default() { return await Promise.resolve('hi') } }, + fred: { type: String, required: false, get default() { return 'fred' } } }, setup(__props: any, { expose }) { expose(); -const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number, quux(): void }; +const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number, quux(): void, quuxx: Promise, fred: string }; diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index c3ba6067648..bf562defd24 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -1041,10 +1041,14 @@ const emit = defineEmits(['a', 'b']) baz: boolean; qux?(): number; quux?(): void + quuxx?: Promise; + fred?: string }>(), { foo: 'hi', qux() { return 1 }, - ['quux']() { } + ['quux']() { }, + async quuxx() { return await Promise.resolve('hi') }, + get fred() { return 'fred' } }) `) @@ -1061,7 +1065,13 @@ const emit = defineEmits(['a', 'b']) `quux: { type: Function, required: false, default() { } }` ) expect(content).toMatch( - `{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void }` + `quuxx: { type: Promise, required: false, async default() { return await Promise.resolve('hi') } }` + ) + expect(content).toMatch( + `fred: { type: String, required: false, get default() { return 'fred' } }` + ) + expect(content).toMatch( + `{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void, quuxx: Promise, fred: string }` ) expect(content).toMatch(`const props = __props`) expect(bindings).toStrictEqual({ @@ -1070,6 +1080,8 @@ const emit = defineEmits(['a', 'b']) baz: BindingTypes.PROPS, qux: BindingTypes.PROPS, quux: BindingTypes.PROPS, + quuxx: BindingTypes.PROPS, + fred: BindingTypes.PROPS, props: BindingTypes.SETUP_CONST }) }) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index d55cb795c77..e944b764725 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -805,7 +805,9 @@ export function compileScript( prop.value.end! )}` } else { - defaultString = `default() ${scriptSetupSource.slice( + defaultString = `${prop.async ? 'async ' : ''}${ + prop.kind !== 'method' ? `${prop.kind} ` : '' + }default() ${scriptSetupSource.slice( prop.body.start!, prop.body.end! )}` From 2c27556fe5fa8ba991dd55c766a92d3a50fbf8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Wed, 9 Nov 2022 11:22:29 +0800 Subject: [PATCH 0074/2232] fix(compiler): avoid namespace collisions when transforming template refs in inline mode (#6975) fix #6964 --- .../transforms/transformElement.spec.ts | 26 +++++++++++++++++++ .../src/transforms/transformElement.ts | 26 ++++++++++--------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts index 43bd2589df1..06fd2e12b19 100644 --- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts @@ -1063,6 +1063,32 @@ describe('compiler: element transform', () => { }) }) + test('script setup inline mode template ref (binding does not exist but props with the same name exist)', () => { + const { node } = parseWithElementTransform(``, { + inline: true, + bindingMetadata: { + msg: BindingTypes.PROPS, + ref: BindingTypes.SETUP_CONST + } + }) + expect(node.props).toMatchObject({ + type: NodeTypes.JS_OBJECT_EXPRESSION, + properties: [ + { + type: NodeTypes.JS_PROPERTY, + key: { + content: 'ref', + isStatic: true + }, + value: { + content: 'msg', + isStatic: true + } + } + ] + }) + }) + test('HYDRATE_EVENTS', () => { // ignore click events (has dedicated fast path) const { node } = parseWithElementTransform(`
`, { diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index 7b53b24822c..57f84f9f750 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -497,19 +497,21 @@ export function buildProps( // in inline mode there is no setupState object, so we can't use string // keys to set the ref. Instead, we need to transform it to pass the // actual ref instead. - if ( - !__BROWSER__ && - value && - context.inline && - context.bindingMetadata[value.content] - ) { - isStatic = false - properties.push( - createObjectProperty( - createSimpleExpression('ref_key', true), - createSimpleExpression(value.content, true, value.loc) + if (!__BROWSER__ && value && context.inline) { + const binding = context.bindingMetadata[value.content] + if ( + binding === BindingTypes.SETUP_LET || + binding === BindingTypes.SETUP_REF || + binding === BindingTypes.SETUP_MAYBE_REF + ) { + isStatic = false + properties.push( + createObjectProperty( + createSimpleExpression('ref_key', true), + createSimpleExpression(value.content, true, value.loc) + ) ) - ) + } } } // skip is on , or is="vue:xxx" From 57c901383792176fd7267b7d34d845088dbeff63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Wed, 9 Nov 2022 11:30:05 +0800 Subject: [PATCH 0075/2232] fix(compiler-sfc): only escape parsing-breaking characters in v-bind css var names (#6816) close #6803 --- .../__snapshots__/cssVars.spec.ts.snap | 8 ++++---- .../compiler-sfc/__tests__/cssVars.spec.ts | 20 +++++++++++++------ packages/compiler-sfc/src/cssVars.ts | 6 +++++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap index 49b0d712a7a..4df59cc3428 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap @@ -75,9 +75,9 @@ export default { _useCssVars(_ctx => ({ \\"xxxxxxxx-foo\\": (_unref(foo)), - \\"xxxxxxxx-foo____px_\\": (_unref(foo) + 'px'), - \\"xxxxxxxx-_a___b____2____px_\\": ((_unref(a) + _unref(b)) / 2 + 'px'), - \\"xxxxxxxx-__a___b______2___a_\\": (((_unref(a) + _unref(b))) / (2 * _unref(a))) + \\"xxxxxxxx-foo\\\\ \\\\+\\\\ \\\\'px\\\\'\\": (_unref(foo) + 'px'), + \\"xxxxxxxx-\\\\(a\\\\ \\\\+\\\\ b\\\\)\\\\ \\\\/\\\\ 2\\\\ \\\\+\\\\ \\\\'px\\\\'\\": ((_unref(a) + _unref(b)) / 2 + 'px'), + \\"xxxxxxxx-\\\\(\\\\(a\\\\ \\\\+\\\\ b\\\\)\\\\)\\\\ \\\\/\\\\ \\\\(2\\\\ \\\\*\\\\ a\\\\)\\": (((_unref(a) + _unref(b))) / (2 * _unref(a))) })) let a = 100 @@ -133,7 +133,7 @@ import { useCssVars as _useCssVars } from 'vue' const __injectCSSVars__ = () => { _useCssVars(_ctx => ({ \\"xxxxxxxx-color\\": (_ctx.color), - \\"xxxxxxxx-font_size\\": (_ctx.font.size) + \\"xxxxxxxx-font\\\\.size\\": (_ctx.font.size) }))} const __setup__ = __default__.setup __default__.setup = __setup__ diff --git a/packages/compiler-sfc/__tests__/cssVars.spec.ts b/packages/compiler-sfc/__tests__/cssVars.spec.ts index 9d3df069e76..ffa5d4e798b 100644 --- a/packages/compiler-sfc/__tests__/cssVars.spec.ts +++ b/packages/compiler-sfc/__tests__/cssVars.spec.ts @@ -12,7 +12,7 @@ describe('CSS vars injection', () => { ) expect(content).toMatch(`_useCssVars(_ctx => ({ "${mockId}-color": (_ctx.color), - "${mockId}-font_size": (_ctx.font.size) + "${mockId}-font\\.size": (_ctx.font.size) })`) assertCode(content) }) @@ -79,6 +79,10 @@ describe('CSS vars injection', () => { source: `.foo { color: v-bind(color); font-size: v-bind('font.size'); + + font-weight: v-bind(_φ); + font-size: v-bind(1-字号); + font-family: v-bind(フォント); }`, filename: 'test.css', id: 'data-v-test' @@ -86,7 +90,11 @@ describe('CSS vars injection', () => { expect(code).toMatchInlineSnapshot(` ".foo { color: var(--test-color); - font-size: var(--test-font_size); + font-size: var(--test-font\\\\.size); + + font-weight: var(--test-_φ); + font-size: var(--test-1-字号); + font-family: var(--test-フォント); }" `) }) @@ -225,10 +233,10 @@ describe('CSS vars injection', () => { ) expect(content).toMatch(`_useCssVars(_ctx => ({ "${mockId}-foo": (_unref(foo)), - "${mockId}-foo____px_": (_unref(foo) + 'px'), - "${mockId}-_a___b____2____px_": ((_unref(a) + _unref(b)) / 2 + 'px'), - "${mockId}-__a___b______2___a_": (((_unref(a) + _unref(b))) / (2 * _unref(a))) -})`) + "${mockId}-foo\\ \\+\\ \\'px\\'": (_unref(foo) + 'px'), + "${mockId}-\\(a\\ \\+\\ b\\)\\ \\/\\ 2\\ \\+\\ \\'px\\'": ((_unref(a) + _unref(b)) / 2 + 'px'), + "${mockId}-\\(\\(a\\ \\+\\ b\\)\\)\\ \\/\\ \\(2\\ \\*\\ a\\)": (((_unref(a) + _unref(b))) / (2 * _unref(a))) +}))`) assertCode(content) }) diff --git a/packages/compiler-sfc/src/cssVars.ts b/packages/compiler-sfc/src/cssVars.ts index 10f9bb480f1..c7c04f83d72 100644 --- a/packages/compiler-sfc/src/cssVars.ts +++ b/packages/compiler-sfc/src/cssVars.ts @@ -30,7 +30,11 @@ function genVarName(id: string, raw: string, isProd: boolean): string { if (isProd) { return hash(id + raw) } else { - return `${id}-${raw.replace(/([^\w-])/g, '_')}` + // escape ASCII Punctuation & Symbols + return `${id}-${raw.replace( + /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, + s => `\\${s}` + )}` } } From f023d49a4999da5ac822fe47f266b00d9a75f43e Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 9 Nov 2022 11:36:09 +0800 Subject: [PATCH 0076/2232] release: v3.2.42 --- CHANGELOG.md | 48 +++++++++++++++++++ package.json | 2 +- packages/compiler-core/package.json | 4 +- packages/compiler-dom/package.json | 6 +-- packages/compiler-sfc/package.json | 12 ++--- packages/compiler-ssr/package.json | 6 +-- packages/reactivity-transform/package.json | 6 +-- packages/reactivity/package.json | 4 +- packages/runtime-core/package.json | 6 +-- packages/runtime-dom/package.json | 6 +-- packages/runtime-test/package.json | 6 +-- packages/server-renderer/package.json | 8 ++-- packages/sfc-playground/package.json | 4 +- packages/shared/package.json | 2 +- packages/size-check/package.json | 2 +- packages/template-explorer/package.json | 2 +- packages/vue-compat/package.json | 4 +- packages/vue/package.json | 12 ++--- pnpm-lock.yaml | 54 +++++++++++----------- 19 files changed, 121 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fef732631a..de27f3fa800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,51 @@ +## [3.2.42](https://github.com/vuejs/core/compare/v3.2.41...v3.2.42) (2022-11-09) + + +### Bug Fixes + +* **compiler-core/v-on:** only apply case preservation on native elements ([#6902](https://github.com/vuejs/core/issues/6902)) ([5bfe438](https://github.com/vuejs/core/commit/5bfe438ef391522bddbe43cd2669061c6a88b03a)), closes [#6900](https://github.com/vuejs/core/issues/6900) +* **compiler-core/v-on:** support inline handler with return type annotation ([#6769](https://github.com/vuejs/core/issues/6769)) ([bcfe480](https://github.com/vuejs/core/commit/bcfe480d75d822111c0d3e5fcbc7e10e073d53dc)), closes [#6378](https://github.com/vuejs/core/issues/6378) +* **compiler-core:** avoid duplicate keys in codegen with `v-if` ([#6689](https://github.com/vuejs/core/issues/6689)) ([640cfce](https://github.com/vuejs/core/commit/640cfce4ff808fdfc419058f39a2ff4874a25899)), closes [#6641](https://github.com/vuejs/core/issues/6641) +* **compiler-core:** fix parsing error on comments between v-if in prod ([dd3354c](https://github.com/vuejs/core/commit/dd3354c4c709c0d76e651bb9202158434619cb6a)), closes [#6843](https://github.com/vuejs/core/issues/6843) +* **compiler-core:** keep whitespaces between interpolation and comment ([#6828](https://github.com/vuejs/core/issues/6828)) ([4887618](https://github.com/vuejs/core/commit/48876182dbe5ef88a65a0aa7377e882c735b6104)), closes [#6352](https://github.com/vuejs/core/issues/6352) +* **compiler-sfc:** add semicolon after `defineProps` statement ([#6461](https://github.com/vuejs/core/issues/6461)) ([b72a4af](https://github.com/vuejs/core/commit/b72a4af38a402447d19b4616d09935c390d0702f)), closes [#6428](https://github.com/vuejs/core/issues/6428) +* **compiler-sfc:** allow type annotation for defineEmits variable ([#5394](https://github.com/vuejs/core/issues/5394)) ([eab7604](https://github.com/vuejs/core/commit/eab76046e3b1779dd7a856b6b974e928075d6a1e)), closes [#5393](https://github.com/vuejs/core/issues/5393) +* **compiler-sfc:** check import source during binding analysation ([#6826](https://github.com/vuejs/core/issues/6826)) ([4a00fdd](https://github.com/vuejs/core/commit/4a00fddfed16caee7a1e07756b9c110bc928c17a)), closes [#6825](https://github.com/vuejs/core/issues/6825) +* **compiler-sfc:** fix binding analysis for aliased late import ([8d1f526](https://github.com/vuejs/core/commit/8d1f526174db277ae5aa9297a43f20a43e991294)) +* **compiler-sfc:** fix macro usage in multi-variable declaration ([#6778](https://github.com/vuejs/core/issues/6778)) ([99b6697](https://github.com/vuejs/core/commit/99b6697fb44dd1094ea0bf372c1d05214ffb92a2)), closes [#6757](https://github.com/vuejs/core/issues/6757) +* **compiler-sfc:** handle method shorthand syntax in withDefaults ([#6972](https://github.com/vuejs/core/issues/6972)) ([8a882ce](https://github.com/vuejs/core/commit/8a882ce0a10bfa5482d6b8a9b68fd49cff4f6937)), closes [#6971](https://github.com/vuejs/core/issues/6971) +* **compiler-sfc:** only escape parsing-breaking characters in v-bind css var names ([#6816](https://github.com/vuejs/core/issues/6816)) ([57c9013](https://github.com/vuejs/core/commit/57c901383792176fd7267b7d34d845088dbeff63)), closes [#6803](https://github.com/vuejs/core/issues/6803) +* **compiler-sfc:** require `) - expect(content).toMatch(`return { vMyDir }`) + expect(content).toMatch(`return { get vMyDir() { return vMyDir } }`) assertCode(content) }) @@ -459,7 +464,9 @@ defineExpose({ foo: 123 })
`) - expect(content).toMatch(`return { cond, bar, baz }`) + expect(content).toMatch( + `return { cond, get bar() { return bar }, get baz() { return baz } }` + ) assertCode(content) }) @@ -475,7 +482,9 @@ defineExpose({ foo: 123 }) // x: used in interpolation // y: should not be matched by {{ yy }} or 'y' in binding exps // x$y: #4274 should escape special chars when creating Regex - expect(content).toMatch(`return { x, z, x$y }`) + expect(content).toMatch( + `return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }` + ) assertCode(content) }) @@ -490,7 +499,9 @@ defineExpose({ foo: 123 }) `) // VAR2 should not be matched - expect(content).toMatch(`return { VAR, VAR3 }`) + expect(content).toMatch( + `return { get VAR() { return VAR }, get VAR3() { return VAR3 } }` + ) assertCode(content) }) @@ -505,7 +516,9 @@ defineExpose({ foo: 123 }) `) - expect(content).toMatch(`return { FooBaz, Last }`) + expect(content).toMatch( + `return { get FooBaz() { return FooBaz }, get Last() { return Last } }` + ) assertCode(content) }) @@ -524,7 +537,7 @@ defineExpose({ foo: 123 })
`) - expect(content).toMatch(`return { a, b, Baz }`) + expect(content).toMatch(`return { a, b, get Baz() { return Baz } }`) assertCode(content) }) @@ -1301,7 +1314,7 @@ const emit = defineEmits(['a', 'b']) import { type Bar, Baz } from './main.ts' ` ) - expect(content).toMatch(`return { Baz }`) + expect(content).toMatch(`return { get Baz() { return Baz } }`) assertCode(content) }) }) diff --git a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts index 6d601379749..8ae5275661e 100644 --- a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts @@ -33,7 +33,8 @@ describe('sfc ref transform', () => { expect(content).toMatch(`let c = () => {}`) expect(content).toMatch(`let d`) expect(content).toMatch( - `return { foo, a, b, get c() { return c }, get d() { return d }, ref, shallowRef }` + `return { foo, a, b, get c() { return c }, set c(v) { c = v }, ` + + `get d() { return d }, set d(v) { d = v }, ref, shallowRef }` ) assertCode(content) expect(bindings).toStrictEqual({ diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 74e3dbd7349..7f6b087a268 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -1486,8 +1486,20 @@ export function compileScript( } returned = `{ ` for (const key in allBindings) { - if (bindingMetadata[key] === BindingTypes.SETUP_LET) { + if ( + allBindings[key] === true && + userImports[key].source !== 'vue' && + !userImports[key].source.endsWith('.vue') + ) { + // generate getter for import bindings + // skip vue imports since we know they will never change returned += `get ${key}() { return ${key} }, ` + } else if (bindingMetadata[key] === BindingTypes.SETUP_LET) { + // local let binding, also add setter + const setArg = key === 'v' ? `_v` : `v` + returned += + `get ${key}() { return ${key} }, ` + + `set ${key}(${setArg}) { ${key} = ${setArg} }, ` } else { returned += `${key}, ` } From a54bff2c9c8e1d908b4a0f3826ac715c9a35e68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=AB=E9=9B=A8=E6=B0=B4=E8=BF=87=E6=BB=A4=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=B0=94=28Rairn=29?= <958414905@qq.com> Date: Thu, 10 Nov 2022 18:01:31 +0800 Subject: [PATCH 0097/2232] fix(hmr/keep-alive): fix error in reload component (#7049) fix #7042 --- packages/runtime-core/__tests__/hmr.spec.ts | 67 +++++++++++++++++++ .../runtime-core/src/components/KeepAlive.ts | 4 +- packages/runtime-core/src/vnode.ts | 8 +++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index eaef8d401a7..4b501052ce4 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -151,6 +151,73 @@ describe('hot module replacement', () => { expect(mountSpy).toHaveBeenCalledTimes(1) }) + // #7042 + test('reload KeepAlive slot', async () => { + const root = nodeOps.createElement('div') + const childId = 'test-child-keep-alive' + const unmountSpy = jest.fn() + const mountSpy = jest.fn() + const activeSpy = jest.fn() + const deactiveSpy = jest.fn() + + const Child: ComponentOptions = { + __hmrId: childId, + data() { + return { count: 0 } + }, + unmounted: unmountSpy, + render: compileToFunction(`
{{ count }}
`) + } + createRecord(childId, Child) + + const Parent: ComponentOptions = { + components: { Child }, + data() { + return { toggle: true } + }, + render: compileToFunction( + `` + ) + } + + render(h(Parent), root) + expect(serializeInner(root)).toBe(`
0
`) + + reload(childId, { + __hmrId: childId, + data() { + return { count: 1 } + }, + mounted: mountSpy, + unmounted: unmountSpy, + activated: activeSpy, + deactivated: deactiveSpy, + render: compileToFunction(`
{{ count }}
`) + }) + await nextTick() + expect(serializeInner(root)).toBe(`
1
`) + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(1) + expect(deactiveSpy).toHaveBeenCalledTimes(0) + + // should not unmount when toggling + triggerEvent(root.children[1] as TestElement, 'click') + await nextTick() + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(1) + expect(deactiveSpy).toHaveBeenCalledTimes(1) + + // should not mount when toggling + triggerEvent(root.children[1] as TestElement, 'click') + await nextTick() + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(2) + expect(deactiveSpy).toHaveBeenCalledTimes(1) + }) + test('reload class component', async () => { const root = nodeOps.createElement('div') const childId = 'test4-child' diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index 9605d79150c..3fec48140fc 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -31,7 +31,6 @@ import { invokeArrayFns } from '@vue/shared' import { watch } from '../apiWatch' -import { hmrDirtyComponents } from '../hmr' import { RendererInternals, queuePostRenderEffect, @@ -281,8 +280,7 @@ const KeepAliveImpl: ComponentOptions = { if ( (include && (!name || !matches(include, name))) || - (exclude && name && matches(exclude, name)) || - (__DEV__ && hmrDirtyComponents.has(comp)) + (exclude && name && matches(exclude, name)) ) { current = vnode return rawVNode diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 7d8017e650a..41f848e44de 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -357,6 +357,14 @@ export function isSameVNodeType(n1: VNode, n2: VNode): boolean { n2.shapeFlag & ShapeFlags.COMPONENT && hmrDirtyComponents.has(n2.type as ConcreteComponent) ) { + // #7042, ensure the vnode being unmounted during HMR + if (n1.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { + n1.shapeFlag -= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + } + // #7042, ensure the vnode being mounted as fresh during HMR + if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { + n2.shapeFlag -= ShapeFlags.COMPONENT_KEPT_ALIVE + } // HMR only: if the component has been hot-updated, force a reload. return false } From 4049ffcf29dc12dca71f682edf0b422a5c502e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=AB=E9=9B=A8=E6=B0=B4=E8=BF=87=E6=BB=A4=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=B0=94=28Rairn=29?= <958414905@qq.com> Date: Thu, 10 Nov 2022 18:03:10 +0800 Subject: [PATCH 0098/2232] fix(runtime-core): fix move/removal of static fragments containing text nodes (#6858) fix #6852 --- .../__tests__/rendererFragment.spec.ts | 36 +++++++++++++++++++ packages/runtime-core/src/renderer.ts | 4 +++ 2 files changed, 40 insertions(+) diff --git a/packages/runtime-core/__tests__/rendererFragment.spec.ts b/packages/runtime-core/__tests__/rendererFragment.spec.ts index 93140f13f4e..1de73ef632c 100644 --- a/packages/runtime-core/__tests__/rendererFragment.spec.ts +++ b/packages/runtime-core/__tests__/rendererFragment.spec.ts @@ -315,4 +315,40 @@ describe('renderer: fragment', () => { `
two
one
` ) }) + + // #6852 + test('`template` keyed fragment w/ text', () => { + const root = nodeOps.createElement('div') + + const renderFn = (items: string[]) => { + return ( + openBlock(true), + createBlock( + Fragment, + null, + renderList(items, item => { + return ( + openBlock(), + createBlock( + Fragment, + { key: item }, + [ + createTextVNode('text'), + createVNode('div', null, item, PatchFlags.TEXT) + ], + PatchFlags.STABLE_FRAGMENT + ) + ) + }), + PatchFlags.KEYED_FRAGMENT + ) + ) + } + + render(renderFn(['one', 'two']), root) + expect(serializeInner(root)).toBe(`text
one
text
two
`) + + render(renderFn(['two', 'one']), root) + expect(serializeInner(root)).toBe(`text
two
text
one
`) + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index c043492e1df..4a6d8993a49 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2386,6 +2386,10 @@ export function traverseStaticChildren(n1: VNode, n2: VNode, shallow = false) { } if (!shallow) traverseStaticChildren(c1, c2) } + // #6852 also inherit for text nodes + if (c2.type === Text) { + c2.el = c1.el + } // 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) { From 845efbbb5d0bc8ba476983d9716668b28a3b8e6f Mon Sep 17 00:00:00 2001 From: Daniel Khalil Date: Fri, 11 Nov 2022 02:12:40 +0100 Subject: [PATCH 0099/2232] chore: escape html tag in change log (#7089) [ci skip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afcf7169771..7057c13aa60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ * **compiler-sfc:** fix macro usage in multi-variable declaration ([#6778](https://github.com/vuejs/core/issues/6778)) ([99b6697](https://github.com/vuejs/core/commit/99b6697fb44dd1094ea0bf372c1d05214ffb92a2)), closes [#6757](https://github.com/vuejs/core/issues/6757) * **compiler-sfc:** handle method shorthand syntax in withDefaults ([#6972](https://github.com/vuejs/core/issues/6972)) ([8a882ce](https://github.com/vuejs/core/commit/8a882ce0a10bfa5482d6b8a9b68fd49cff4f6937)), closes [#6971](https://github.com/vuejs/core/issues/6971) * **compiler-sfc:** only escape parsing-breaking characters in v-bind css var names ([#6816](https://github.com/vuejs/core/issues/6816)) ([57c9013](https://github.com/vuejs/core/commit/57c901383792176fd7267b7d34d845088dbeff63)), closes [#6803](https://github.com/vuejs/core/issues/6803) -* **compiler-sfc:** require