From 7449c7626debbdbeb87c7828920206fe620ebfd4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 16 Sep 2020 00:21:34 +0000 Subject: [PATCH 01/12] build(deps-dev): bump @types/node from 14.10.1 to 14.10.2 (#2122) --- yarn.lock | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 531ed2c16a3..cd2df31c9e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -886,21 +886,16 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= -"@types/node@*": - version "13.11.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7" - integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g== +"@types/node@*", "@types/node@^14.10.1": + version "14.10.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.10.2.tgz#9b47a2c8e4dabd4db73b57e750b24af689600514" + integrity sha512-IzMhbDYCpv26pC2wboJ4MMOa9GKtjplXfcAqrMeNJpUUwpM/2ATt2w1JPUXwS6spu856TvKZL2AOmeU2rAxskw== "@types/node@10.17.13": version "10.17.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== -"@types/node@^14.10.1": - version "14.10.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.10.1.tgz#cc323bad8e8a533d4822f45ce4e5326f36e42177" - integrity sha512-aYNbO+FZ/3KGeQCEkNhHFRIzBOUgc7QvcVNKXbfnhDkSfwUv91JsQQa10rDgKSTSLkXZ1UIyPe4FJJNVgw1xWQ== - "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" From 066d514d757fb7e8844104210d7d04cc11598fef Mon Sep 17 00:00:00 2001 From: underfin Date: Wed, 16 Sep 2020 21:28:31 +0800 Subject: [PATCH 02/12] feat(compiler-sfc): `additionalData` support for css preprocessors (#2126) close https://github.com/vitejs/vite/issues/520 --- .../__tests__/compileStyle.spec.ts | 30 ++++++++++++++++++- .../compiler-sfc/src/stylePreprocessors.ts | 18 +++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileStyle.spec.ts b/packages/compiler-sfc/__tests__/compileStyle.spec.ts index 46a34463907..ce83747033b 100644 --- a/packages/compiler-sfc/__tests__/compileStyle.spec.ts +++ b/packages/compiler-sfc/__tests__/compileStyle.spec.ts @@ -337,7 +337,7 @@ describe('SFC style preprocessors', () => { ]) }) - test('scss respect user-defined options.additionalData', () => { + test('scss respect user-defined string options.additionalData', () => { const res = compileStyle({ preprocessOptions: { additionalData: ` @@ -358,4 +358,32 @@ describe('SFC style preprocessors', () => { expect(res.errors.length).toBe(0) }) + + test('scss respect user-defined function options.additionalData', () => { + const source = ` + .square { + @include square(100px); + } + ` + const filename = path.resolve(__dirname, './fixture/test.scss') + const res = compileStyle({ + preprocessOptions: { + additionalData: (s: string, f: string) => { + expect(s).toBe(source) + expect(f).toBe(filename) + return ` + @mixin square($size) { + width: $size; + height: $size; + }` + } + }, + source, + filename, + id: '', + preprocessLang: 'scss' + }) + + expect(res.errors.length).toBe(0) + }) }) diff --git a/packages/compiler-sfc/src/stylePreprocessors.ts b/packages/compiler-sfc/src/stylePreprocessors.ts index a6287dfc7e6..1a4e7b7c87f 100644 --- a/packages/compiler-sfc/src/stylePreprocessors.ts +++ b/packages/compiler-sfc/src/stylePreprocessors.ts @@ -1,12 +1,14 @@ import merge from 'merge-source-map' import { RawSourceMap } from 'source-map' import { SFCStyleCompileOptions } from './compileStyle' +import { isFunction } from '@vue/shared' export type StylePreprocessor = ( source: string, map: RawSourceMap | undefined, options: { [key: string]: any + additionalData?: string | ((source: string, filename: string) => string) filename: string }, customRequire: SFCStyleCompileOptions['preprocessCustomRequire'] @@ -24,7 +26,7 @@ const scss: StylePreprocessor = (source, map, options, load = require) => { const nodeSass = load('sass') const finalOptions = { ...options, - data: (options.additionalData || '') + source, + data: getSource(source, options.filename, options.additionalData), file: options.filename, outFile: options.filename, sourceMap: !!map @@ -66,7 +68,7 @@ const less: StylePreprocessor = (source, map, options, load = require) => { let result: any let error: Error | null = null nodeLess.render( - source, + getSource(source, options.filename, options.additionalData), { ...options, syncImport: true }, (err: Error | null, output: any) => { error = err @@ -117,6 +119,18 @@ const styl: StylePreprocessor = (source, map, options, load = require) => { } } +function getSource( + source: string, + filename: string, + additionalData?: string | ((source: string, filename: string) => string) +) { + if (!additionalData) return source + if (isFunction(additionalData)) { + return additionalData(source, filename) + } + return additionalData + source +} + export type PreprocessLang = 'less' | 'sass' | 'scss' | 'styl' | 'stylus' export const processors: Record = { From be27bbc5ad4eccdb28525e078a1afe20dd9e8dbd Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Wed, 16 Sep 2020 14:29:42 +0100 Subject: [PATCH 03/12] types(defineComponent): fix missing exported types (#2124) --- packages/runtime-core/src/index.ts | 5 ++++- test-dts/defineComponent.test-d.tsx | 23 ++++++++++++++++++++++- test-dts/tsconfig.build.json | 10 +++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index bc6cedb517b..75dd69fbe8f 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -174,8 +174,11 @@ export { ComponentOptionsWithArrayProps, ComponentCustomOptions, ComponentOptionsBase, - RenderFunction + RenderFunction, + MethodOptions, + ComputedOptions } from './componentOptions' +export { EmitsOptions, ObjectEmitsOptions } from './componentEmits' export { ComponentPublicInstance, ComponentCustomProperties diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 59d6a3bcb28..98f8018e271 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -10,7 +10,8 @@ import { expectType, ComponentPublicInstance, ComponentOptions, - SetupContext + SetupContext, + h } from './index' describe('with object props', () => { @@ -900,3 +901,23 @@ describe('async setup', () => { // setup context properties should be mutable vm.a = 2 }) + +// check if defineComponent can be exported +export default { + // function components + a: defineComponent(_ => h('div')), + // no props + b: defineComponent({ + data() { + return {} + } + }), + c: defineComponent({ + props: ['a'] + }), + d: defineComponent({ + props: { + a: Number + } + }) +} diff --git a/test-dts/tsconfig.build.json b/test-dts/tsconfig.build.json index 0ed6e46e00c..6747eb10b1a 100644 --- a/test-dts/tsconfig.build.json +++ b/test-dts/tsconfig.build.json @@ -1,10 +1,18 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "noEmit": false, "paths": { "@vue/*": ["../packages/*/dist"], "vue": ["../packages/vue/dist"] } }, - "exclude": ["../packages/*/__tests__", "../packages/*/src"] + "exclude": ["../packages/*/__tests__", "../packages/*/src"], + "include": [ + "../packages/global.d.ts", + "../packages/*/dist", + "../packages/runtime-dom/types/jsx.d.ts", + "../packages/*/__tests__", + "../test-dts" + ] } From 7e68ddd35401ff52ecb6b2b4b7028029d1bad5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Exbrayat?= Date: Wed, 16 Sep 2020 15:30:47 +0200 Subject: [PATCH 04/12] chore: typos in suspense events (#2131) Removes the mention of the recede event that has been replaced. --- packages/runtime-core/src/components/Suspense.ts | 2 +- test-dts/tsx.test-d.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/components/Suspense.ts b/packages/runtime-core/src/components/Suspense.ts index 129e62c2f8f..16204b6d0e0 100644 --- a/packages/runtime-core/src/components/Suspense.ts +++ b/packages/runtime-core/src/components/Suspense.ts @@ -502,7 +502,7 @@ function createSuspenseBoundary( optimized } = suspense - // invoke @recede event + // invoke @fallback event const onFallback = vnode.props && vnode.props.onFallback if (isFunction(onFallback)) { onFallback() diff --git a/test-dts/tsx.test-d.tsx b/test-dts/tsx.test-d.tsx index 70db5f40158..43a2464b859 100644 --- a/test-dts/tsx.test-d.tsx +++ b/test-dts/tsx.test-d.tsx @@ -49,6 +49,8 @@ expectError() // Suspense expectType() expectType() -expectType( {}} onFallback={() => {}} />) +expectType( + {}} onFallback={() => {}} onPending={() => {}} /> +) // @ts-expect-error expectError() From 89e9ab8a2a387f26a370848db0b1ffb1d0ab9549 Mon Sep 17 00:00:00 2001 From: wonderful-panda Date: Wed, 16 Sep 2020 23:09:35 +0900 Subject: [PATCH 05/12] fix(types/tsx): optional props from Mixin/Extends are treated as required (#2048) --- .../runtime-core/src/apiDefineComponent.ts | 23 +++--- packages/runtime-core/src/componentOptions.ts | 72 ++++++++++++++++--- packages/runtime-core/src/componentProps.ts | 31 ++++---- .../src/componentPublicInstance.ts | 24 +++++-- packages/runtime-core/src/index.ts | 6 +- test-dts/defineComponent.test-d.tsx | 23 +++++- 6 files changed, 133 insertions(+), 46 deletions(-) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index f3156332b45..ef08d3c6533 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -13,7 +13,11 @@ import { AllowedComponentProps, ComponentCustomProps } from './component' -import { ExtractPropTypes, ComponentPropsOptions } from './componentProps' +import { + ExtractPropTypes, + ComponentPropsOptions, + ExtractDefaultPropTypes +} from './componentProps' import { EmitsOptions } from './componentEmits' import { isFunction } from '@vue/shared' import { VNodeProps } from './vnode' @@ -37,11 +41,11 @@ export type DefineComponent< E extends EmitsOptions = Record, EE extends string = string, PP = PublicProps, - RequiredProps = Readonly>, - OptionalProps = Readonly> + Props = Readonly>, + Defaults = ExtractDefaultPropTypes > = ComponentPublicInstanceConstructor< CreateComponentPublicInstance< - OptionalProps, + Props, RawBindings, D, C, @@ -49,12 +53,14 @@ export type DefineComponent< Mixin, Extends, E, - PP & OptionalProps + PP & Props, + Defaults, + true > & - RequiredProps + Props > & ComponentOptionsBase< - RequiredProps, + Props, RawBindings, D, C, @@ -62,7 +68,8 @@ export type DefineComponent< Mixin, Extends, E, - EE + EE, + Defaults > & PP diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 4d6291c9527..ff499d36cdc 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -42,7 +42,11 @@ import { WritableComputedOptions, toRaw } from '@vue/reactivity' -import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps' +import { + ComponentObjectPropsOptions, + ExtractPropTypes, + ExtractDefaultPropTypes +} from './componentProps' import { EmitsOptions } from './componentEmits' import { Directive } from './directives' import { @@ -81,7 +85,8 @@ export interface ComponentOptionsBase< Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin, E extends EmitsOptions, - EE extends string = string + EE extends string = string, + Defaults = {} > extends LegacyOptions, ComponentInternalOptions, @@ -148,6 +153,8 @@ export interface ComponentOptionsBase< __isFragment?: never __isTeleport?: never __isSuspense?: never + + __defaults?: Defaults } export type ComponentOptionsWithoutProps< @@ -159,8 +166,20 @@ export type ComponentOptionsWithoutProps< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, - EE extends string = string -> = ComponentOptionsBase & { + EE extends string = string, + Defaults = {} +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Defaults +> & { props?: undefined } & ThisType< CreateComponentPublicInstance< @@ -172,7 +191,9 @@ export type ComponentOptionsWithoutProps< Mixin, Extends, E, - Readonly + Readonly, + Defaults, + false > > @@ -187,7 +208,18 @@ export type ComponentOptionsWithArrayProps< E extends EmitsOptions = EmitsOptions, EE extends string = string, Props = Readonly<{ [key in PropNames]?: any }> -> = ComponentOptionsBase & { +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + {} +> & { props: PropNames[] } & ThisType< CreateComponentPublicInstance< @@ -212,8 +244,20 @@ export type ComponentOptionsWithObjectProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - Props = Readonly> -> = ComponentOptionsBase & { + Props = Readonly>, + Defaults = ExtractDefaultPropTypes +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Defaults +> & { props: PropsOptions & ThisType } & ThisType< CreateComponentPublicInstance< @@ -224,7 +268,10 @@ export type ComponentOptionsWithObjectProps< M, Mixin, Extends, - E + E, + Props, + Defaults, + false > > @@ -261,6 +308,7 @@ export type ComponentOptionsMixin = ComponentOptionsBase< any, any, any, + any, any > @@ -347,20 +395,22 @@ interface LegacyOptions< delimiters?: [string, string] } -export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' +export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'Defaults' export type OptionTypesType< P = {}, B = {}, D = {}, C extends ComputedOptions = {}, - M extends MethodOptions = {} + M extends MethodOptions = {}, + Defaults = {} > = { P: P B: B D: D C: C M: M + Defaults: Defaults } const enum OptionTypes { diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index a8bccd9a5af..0fde127a28f 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -63,18 +63,15 @@ type PropMethod = T extends (...args: any) => any // if i ? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor : never -type RequiredKeys = { - [K in keyof T]: T[K] extends - | { required: true } - | (MakeDefaultRequired extends true ? { default: any } : never) - ? K - : never +type RequiredKeys = { + [K in keyof T]: T[K] extends { required: true } | { default: any } ? K : never }[keyof T] -type OptionalKeys = Exclude< - keyof T, - RequiredKeys -> +type OptionalKeys = Exclude> + +type DefaultKeys = { + [K in keyof T]: T[K] extends { default: any } ? K : never +}[keyof T] type InferPropType = T extends null ? any // null & true would fail to infer @@ -86,12 +83,9 @@ type InferPropType = T extends null ? boolean : T extends Prop ? (unknown extends V ? D : V) : T -export type ExtractPropTypes< - O, - MakeDefaultRequired extends boolean = true -> = O extends object - ? { [K in RequiredKeys]: InferPropType } & - { [K in OptionalKeys]?: InferPropType } +export type ExtractPropTypes = O extends object + ? { [K in RequiredKeys]: InferPropType } & + { [K in OptionalKeys]?: InferPropType } : { [K in string]: any } const enum BooleanFlags { @@ -99,6 +93,11 @@ const enum BooleanFlags { shouldCastTrue } +// extract props which defined with default from prop options +export type ExtractDefaultPropTypes = O extends object + ? { [K in DefaultKeys]: InferPropType } + : {} + type NormalizedProp = | null | (PropOptions & { diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 2a9fa747339..e0e239a7997 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -77,9 +77,11 @@ type MixinToOptionTypes = T extends ComponentOptionsBase< infer M, infer Mixin, infer Extends, - any + any, + any, + infer Defaults > - ? OptionTypesType

& + ? OptionTypesType

& IntersectionMixin & IntersectionMixin : never @@ -130,6 +132,8 @@ export type CreateComponentPublicInstance< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, PublicProps = P, + Defaults = {}, + MakeDefaultsOptional extends boolean = false, PublicMixin = IntersectionMixin & IntersectionMixin, PublicP = UnwrapMixinsType & EnsureNonVoid

, PublicB = UnwrapMixinsType & EnsureNonVoid, @@ -137,7 +141,9 @@ export type CreateComponentPublicInstance< PublicC extends ComputedOptions = UnwrapMixinsType & EnsureNonVoid, PublicM extends MethodOptions = UnwrapMixinsType & - EnsureNonVoid + EnsureNonVoid, + PublicDefaults = UnwrapMixinsType & + EnsureNonVoid > = ComponentPublicInstance< PublicP, PublicB, @@ -146,7 +152,9 @@ export type CreateComponentPublicInstance< PublicM, E, PublicProps, - ComponentOptionsBase + PublicDefaults, + MakeDefaultsOptional, + ComponentOptionsBase > // public properties exposed on the proxy, which is used as the render context @@ -159,11 +167,15 @@ export type ComponentPublicInstance< M extends MethodOptions = {}, E extends EmitsOptions = {}, PublicProps = P, - Options = ComponentOptionsBase + Defaults = {}, + MakeDefaultsOptional extends boolean = false, + Options = ComponentOptionsBase > = { $: ComponentInternalInstance $data: D - $props: P & PublicProps + $props: MakeDefaultsOptional extends true + ? Partial & Omit

+ : P & PublicProps $attrs: Data $refs: Data $slots: Slots diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 75dd69fbe8f..26c27d544e6 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -41,7 +41,7 @@ export { } from './apiLifecycle' export { provide, inject } from './apiInject' export { nextTick } from './scheduler' -export { defineComponent, DefineComponent } from './apiDefineComponent' +export { defineComponent } from './apiDefineComponent' export { defineAsyncComponent } from './apiAsyncComponent' // Advanced API ---------------------------------------------------------------- @@ -166,6 +166,7 @@ export { ComponentCustomProps, AllowedComponentProps } from './component' +export { DefineComponent } from './apiDefineComponent' export { ComponentOptions, ComponentOptionsMixin, @@ -198,7 +199,8 @@ export { PropType, ComponentPropsOptions, ComponentObjectPropsOptions, - ExtractPropTypes + ExtractPropTypes, + ExtractDefaultPropTypes } from './componentProps' export { Directive, diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 98f8018e271..f201d660d87 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -597,7 +597,11 @@ describe('extends with mixins', () => { type: String, default: 'mP1' }, - mP2: Boolean + mP2: Boolean, + mP3: { + type: Boolean, + required: true + } }, data() { return { @@ -611,6 +615,10 @@ describe('extends with mixins', () => { p2: { type: Number, default: 2 + }, + p3: { + type: Boolean, + required: true } }, data() { @@ -663,11 +671,20 @@ describe('extends with mixins', () => { }) // Test TSX - expectType() + expectType() + + // mP1, mP2, p1, and p2 have default value. these are not required + expectType() // missing required props // @ts-expect-error - expectError() + expectError() + // missing required props from mixin + // @ts-expect-error + expectError() + // missing required props from extends + // @ts-expect-error + expectError() // wrong prop types // @ts-expect-error From 3810de7d6bd0044177f043285228c2e988093883 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 16 Sep 2020 10:52:31 -0400 Subject: [PATCH 06/12] fix(reactivity): effect shoud only recursively self trigger with explicit options fix #2125 --- packages/reactivity/src/effect.ts | 7 +++- .../runtime-core/__tests__/apiWatch.spec.ts | 13 +++++++ .../__tests__/rendererComponent.spec.ts | 35 ++++++++++++++++++- packages/runtime-core/src/renderer.ts | 8 ++--- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 495d6714939..2d2b4d2a1be 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -25,6 +25,7 @@ export interface ReactiveEffectOptions { onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void onStop?: () => void + allowRecurse?: boolean } export type DebuggerEvent = { @@ -178,7 +179,11 @@ export function trigger( const effects = new Set() const add = (effectsToAdd: Set | undefined) => { if (effectsToAdd) { - effectsToAdd.forEach(effect => effects.add(effect)) + effectsToAdd.forEach(effect => { + if (effect !== activeEffect || effect.options.allowRecurse) { + effects.add(effect) + } + }) } } diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 138a3e04f5d..7b64e50bd5f 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -779,4 +779,17 @@ describe('api: watch', () => { // should trigger now expect(sideEffect).toBe(2) }) + + // #2125 + test('watchEffect should not recursively trigger itself', async () => { + const spy = jest.fn() + const price = ref(10) + const history = ref([]) + watchEffect(() => { + history.value.push(price.value) + spy() + }) + await nextTick() + expect(spy).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/runtime-core/__tests__/rendererComponent.spec.ts b/packages/runtime-core/__tests__/rendererComponent.spec.ts index 243e4cbd0be..4253779d4cf 100644 --- a/packages/runtime-core/__tests__/rendererComponent.spec.ts +++ b/packages/runtime-core/__tests__/rendererComponent.spec.ts @@ -5,7 +5,10 @@ import { nodeOps, serializeInner, nextTick, - VNode + VNode, + provide, + inject, + Ref } from '@vue/runtime-test' describe('renderer: component', () => { @@ -104,4 +107,34 @@ describe('renderer: component', () => { ) expect(Comp1.updated).not.toHaveBeenCalled() }) + + // #2043 + test('component child synchronously updating parent state should trigger parent re-render', async () => { + const App = { + setup() { + const n = ref(0) + provide('foo', n) + return () => { + return [h('div', n.value), h(Child)] + } + } + } + + const Child = { + setup() { + const n = inject>('foo')! + n.value++ + + return () => { + return h('div', n.value) + } + } + } + + const root = nodeOps.createElement('div') + render(h(App), root) + expect(serializeInner(root)).toBe(`

0
1
`) + await nextTick() + expect(serializeInner(root)).toBe(`
1
1
`) + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index f38fc9da134..485dcf15077 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -44,7 +44,6 @@ import { flushPostFlushCbs, invalidateJob, flushPreFlushCbs, - SchedulerJob, SchedulerCb } from './scheduler' import { effect, stop, ReactiveEffectOptions, isRef } from '@vue/reactivity' @@ -261,7 +260,9 @@ export const enum MoveType { } const prodEffectOptions = { - scheduler: queueJob + scheduler: queueJob, + // #1801, #2043 component render effects should allow recursive updates + allowRecurse: true } function createDevEffectOptions( @@ -269,6 +270,7 @@ function createDevEffectOptions( ): ReactiveEffectOptions { return { scheduler: queueJob, + allowRecurse: true, onTrack: instance.rtc ? e => invokeArrayFns(instance.rtc!, e) : void 0, onTrigger: instance.rtg ? e => invokeArrayFns(instance.rtg!, e) : void 0 } @@ -1489,8 +1491,6 @@ function baseCreateRenderer( } } }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions) - // #1801 mark it to allow recursive updates - ;(instance.update as SchedulerJob).allowRecurse = true } const updateComponentPreRender = ( From bad0ecb9106dbf7186a45ee5369518b857534ab8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 16 Sep 2020 10:53:02 -0400 Subject: [PATCH 07/12] workflow: temporarily disable no-unused-vars eslint rule for arguments --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index caa5c7213ca..98f42a74b9a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,7 +11,7 @@ module.exports = { 'error', // we are only using this rule to check for unused arguments since TS // catches unused variables but not args. - { varsIgnorePattern: '.*', args: 'after-used', argsIgnorePattern: '^_' } + { varsIgnorePattern: '.*', args: 'none' } ], // most of the codebase are expected to be env agnostic 'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals], From c7b4a379cf8627c79a01d61039d3e3b283477dc1 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 16 Sep 2020 11:10:16 -0400 Subject: [PATCH 08/12] fix(runtime-core/async-component): fix error component when there are no error handlers fix #2129 --- .../__tests__/apiAsyncComponent.spec.ts | 45 +++++++++++++++++++ .../runtime-core/src/apiAsyncComponent.ts | 9 +++- packages/runtime-core/src/errorHandling.ts | 20 ++++++--- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts b/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts index 0ba6079ab3d..af78ee7e0c9 100644 --- a/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts +++ b/packages/runtime-core/__tests__/apiAsyncComponent.spec.ts @@ -206,6 +206,51 @@ describe('api: defineAsyncComponent', () => { expect(serializeInner(root)).toBe('resolved') }) + // #2129 + test('error with error component, without global handler', async () => { + let resolve: (comp: Component) => void + let reject: (e: Error) => void + const Foo = defineAsyncComponent({ + loader: () => + new Promise((_resolve, _reject) => { + resolve = _resolve as any + reject = _reject + }), + errorComponent: (props: { error: Error }) => props.error.message + }) + + const toggle = ref(true) + const root = nodeOps.createElement('div') + const app = createApp({ + render: () => (toggle.value ? h(Foo) : null) + }) + + app.mount(root) + expect(serializeInner(root)).toBe('') + + const err = new Error('errored out') + reject!(err) + await timeout() + expect(serializeInner(root)).toBe('errored out') + expect( + 'Unhandled error during execution of async component loader' + ).toHaveBeenWarned() + + toggle.value = false + await nextTick() + expect(serializeInner(root)).toBe('') + + // errored out on previous load, toggle and mock success this time + toggle.value = true + await nextTick() + expect(serializeInner(root)).toBe('') + + // should render this time + resolve!(() => 'resolved') + await timeout() + expect(serializeInner(root)).toBe('resolved') + }) + test('error with error + loading components', async () => { let resolve: (comp: Component) => void let reject: (e: Error) => void diff --git a/packages/runtime-core/src/apiAsyncComponent.ts b/packages/runtime-core/src/apiAsyncComponent.ts index 3f668a46242..a4c46867442 100644 --- a/packages/runtime-core/src/apiAsyncComponent.ts +++ b/packages/runtime-core/src/apiAsyncComponent.ts @@ -117,7 +117,12 @@ export function defineAsyncComponent< const onError = (err: Error) => { pendingRequest = null - handleError(err, instance, ErrorCodes.ASYNC_COMPONENT_LOADER) + handleError( + err, + instance, + ErrorCodes.ASYNC_COMPONENT_LOADER, + !errorComponent /* do not throw in dev if user provided error component */ + ) } // suspense-controlled or SSR. @@ -152,7 +157,7 @@ export function defineAsyncComponent< if (timeout != null) { setTimeout(() => { - if (!loaded.value) { + if (!loaded.value && !error.value) { const err = new Error( `Async component timed out after ${timeout}ms.` ) diff --git a/packages/runtime-core/src/errorHandling.ts b/packages/runtime-core/src/errorHandling.ts index a922e964e5d..b9896c58e19 100644 --- a/packages/runtime-core/src/errorHandling.ts +++ b/packages/runtime-core/src/errorHandling.ts @@ -99,7 +99,8 @@ export function callWithAsyncErrorHandling( export function handleError( err: unknown, instance: ComponentInternalInstance | null, - type: ErrorTypes + type: ErrorTypes, + throwInDev = true ) { const contextVNode = instance ? instance.vnode : null if (instance) { @@ -131,10 +132,15 @@ export function handleError( return } } - logError(err, type, contextVNode) + logError(err, type, contextVNode, throwInDev) } -function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) { +function logError( + err: unknown, + type: ErrorTypes, + contextVNode: VNode | null, + throwInDev = true +) { if (__DEV__) { const info = ErrorTypeStrings[type] if (contextVNode) { @@ -144,8 +150,12 @@ function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) { if (contextVNode) { popWarningContext() } - // crash in dev so it's more noticeable - throw err + // crash in dev by default so it's more noticeable + if (throwInDev) { + throw err + } else { + console.error(err) + } } else { // recover in prod to reduce the impact on end-user console.error(err) From bebd44f793ccd13bfdf90c7e45eac320a340650c Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 16 Sep 2020 12:18:03 -0400 Subject: [PATCH 09/12] fix(runtime-core): ensure root stable fragments inherit elements for moving fix #2134 --- packages/runtime-core/src/renderer.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 485dcf15077..6d01e3e5147 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1157,7 +1157,12 @@ function baseCreateRenderer( ) // #2080 if the stable fragment has a key, it's a