From 2b698fb24916141dbb7f1488c3f7bebc6a5134eb Mon Sep 17 00:00:00 2001 From: Daishi Kato Date: Tue, 14 Jun 2022 23:00:07 +0900 Subject: [PATCH] chore: run prettier for docs (#1007) --- .github/workflows/lint-and-type.yml | 2 + .husky/.gitignore | 1 - .husky/pre-commit | 4 - docs/typescript.md | 308 +++++++++++++----------- docs/v4-migration.md | 2 +- package.json | 11 +- readme.md | 360 +++++++++++++++------------- yarn.lock | 251 +------------------ 8 files changed, 378 insertions(+), 561 deletions(-) delete mode 100644 .husky/.gitignore delete mode 100755 .husky/pre-commit diff --git a/.github/workflows/lint-and-type.yml b/.github/workflows/lint-and-type.yml index cd1d25872b..70a1336d04 100644 --- a/.github/workflows/lint-and-type.yml +++ b/.github/workflows/lint-and-type.yml @@ -18,6 +18,8 @@ jobs: - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - run: yarn install --frozen-lockfile --check-files - run: cd examples && yarn install --frozen-lockfile --check-files + - name: Prettier + run: yarn prettier:ci - name: Lint run: yarn eslint:ci - name: Type diff --git a/.husky/.gitignore b/.husky/.gitignore deleted file mode 100644 index 31354ec138..0000000000 --- a/.husky/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_ diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index d2ae35e84b..0000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -yarn lint-staged diff --git a/docs/typescript.md b/docs/typescript.md index 6e5d16818a..26faf73511 100644 --- a/docs/typescript.md +++ b/docs/typescript.md @@ -5,7 +5,7 @@ When using TypeScript you just have to make a tiny change that instead of writing `create(...)` you'll have to write `create()(...)` where `T` would be type of the state so as to annotate it. Example... ```ts -import create from "zustand" +import create from 'zustand' interface BearState { bears: number @@ -23,50 +23,51 @@ const useStore = create()((set) => ({
- **TLDR**: Because state generic `T` is invariant. - - Consider this minimal version `create`... +**TLDR**: Because state generic `T` is invariant. - ```ts - declare const create: (f: (get: () => T) => T) => T +Consider this minimal version `create`... - const x = create((get) => ({ - foo: 0, - bar: () => get() - })) - // `x` is inferred as `unknown` instead of - // interface X { - // foo: number, - // bar: () => X - // } - ``` +```ts +declare const create: (f: (get: () => T) => T) => T - Here if you look at the type of `f` in `create` ie `(get: () => T) => T` it "gives" `T` as it returns `T` but then it also "takes" `T` via `get` so where does `T` come from TypeScript thinks... It's a like that chicken or egg problem. At the end TypeScript gives up and infers `T` as `unknown`. +const x = create((get) => ({ + foo: 0, + bar: () => get(), +})) +// `x` is inferred as `unknown` instead of +// interface X { +// foo: number, +// bar: () => X +// } +``` - So as long as the generic to be inferred is invariant TypeScript won't be able to infer it. Another simple example would be this... +Here if you look at the type of `f` in `create` ie `(get: () => T) => T` it "gives" `T` as it returns `T` but then it also "takes" `T` via `get` so where does `T` come from TypeScript thinks... It's a like that chicken or egg problem. At the end TypeScript gives up and infers `T` as `unknown`. - ```ts - declare const createFoo: (f: (t: T) => T) => T - const x = createFoo(_ => "hello") - ``` +So as long as the generic to be inferred is invariant TypeScript won't be able to infer it. Another simple example would be this... - Here again `x` is `unknown` instead of `string`. - - Now one can argue it's impossible to write an implementation for `createFoo`, and that's true. But then it's also impossible to write Zustand's `create`... Wait but Zustand exists? So what do I mean by that? +```ts +declare const createFoo: (f: (t: T) => T) => T +const x = createFoo((_) => 'hello') +``` - The thing is Zustand is lying in it's type, the simplest way to prove it by showing unsoundness. Consider this example... +Here again `x` is `unknown` instead of `string`. - ```ts - import create from "zustand/vanilla" +Now one can argue it's impossible to write an implementation for `createFoo`, and that's true. But then it's also impossible to write Zustand's `create`... Wait but Zustand exists? So what do I mean by that? - const useStore = create<{ foo: number }>()((_, get) => ({ - foo: get().foo, - })) - ``` +The thing is Zustand is lying in it's type, the simplest way to prove it by showing unsoundness. Consider this example... - This code compiles, but guess what happens when you run it? You'll get an exception "Uncaught TypeError: Cannot read properties of undefined (reading 'foo') because after all `get` would return `undefined` before the initial state is created (hence kids don't call `get` when creating the initial state). But the types tell that get is `() => { foo: number }` which is exactly the lie I was taking about, `get` is that eventually but first it's `() => undefined`. +```ts +import create from 'zustand/vanilla' + +const useStore = create<{ foo: number }>()((_, get) => ({ + foo: get().foo, +})) +``` + +This code compiles, but guess what happens when you run it? You'll get an exception "Uncaught TypeError: Cannot read properties of undefined (reading 'foo') because after all `get` would return `undefined` before the initial state is created (hence kids don't call `get` when creating the initial state). But the types tell that get is `() => { foo: number }` which is exactly the lie I was taking about, `get` is that eventually but first it's `() => undefined`. + +Okay we're quite deep in the rabbit hole haha, long story short zustand has a bit crazy runtime behavior that can't be typed in a sound way and inferrable way. We could make it inferrable with the right TypeScript features that don't exist today. And hey that tiny bit of unsoundness is not a problem. - Okay we're quite deep in the rabbit hole haha, long story short zustand has a bit crazy runtime behavior that can't be typed in a sound way and inferrable way. We could make it inferrable with the right TypeScript features that don't exist today. And hey that tiny bit of unsoundness is not a problem.
@@ -76,36 +77,42 @@ const useStore = create()((set) => ({ **TLDR**: It's a workaround for [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571). - Imagine you have a scenario like this... +Imagine you have a scenario like this... - ```ts - declare const withError: (p: Promise) => - Promise<[error: undefined, value: T] | [error: E, value: undefined]> - declare const doSomething: () => Promise +```ts +declare const withError: ( + p: Promise +) => Promise<[error: undefined, value: T] | [error: E, value: undefined]> +declare const doSomething: () => Promise - const main = async () => { - let [error, value] = await withError(doSomething()) - } - ``` +const main = async () => { + let [error, value] = await withError(doSomething()) +} +``` - Here `T` is inferred as `string` and `E` is inferred as `unknown`. Now for some reason you want to annotate `E` as `Foo` because you're certain what shape of error `doSomething()` would throw. But too bad you can't do that, you can either pass all generics or none. So now along with annotating `E` as `Foo` you'll also have to annotate `T` as `string` which gets inferred anyway. So what to do? What you do is make a curried version of `withError` that does nothing in runtime, it's purpose is to just allow you annotate `E`... +Here `T` is inferred as `string` and `E` is inferred as `unknown`. Now for some reason you want to annotate `E` as `Foo` because you're certain what shape of error `doSomething()` would throw. But too bad you can't do that, you can either pass all generics or none. So now along with annotating `E` as `Foo` you'll also have to annotate `T` as `string` which gets inferred anyway. So what to do? What you do is make a curried version of `withError` that does nothing in runtime, it's purpose is to just allow you annotate `E`... - ```ts - declare const withError: { - (): (p: Promise) => - Promise<[error: undefined, value: T] | [error: E, value: undefined]> - (p: Promise): - Promise<[error: undefined, value: T] | [error: E, value: undefined]> - } - declare const doSomething: () => Promise - interface Foo { bar: string } +```ts +declare const withError: { + (): ( + p: Promise + ) => Promise<[error: undefined, value: T] | [error: E, value: undefined]> + (p: Promise): Promise< + [error: undefined, value: T] | [error: E, value: undefined] + > +} +declare const doSomething: () => Promise +interface Foo { + bar: string +} - const main = async () => { - let [error, value] = await withError()(doSomething()) - } - ``` +const main = async () => { + let [error, value] = await withError()(doSomething()) +} +``` + +And now `T` gets inferred and you get to annotate `E` too. Zustand has the same use case we want to annotate the state (the first type parameter) but allow the rest type parameters to get inferred. - And now `T` gets inferred and you get to annotate `E` too. Zustand has the same use case we want to annotate the state (the first type parameter) but allow the rest type parameters to get inferred.
Alternatively you can also use `combine` which infers the state instead of you having to type it... @@ -124,11 +131,12 @@ const useStore = create(combine({ bears: 0 }, (set) => ({
- We achieve the inference by lying a little in the types of `set`, `get` and `store` that you receive as parameters. The lie is that they're typed in a way as if the state is the first parameter only when in fact the state is the shallow-merge (`{ ...a, ...b }`) of both first parameter and the second parameter's return. So for example `get` from the second parameter has type `() => { bears: number }` and that's a lie as it should be `() => { bears: number, increase: (by: number) => void }`. And `useStore` still has the correct type, ie for example `useStore.getState` is typed as `() => { bears: number, increase: (by: number) => void }`. +We achieve the inference by lying a little in the types of `set`, `get` and `store` that you receive as parameters. The lie is that they're typed in a way as if the state is the first parameter only when in fact the state is the shallow-merge (`{ ...a, ...b }`) of both first parameter and the second parameter's return. So for example `get` from the second parameter has type `() => { bears: number }` and that's a lie as it should be `() => { bears: number, increase: (by: number) => void }`. And `useStore` still has the correct type, ie for example `useStore.getState` is typed as `() => { bears: number, increase: (by: number) => void }`. - It's not a lie lie because `{ bears: number }` is still a subtype `{ bears: number, increase: (by: number) => void }`, so in most cases there won't be a problem. Just you have to be careful while using replace. For eg `set({ bears: 0 }, true)` would compile but will be unsound as it'll delete the `increase` function. (If you set from "outside" ie `useStore.setState({ bears: 0 }, true)` then it won't compile because the "outside" store knows that `increase` is missing.) Another instance where you should be careful you're doing `Object.keys`, `Object.keys(get())` will return `["bears", "increase"]` and not `["bears"]` (the return type of `get` can make you fall for this). +It's not a lie lie because `{ bears: number }` is still a subtype `{ bears: number, increase: (by: number) => void }`, so in most cases there won't be a problem. Just you have to be careful while using replace. For eg `set({ bears: 0 }, true)` would compile but will be unsound as it'll delete the `increase` function. (If you set from "outside" ie `useStore.setState({ bears: 0 }, true)` then it won't compile because the "outside" store knows that `increase` is missing.) Another instance where you should be careful you're doing `Object.keys`, `Object.keys(get())` will return `["bears", "increase"]` and not `["bears"]` (the return type of `get` can make you fall for this). + +So `combine` trades-off a little type-safety for the convenience of not having to write a type for state. Hence you should use `combine` accordingly, usually it's not a big deal and it's okay to use it. - So `combine` trades-off a little type-safety for the convenience of not having to write a type for state. Hence you should use `combine` accordingly, usually it's not a big deal and it's okay to use it. Also note that we're not using the curried version when using `combine` because `combine` "creates" the state. When using a middleware that creates the state, it's not necessary to use the curried version because the state now can be inferred. Another middleware that creates state is `redux`. So when using `combine`, `redux` or any other custom middleware that creates the state, it's not recommended to use the curried version. @@ -138,37 +146,43 @@ Also note that we're not using the curried version when using `combine` because You don't have to do anything special to use middlewares in TypeScript. ```ts -import create from "zustand" -import { devtools, persist } from "zustand/middleware" +import create from 'zustand' +import { devtools, persist } from 'zustand/middleware' interface BearState { bears: number increase: (by: number) => void } -const useStore = create()(devtools(persist((set) => ({ - bears: 0, - increase: (by) => set((state) => ({ bears: state.bears + by })), -})))) +const useStore = create()( + devtools( + persist((set) => ({ + bears: 0, + increase: (by) => set((state) => ({ bears: state.bears + by })), + })) + ) +) ``` Just make sure you're using them immediately inside `create` so as to make the contextual inference work. Doing something even remotely fancy like the following `myMiddlewares` would require more advanced types. ```ts -import create from "zustand" -import { devtools, persist } from "zustand/middleware" +import create from 'zustand' +import { devtools, persist } from 'zustand/middleware' -const myMiddlewares = f => devtools(persist(f)) +const myMiddlewares = (f) => devtools(persist(f)) interface BearState { bears: number increase: (by: number) => void } -const useStore = create()(myMiddlewares((set) => ({ - bears: 0, - increase: (by) => set((state) => ({ bears: state.bears + by })), -}))) +const useStore = create()( + myMiddlewares((set) => ({ + bears: 0, + increase: (by) => set((state) => ({ bears: state.bears + by })), + })) +) ``` ## Authoring middlewares and advanced usage @@ -176,14 +190,14 @@ const useStore = create()(myMiddlewares((set) => ({ Imagine you had to write this hypothetical middleware... ```ts -import create from "zustand" +import create from 'zustand' const foo = (f, bar) => (set, get, store) => { store.foo = bar - return f(set, get, store); + return f(set, get, store) } -const useStore = create(foo(() => ({ bears: 0 }), "hello")) +const useStore = create(foo(() => ({ bears: 0 }), 'hello')) console.log(useStore.foo.toUpperCase()) ``` @@ -198,24 +212,21 @@ If you're eager to know what the answer is to this particular problem then it's ### Middleware that does not change the store type ```ts -import create, { State, StateCreator, StoreMutatorIdentifier } from "zustand" - -type Logger = - < T extends State - , Mps extends [StoreMutatorIdentifier, unknown][] = [] - , Mcs extends [StoreMutatorIdentifier, unknown][] = [] - > - ( f: StateCreator - , name?: string - ) => - StateCreator - -type LoggerImpl = - - ( f: PopArgument> - , name?: string - ) => - PopArgument> +import create, { State, StateCreator, StoreMutatorIdentifier } from 'zustand' + +type Logger = < + T extends State, + Mps extends [StoreMutatorIdentifier, unknown][] = [], + Mcs extends [StoreMutatorIdentifier, unknown][] = [] +>( + f: StateCreator, + name?: string +) => StateCreator + +type LoggerImpl = ( + f: PopArgument>, + name?: string +) => PopArgument> const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => { type T = ReturnType @@ -230,34 +241,45 @@ const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => { export const logger = loggerImpl as unknown as Logger -type PopArgument unknown> = - T extends (...a: [...infer A, infer _]) => infer R - ? (...a: A) => R - : never +type PopArgument unknown> = T extends ( + ...a: [...infer A, infer _] +) => infer R + ? (...a: A) => R + : never // --- -const useStore = create()(logger((set) => ({ - bears: 0, - increase: (by) => set((state) => ({ bears: state.bears + by })), -}), "bear-store")) +const useStore = create()( + logger( + (set) => ({ + bears: 0, + increase: (by) => set((state) => ({ bears: state.bears + by })), + }), + 'bear-store' + ) +) ``` ### Middleware that changes the store type ```ts -import create, { State, StateCreator, StoreMutatorIdentifier, Mutate, StoreApi } from "zustand" - -type Foo = - < T extends State - , A - , Mps extends [StoreMutatorIdentifier, unknown][] = [] - , Mcs extends [StoreMutatorIdentifier, unknown][] = [] - > - ( f: StateCreator - , bar: A - ) => - StateCreator +import create, { + State, + StateCreator, + StoreMutatorIdentifier, + Mutate, + StoreApi, +} from 'zustand' + +type Foo = < + T extends State, + A, + Mps extends [StoreMutatorIdentifier, unknown][] = [], + Mcs extends [StoreMutatorIdentifier, unknown][] = [] +>( + f: StateCreator, + bar: A +) => StateCreator declare module 'zustand' { interface StoreMutators { @@ -265,11 +287,10 @@ declare module 'zustand' { } } -type FooImpl = - - ( f: PopArgument> - , bar: A - ) => PopArgument> +type FooImpl = ( + f: PopArgument>, + bar: A +) => PopArgument> const fooImpl: FooImpl = (f, bar) => (set, get, _store) => { type T = ReturnType @@ -282,20 +303,19 @@ const fooImpl: FooImpl = (f, bar) => (set, get, _store) => { export const foo = fooImpl as unknown as Foo -type PopArgument unknown> = - T extends (...a: [...infer A, infer _]) => infer R - ? (...a: A) => R - : never +type PopArgument unknown> = T extends ( + ...a: [...infer A, infer _] +) => infer R + ? (...a: A) => R + : never -type Write = - Omit & U +type Write = Omit & U -type Cast = - T extends U ? T : U; +type Cast = T extends U ? T : U // --- -const useStore = create(foo(() => ({ bears: 0 }), "hello")) +const useStore = create(foo(() => ({ bears: 0 }), 'hello')) console.log(useStore.foo.toUpperCase()) ``` @@ -326,7 +346,7 @@ const useStore = create< ### Independent slices pattern ```ts -import create, { StateCreator } from "zustand" +import create, { StateCreator } from 'zustand' interface BearSlice { bears: number @@ -334,7 +354,7 @@ interface BearSlice { } const createBearSlice: StateCreator = (set) => ({ bears: 0, - addBear: () => set((state) => ({ bears: state.bears + 1 })) + addBear: () => set((state) => ({ bears: state.bears + 1 })), }) interface FishSlice { @@ -343,12 +363,12 @@ interface FishSlice { } const createFishSlice: StateCreator = (set) => ({ fishes: 0, - addFish: () => set((state) => ({ fishes: state.fishes + 1 })) + addFish: () => set((state) => ({ fishes: state.fishes + 1 })), }) const useStore = create()((...a) => ({ ...createBearSlice(...a), - ...createFishSlice(...a) + ...createFishSlice(...a), })) ``` @@ -359,31 +379,41 @@ Also you can even write `StateCreator` instead of `StateCreator void eatFish: () => void } -const createBearSlice: StateCreator = (set) => ({ +const createBearSlice: StateCreator< + BearSlice & FishSlice, + [], + [], + BearSlice +> = (set) => ({ bears: 0, addBear: () => set((state) => ({ bears: state.bears + 1 })), - eatFish: () => set((state) => ({ fishes: state.fishes - 1 })) + eatFish: () => set((state) => ({ fishes: state.fishes - 1 })), }) interface FishSlice { fishes: number addFish: () => void } -const createFishSlice: StateCreator = (set) => ({ +const createFishSlice: StateCreator< + BearSlice & FishSlice, + [], + [], + FishSlice +> = (set) => ({ fishes: 0, - addFish: () => set((state) => ({ fishes: state.fishes + 1 })) + addFish: () => set((state) => ({ fishes: state.fishes + 1 })), }) const useStore = create()((...a) => ({ ...createBearSlice(...a), - ...createFishSlice(...a) + ...createFishSlice(...a), })) ``` diff --git a/docs/v4-migration.md b/docs/v4-migration.md index 800340e670..316bb14020 100644 --- a/docs/v4-migration.md +++ b/docs/v4-migration.md @@ -106,7 +106,7 @@ If you're not passing any type parameters to `useStore` then there is no migrati ## `UseBoundStore` (from `zustand` and `zustand/react`) -### Change +### Change ```diff - type UseBoundStore< diff --git a/package.json b/package.json index 6188c6ff44..ca1d5d4640 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,8 @@ "build:shallow": "rollup -c --config-shallow", "build:context": "rollup -c --config-context", "postbuild": "yarn copy", + "prettier": "prettier '*.{js,json,md}' '{src,tests,docs}/**/*.{ts,tsx,md,mdx}' --write", + "prettier:ci": "prettier '*.{js,json,md}' '{src,tests,docs}/**/*.{ts,tsx,md,mdx}' --list-different", "eslint": "eslint --fix '*.{js,json}' '{src,tests}/**/*.{ts,tsx}'", "eslint:ci": "eslint '*.{js,json}' '{src,tests}/**/*.{ts,tsx}'", "pretest": "tsc --noEmit", @@ -75,7 +77,7 @@ "test:ci": "jest", "test:dev": "jest --watch --no-coverage", "test:coverage:watch": "jest --watch", - "copy": "shx cp -r dist/src/* dist/esm && shx cp -r dist/src/* dist && shx rm -rf dist/src && shx rm -rf dist/{src,tests} && downlevel-dts dist dist/ts3.4 && shx cp package.json readme.md LICENSE dist && json -I -f dist/package.json -e \"this.private=false; this.devDependencies=undefined; this.optionalDependencies=undefined; this.scripts=undefined; this.prettier=undefined; this.jest=undefined; this['lint-staged']=undefined;\"" + "copy": "shx cp -r dist/src/* dist/esm && shx cp -r dist/src/* dist && shx rm -rf dist/src && shx rm -rf dist/{src,tests} && downlevel-dts dist dist/ts3.4 && shx cp package.json readme.md LICENSE dist && json -I -f dist/package.json -e \"this.private=false; this.devDependencies=undefined; this.optionalDependencies=undefined; this.scripts=undefined; this.prettier=undefined; this.jest=undefined;\"" }, "engines": { "node": ">=12.7.0" @@ -88,11 +90,6 @@ "tabWidth": 2, "printWidth": 80 }, - "lint-staged": { - "*.{js,ts,tsx}": [ - "prettier --write" - ] - }, "repository": { "type": "git", "url": "git+https://github.com/pmndrs/zustand.git" @@ -183,12 +180,10 @@ "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.5.0", - "husky": "^7.0.4", "immer": "^9.0.12", "jest": "^28.0.3", "jest-environment-jsdom": "^28.0.2", "json": "^11.0.0", - "lint-staged": "^12.4.1", "prettier": "^2.6.2", "react": "^18.1.0", "react-dom": "^18.1.0", diff --git a/readme.md b/readme.md index cbcac3e348..3d09b9d5a0 100644 --- a/readme.md +++ b/readme.md @@ -20,15 +20,15 @@ npm install zustand # or yarn add zustand ## First create a store -Your store is a hook! You can put anything in it: primitives, objects, functions. The `set` function *merges* state. +Your store is a hook! You can put anything in it: primitives, objects, functions. The `set` function _merges_ state. ```jsx import create from 'zustand' -const useStore = create(set => ({ +const useStore = create((set) => ({ bears: 0, - increasePopulation: () => set(state => ({ bears: state.bears + 1 })), - removeAllBears: () => set({ bears: 0 }) + increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), + removeAllBears: () => set({ bears: 0 }), })) ``` @@ -38,28 +38,28 @@ Use the hook anywhere, no providers needed. Select your state and the component ```jsx function BearCounter() { - const bears = useStore(state => state.bears) + const bears = useStore((state) => state.bears) return

{bears} around here ...

} function Controls() { - const increasePopulation = useStore(state => state.increasePopulation) + const increasePopulation = useStore((state) => state.increasePopulation) return } ``` ### Why zustand over redux? -* Simple and un-opinionated -* Makes hooks the primary means of consuming state -* Doesn't wrap your app in context providers -* [Can inform components transiently (without causing render)](#transient-updates-for-often-occurring-state-changes) +- Simple and un-opinionated +- Makes hooks the primary means of consuming state +- Doesn't wrap your app in context providers +- [Can inform components transiently (without causing render)](#transient-updates-for-often-occurring-state-changes) ### Why zustand over context? -* Less boilerplate -* Renders components only on changes -* Centralized, action-based state management +- Less boilerplate +- Renders components only on changes +- Centralized, action-based state management --- @@ -78,8 +78,8 @@ const state = useStore() It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks. ```jsx -const nuts = useStore(state => state.nuts) -const honey = useStore(state => state.honey) +const nuts = useStore((state) => state.nuts) +const honey = useStore((state) => state.honey) ``` If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can tell zustand that you want the object to be diffed shallowly by passing the `shallow` equality function. @@ -88,20 +88,23 @@ If you want to construct a single object with multiple state-picks inside, simil import shallow from 'zustand/shallow' // Object pick, re-renders the component when either state.nuts or state.honey change -const { nuts, honey } = useStore(state => ({ nuts: state.nuts, honey: state.honey }), shallow) +const { nuts, honey } = useStore( + (state) => ({ nuts: state.nuts, honey: state.honey }), + shallow +) // Array pick, re-renders the component when either state.nuts or state.honey change -const [nuts, honey] = useStore(state => [state.nuts, state.honey], shallow) +const [nuts, honey] = useStore((state) => [state.nuts, state.honey], shallow) // Mapped picks, re-renders the component when state.treats changes in order, count or keys -const treats = useStore(state => Object.keys(state.treats), shallow) +const treats = useStore((state) => Object.keys(state.treats), shallow) ``` For more control over re-rendering, you may provide any custom equality function. ```jsx const treats = useStore( - state => state.treats, + (state) => state.treats, (oldTreats, newTreats) => compare(oldTreats, newTreats) ) ``` @@ -111,13 +114,13 @@ const treats = useStore( The `set` function has a second argument, `false` by default. Instead of merging, it will replace the state model. Be careful not to wipe out parts you rely on, like actions. ```jsx -import omit from "lodash-es/omit" +import omit from 'lodash-es/omit' -const useStore = create(set => ({ +const useStore = create((set) => ({ salmon: 1, tuna: 2, - deleteEverything: () => set({ }, true), // clears the entire store, actions included - deleteTuna: () => set(state => omit(state, ['tuna']), true) + deleteEverything: () => set({}, true), // clears the entire store, actions included + deleteTuna: () => set((state) => omit(state, ['tuna']), true), })) ``` @@ -126,12 +129,12 @@ const useStore = create(set => ({ Just call `set` when you're ready, zustand doesn't care if your actions are async or not. ```jsx -const useStore = create(set => ({ +const useStore = create((set) => ({ fishies: {}, - fetch: async pond => { + fetch: async (pond) => { const response = await fetch(pond) set({ fishies: await response.json() }) - } + }, })) ``` @@ -178,22 +181,34 @@ If you need to subscribe with selector, `subscribeWithSelector` middleware will help. With this middleware `subscribe` accepts an additional signature: + ```ts subscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubscribe ``` ```js import { subscribeWithSelector } from 'zustand/middleware' -const useStore = create(subscribeWithSelector(() => ({ paw: true, snout: true, fur: true }))) +const useStore = create( + subscribeWithSelector(() => ({ paw: true, snout: true, fur: true })) +) // Listening to selected changes, in this case when "paw" changes -const unsub2 = useStore.subscribe(state => state.paw, console.log) +const unsub2 = useStore.subscribe((state) => state.paw, console.log) // Subscribe also exposes the previous value -const unsub3 = useStore.subscribe(state => state.paw, (paw, previousPaw) => console.log(paw, previousPaw)) +const unsub3 = useStore.subscribe( + (state) => state.paw, + (paw, previousPaw) => console.log(paw, previousPaw) +) // Subscribe also supports an optional equality function -const unsub4 = useStore.subscribe(state => [state.paw, state.fur], console.log, { equalityFn: shallow }) +const unsub4 = useStore.subscribe( + (state) => [state.paw, state.fur], + console.log, + { equalityFn: shallow } +) // Subscribe and fire immediately -const unsub5 = useStore.subscribe(state => state.paw, console.log, { fireImmediately: true }) +const unsub5 = useStore.subscribe((state) => state.paw, console.log, { + fireImmediately: true, +}) ``` ## Using zustand without React @@ -241,15 +256,18 @@ Reducing nested structures is tiresome. Have you tried [immer](https://github.co ```jsx import produce from 'immer' -const useStore = create(set => ({ - lush: { forest: { contains: { a: "bear" } } }, - clearForest: () => set(produce(state => { - state.lush.forest.contains = null - })) +const useStore = create((set) => ({ + lush: { forest: { contains: { a: 'bear' } } }, + clearForest: () => + set( + produce((state) => { + state.lush.forest.contains = null + }) + ), })) -const clearForest = useStore(state => state.clearForest) -clearForest(); +const clearForest = useStore((state) => state.clearForest) +clearForest() ``` [Alternatively, there are some other solutions.](https://github.com/pmndrs/zustand/wiki/Updating-nested-state-object-values) @@ -260,16 +278,23 @@ You can functionally compose your store any way you like. ```jsx // Log every time state is changed -const log = (config) => (set, get, api) => config((...args) => { - console.log(" applying", args) - set(...args) - console.log(" new state", get()) -}, get, api) - -const useStore = create(log((set) => ({ - bees: false, - setBees: (input) => set({ bees: input }), -}))) +const log = (config) => (set, get, api) => + config( + (...args) => { + console.log(' applying', args) + set(...args) + console.log(' new state', get()) + }, + get, + api + ) + +const useStore = create( + log((set) => ({ + bees: false, + setBees: (input) => set({ bees: input }), + })) +) ``` ## Persist middleware @@ -277,19 +302,21 @@ const useStore = create(log((set) => ({ You can persist your store's data using any kind of storage. ```jsx -import create from "zustand" -import { persist } from "zustand/middleware" - -const useStore = create(persist( - (set, get) => ({ - fishes: 0, - addAFish: () => set({ fishes: get().fishes + 1 }) - }), - { - name: "food-storage", // unique name - getStorage: () => sessionStorage, // (optional) by default, 'localStorage' is used - } -)) +import create from 'zustand' +import { persist } from 'zustand/middleware' + +const useStore = create( + persist( + (set, get) => ({ + fishes: 0, + addAFish: () => set({ fishes: get().fishes + 1 }), + }), + { + name: 'food-storage', // unique name + getStorage: () => sessionStorage, // (optional) by default, 'localStorage' is used + } + ) +) ``` [See the full documentation for this middleware.](https://github.com/pmndrs/zustand/wiki/Persisting-the-store's-data) @@ -299,33 +326,40 @@ const useStore = create(persist( Immer is available as middleware too. ```jsx -import create from "zustand" -import { immer } from "zustand/middleware/immer" - -const useStore = create(immer((set) => ({ - bees: 0, - addBees: (by) => set((state) => { state.bees += by }), -}))) +import create from 'zustand' +import { immer } from 'zustand/middleware/immer' + +const useStore = create( + immer((set) => ({ + bees: 0, + addBees: (by) => + set((state) => { + state.bees += by + }), + })) +) ``` ## Can't live without redux-like reducers and action types? ```jsx -const types = { increase: "INCREASE", decrease: "DECREASE" } +const types = { increase: 'INCREASE', decrease: 'DECREASE' } const reducer = (state, { type, by = 1 }) => { switch (type) { - case types.increase: return { grumpiness: state.grumpiness + by } - case types.decrease: return { grumpiness: state.grumpiness - by } + case types.increase: + return { grumpiness: state.grumpiness + by } + case types.decrease: + return { grumpiness: state.grumpiness - by } } } -const useStore = create(set => ({ +const useStore = create((set) => ({ grumpiness: 0, - dispatch: args => set(state => reducer(state, args)), + dispatch: (args) => set((state) => reducer(state, args)), })) -const dispatch = useStore(state => state.dispatch) +const dispatch = useStore((state) => state.dispatch) dispatch({ type: types.increase, by: 2 }) ``` @@ -348,15 +382,15 @@ const useStore = create(devtools(store)) const useStore = create(devtools(redux(reducer, initialState))) ``` -devtools takes the store function as its first argument, optionally you can name the store or configure [serialize](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#serialize) options with a second argument. - +devtools takes the store function as its first argument, optionally you can name the store or configure [serialize](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#serialize) options with a second argument. + Name store: `devtools(store, {name: "MyStore"})`, which will create a separate instance named "MyStore" in the devtools. Serialize options: `devtools(store, { serialize: { options: true } })`. - + #### Logging Actions -devtools will only log actions from each separated store unlike in a typical *combined reducers* redux store. See an approach to combining stores https://github.com/pmndrs/zustand/issues/163 +devtools will only log actions from each separated store unlike in a typical _combined reducers_ redux store. See an approach to combining stores https://github.com/pmndrs/zustand/issues/163 You can log a specific action type for each `set` function by passing a third parameter: @@ -366,12 +400,12 @@ const createBearSlice = (set, get) => ({ set( (prev) => ({ fishes: prev.fishes > 1 ? prev.fishes - 1 : 0 }), false, - "bear/eatFish" + 'bear/eatFish' ), }) ``` -If an action type is not provided, it is defaulted to "anonymous". You can customize this default value by providing an `anonymousActionType` parameter: +If an action type is not provided, it is defaulted to "anonymous". You can customize this default value by providing an `anonymousActionType` parameter: ```jsx devtools(..., { anonymousActionType: 'unknown', ... }) @@ -431,81 +465,82 @@ const Component = () => {
createContext usage in real components - ```jsx - import create from "zustand"; - import createContext from "zustand/context"; - - // Best practice: You can move the below createContext() and createStore to a separate file(store.js) and import the Provider, useStore here/wherever you need. - - const { Provider, useStore } = createContext(); +```jsx +import create from "zustand"; +import createContext from "zustand/context"; + +// Best practice: You can move the below createContext() and createStore to a separate file(store.js) and import the Provider, useStore here/wherever you need. + +const { Provider, useStore } = createContext(); + +const createStore = () => + create((set) => ({ + bears: 0, + increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), + removeAllBears: () => set({ bears: 0 }) + })); + +const Button = () => { + return ( + {/** store() - This will create a store for each time using the Button component instead of using one store for all components **/} + + + + ); +}; + +const ButtonChild = () => { + const state = useStore(); + return ( +
+ {state.bears} + +
+ ); +}; + +export default function App() { + return ( +
+
+ ); +} +``` - const createStore = () => - create((set) => ({ - bears: 0, - increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), - removeAllBears: () => set({ bears: 0 }) - })); - - const Button = () => { - return ( - {/** store() - This will create a store for each time using the Button component instead of using one store for all components **/} - - - - ); - }; - - const ButtonChild = () => { - const state = useStore(); - return ( -
- {state.bears} - -
- ); - }; - - export default function App() { - return ( -
-
- ); - } - ```
createContext usage with initialization from props - ```tsx - import create from "zustand"; - import createContext from "zustand/context"; - - const { Provider, useStore } = createContext(); - - export default function App({ initialBears }) { - return ( - - create((set) => ({ - bears: initialBears, - increase: () => set((state) => ({ bears: state.bears + 1 })), - })) - } - > -
## TypeScript Usage @@ -513,28 +548,31 @@ const Component = () => { Basic typescript usage doesn't require anything special except for writing `create()(...)` instead of `create(...)`... ```ts -import create from "zustand" -import { devtools, persist } from "zustand/middleware" +import create from 'zustand' +import { devtools, persist } from 'zustand/middleware' interface BearState { bears: number increase: (by: number) => void } -const useStore = create()(devtools(persist((set) => ({ - bears: 0, - increase: (by) => set((state) => ({ bears: state.bears + by })), -})))) +const useStore = create()( + devtools( + persist((set) => ({ + bears: 0, + increase: (by) => set((state) => ({ bears: state.bears + by })), + })) + ) +) ``` A more complete TypeScript guide is [here](https://github.com/pmndrs/zustand/blob/main/docs/typescript.md). ## Best practices - -* You may wonder how to organize your code for better maintenance: [Splitting the store into seperate slices](https://github.com/pmndrs/zustand/wiki/Splitting-the-store-into-separate-slices). - -* Recommended usage for this unopinionated library: [Flux inspired practice](https://github.com/pmndrs/zustand/wiki/Flux-inspired-practice). - + +- You may wonder how to organize your code for better maintenance: [Splitting the store into seperate slices](https://github.com/pmndrs/zustand/wiki/Splitting-the-store-into-separate-slices). +- Recommended usage for this unopinionated library: [Flux inspired practice](https://github.com/pmndrs/zustand/wiki/Flux-inspired-practice). +
Calling actions outside a React event handler in pre React 18 @@ -548,7 +586,7 @@ import { unstable_batchedUpdates } from 'react-dom' // or 'react-native' const useStore = create((set) => ({ fishes: 0, - increaseFishes: () => set((prev) => ({ fishes: prev.fishes + 1 })) + increaseFishes: () => set((prev) => ({ fishes: prev.fishes + 1 })), })) const nonReactCallback = () => { diff --git a/yarn.lock b/yarn.lock index ff83bc1c36..c5d47fd1c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1769,14 +1769,6 @@ agent-base@6: dependencies: debug "4" -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -1787,7 +1779,7 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: +ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -1799,11 +1791,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1823,11 +1810,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" - integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== - anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -1889,11 +1871,6 @@ array.prototype.flatmap@^1.2.5: es-abstract "^1.19.2" es-shim-unscopables "^1.0.0" -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2103,34 +2080,6 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - -cli-truncate@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" - integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== - dependencies: - slice-ansi "^5.0.0" - string-width "^5.0.0" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -2174,11 +2123,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.16: - version "2.0.16" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" - integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2191,11 +2135,6 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2386,11 +2325,6 @@ downlevel-dts@^0.10.0: shelljs "^0.8.3" typescript next -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - electron-to-chromium@^1.4.118: version "1.4.127" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.127.tgz#4ef19d5d920abe2676d938f4170729b44f7f423a" @@ -2406,11 +2340,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -2834,7 +2763,7 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -execa@^5.0.0, execa@^5.1.1: +execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -3153,11 +3082,6 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -husky@^7.0.4: - version "7.0.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" - integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== - iconv-lite@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -3196,11 +3120,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3277,11 +3196,6 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-fullwidth-code-point@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" - integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== - is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -3953,50 +3867,11 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lilconfig@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" - integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== - lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -lint-staged@^12.4.1: - version "12.4.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.4.1.tgz#63fa27bfc8a33515f6902f63f6670864f1fb233c" - integrity sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg== - dependencies: - cli-truncate "^3.1.0" - colorette "^2.0.16" - commander "^8.3.0" - debug "^4.3.3" - execa "^5.1.1" - lilconfig "2.0.4" - listr2 "^4.0.1" - micromatch "^4.0.4" - normalize-path "^3.0.0" - object-inspect "^1.12.0" - pidtree "^0.5.0" - string-argv "^0.3.1" - supports-color "^9.2.1" - yaml "^1.10.2" - -listr2@^4.0.1: - version "4.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" - integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== - dependencies: - cli-truncate "^2.1.0" - colorette "^2.0.16" - log-update "^4.0.0" - p-map "^4.0.0" - rfdc "^1.3.0" - rxjs "^7.5.5" - through "^2.3.8" - wrap-ansi "^7.0.0" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -4032,16 +3907,6 @@ lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== - dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" - loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -4243,7 +4108,7 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -4302,13 +4167,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -4381,11 +4239,6 @@ picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pidtree@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.5.0.tgz#ad5fbc1de78b8a5f99d6fbdd4f6e4eee21d1aca1" - integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== - pirates@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -4628,24 +4481,11 @@ resolve@^2.0.0-next.3: is-core-module "^2.2.0" path-parse "^1.0.6" -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfdc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -4695,13 +4535,6 @@ rxjs@^6.6.3: dependencies: tslib "^1.9.0" -rxjs@^7.5.5: - version "7.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" - integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== - dependencies: - tslib "^2.1.0" - safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -4793,7 +4626,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -4808,32 +4641,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" - integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== - dependencies: - ansi-styles "^6.0.0" - is-fullwidth-code-point "^4.0.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -4889,11 +4696,6 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -string-argv@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" - integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -4911,15 +4713,6 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - string.prototype.matchall@^4.0.6: version "4.0.7" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" @@ -4957,13 +4750,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" - integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== - dependencies: - ansi-regex "^6.0.1" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -5005,11 +4791,6 @@ supports-color@^8.0.0, supports-color@^8.1.0: dependencies: has-flag "^4.0.0" -supports-color@^9.2.1: - version "9.2.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.2.tgz#502acaf82f2b7ee78eb7c83dcac0f89694e5a7bb" - integrity sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA== - supports-hyperlinks@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -5065,11 +4846,6 @@ throat@^6.0.1: resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== -through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -5130,11 +4906,6 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -5336,15 +5107,6 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -5392,11 +5154,6 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"