diff --git a/.changeset/mobx-7-react-10.md b/.changeset/mobx-7-react-10.md new file mode 100644 index 0000000000..6894385033 --- /dev/null +++ b/.changeset/mobx-7-react-10.md @@ -0,0 +1,69 @@ +--- +"mobx": major +"mobx-react-lite": major +"mobx-react": major +--- + +Release MobX 7, mobx-react-lite 5, and mobx-react 10. + +Bundle sizes are down: ESM prod 17.02 KiB gzip -> 13.96 KiB gzip; a minimal tree-shaken example is 10.32 KiB gzip now. + +It removes long-deprecated compatibility paths and keeps the React bindings split between `mobx-react-lite` for function components and `mobx-react` for class-component support. + +## MobX 7 + +MobX 7 is a cleanup release focused on the modern runtime and decorator model. + +- MobX now always uses Proxy-backed observable objects and arrays. The ES5/non-proxy fallback has been removed. +- `configure({ useProxies: ... })` is no longer supported. +- `{ proxy: false }` options for `observable`, `observable.object`, and `observable.array` are no longer supported. +- Legacy decorators are no longer supported. +- Namespaced annotation and comparer properties now use named exports to reduce bundle size: + +| Removed API | Replacement | +| --------------------- | ------------------- | +| `observable.ref` | `observableRef` | +| `observable.shallow` | `observableShallow` | +| `observable.deep` | `observableDeep` | +| `observable.struct` | `observableStruct` | +| `computed.struct` | `computedStruct` | +| `action.bound` | `actionBound` | +| `flow.bound` | `flowBound` | +| `comparer.identity` | `compareIdentity` | +| `comparer.default` | `compareDefault` | +| `comparer.structural` | `compareStructural` | +| `comparer.shallow` | `compareShallow` | + +- The public `trace` API and its related runtime support have been removed. Use `toJS`, `getDependencyTree`, `getObserverTree`, `spy` or `mobx-log` package for debugging. + +## mobx-react-lite 5 and mobx-react 10 + +mobx-react-lite 5 and mobx-react 10 require MobX 7 and React 18 or later. + +`mobx-react-lite` remains the function-component package. `mobx-react` remains a thin wrapper around `mobx-react-lite` that adds class component and Stage 3 `@observer` class decorator support. + +- Keep function-component imports on `mobx-react-lite` if you do not need class component support. +- Use `mobx-react` when you need class components or `@observer` class decorators. +- `mobx-react-lite` supports function components and `forwardRef`; `mobx-react` delegates function components to `mobx-react-lite` and handles classes itself. +- Remove React batching imports, including the stale React Native batching deep import. React 18+ renderers handle batching. + +The recommended public React binding surface for both packages is: + +- `observer` +- `Observer` +- `useLocalObservable` +- `enableStaticRendering` +- `isUsingStaticRendering` + +The following APIs have been removed from the React binding packages: + +- `Provider`, `inject`, and `MobXProviderContext`; use `React.createContext` directly. +- `disposeOnUnmount`; dispose reactions in `componentWillUnmount` or return cleanup functions from `useEffect`. +- `PropTypes`; use TypeScript or the regular `prop-types` package. +- `useObserver`; wrap components with `observer` or use ``. +- `useLocalStore`; use `useLocalObservable`. +- `useAsObservableSource`; synchronize values from props into local observable state explicitly. +- `useStaticRendering`; use `enableStaticRendering`. +- `observerBatching`, `isObserverBatched`, `batchingForReactDom`, `batchingOptOut`, and `batchingForReactNative`; remove these imports because React 18+ renderers handle batching. +- Deprecated `observer(fn, { forwardRef: true })`; pass an already-created `React.forwardRef(...)` component to `observer` instead. +- Legacy function-component `contextTypes` handling. diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 38bd174f6a..cfb4c44cfa 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -35,8 +35,8 @@ jobs: - name: Test run: npm test -- -i - - name: Test size - run: npm run test:size + - name: Check size + run: npm run check-size - name: Test performance run: npm -w mobx run test:performance diff --git a/docs/analyzing-reactivity.md b/docs/analyzing-reactivity.md index 80ad31378a..fb230add38 100644 --- a/docs/analyzing-reactivity.md +++ b/docs/analyzing-reactivity.md @@ -8,58 +8,6 @@ hide_title: true # Analyzing reactivity {🚀} -# Using `trace` for debugging - -Trace is a small utility that helps you find out why your computed values, reactions or components are re-evaluating. - -It can be used by simply importing `import { trace } from "mobx"`, and then putting it inside a reaction or computed value. -It will print why it is re-evaluating the current derivation. - -Optionally it is possible to automatically enter the debugger by passing `true` as the last argument. -This way the exact mutation that causes the reaction to re-run will still be in stack, usually ~8 stack frames up. See the image below. - -In debugger mode, the debug information will also reveal the full derivation tree that is affecting the current computation / reaction. - -![trace](assets/trace-tips2.png) - -![trace](assets/trace.gif) - -## Live examples - -Simple [CodeSandbox `trace` example](https://codesandbox.io/s/trace-dnhbz?file=/src/index.js:309-338). - -[Here's a deployed example](https://csb-nr58ylyn4m-hontnuliaa.now.sh/) for exploring the stack. -Make sure to play with the chrome debugger's blackbox feature! - -## Usage examples - -There are different ways of calling `trace()`, some examples: - -```javascript -import { observer } from "mobx-react" -import { trace } from "mobx" - -const MyComponent = observer(() => { - trace(true) // Enter the debugger whenever an observable value causes this component to re-run. - return
{this.props.user.name} -}) -``` - -Enable trace by using the `reaction` argument of a reaction / autorun: - -```javascript -mobx.autorun("logger", reaction => { - reaction.trace() - console.log(user.fullname) -}) -``` - -Pass in the property name of a computed property: - -```javascript -trace(user, "fullname") -``` - # Introspection APIs The following APIs might come in handy if you want to inspect the internal state of MobX while debugging, or want to build cool tools on top of MobX. diff --git a/docs/api.md b/docs/api.md index 4db83b102a..9f3fc32460 100644 --- a/docs/api.md +++ b/docs/api.md @@ -25,7 +25,7 @@ _Making things observable._ ### `makeObservable` -Usage: `makeObservable(target, annotations?, options?)` +Usage: `makeObservable(target, annotations, options?)` ([further information](observable-state.md#makeobservable)) Properties, entire objects, arrays, Maps and Sets can all be made observable. @@ -89,7 +89,7 @@ If the values in the array should not be turned into observables automatically, Creates a new observable [ES6 Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) based on the provided `initialMap`. They are very useful if you don't want to react just to the change of a specific entry, but also to their addition and removal. -Creating observable Maps is the recommended approach for creating dynamically keyed collections if you don't have [enabled Proxies](configuration.md#proxy-support). +Creating observable Maps is the recommended approach for creating dynamically keyed collections. Besides all the language built-in Map functions, the following goodies are available on observable Maps as well: @@ -216,7 +216,7 @@ Creates an observable value that is derived from other observables, but won't be ## React integration -_From the `mobx-react` / `mobx-react-lite` packages._ +_From `mobx-react-lite` for function components, or `mobx-react` for class component support._ ### `observer` @@ -345,7 +345,7 @@ Use it to change how MobX behaves as a whole. ## Collection utilities {🚀} -_They enable manipulating observable arrays, objects and Maps with the same generic API. This can be useful in [environments without `Proxy` support](configuration.md#limitations-without-proxy-support), but is otherwise typically not needed._ +_They enable manipulating observable arrays, objects and Maps with the same generic API._ ### `values` @@ -462,13 +462,6 @@ Is this a boxed computed value, created using `computed(() => expr)`? Is this a computed property? -### `trace` - -{🚀} Usage: `trace()`, `trace(true)` _(enter debugger)_ or `trace(object, propertyName, enterDebugger?)` -([further information](analyzing-reactivity.md)) - -Should be used inside an observer, reaction or computed value. Logs when the value is invalidated, or sets the debugger breakpoint if called with _true_. - ### `spy` {🚀} Usage: `spy(eventListener)` diff --git a/docs/assets/getting-started-assets/script.js b/docs/assets/getting-started-assets/script.js index 1d51fd9ce7..a4ddfe6db4 100755 --- a/docs/assets/getting-started-assets/script.js +++ b/docs/assets/getting-started-assets/script.js @@ -25,9 +25,10 @@ function runCodeHelper(code) { window.autorun = mobx.autorun window.computed = mobx.computed window.action = mobx.action - window.observer = mobxReactLite.observer + window.observer = mobxReact.observer window.makeObservable = mobx.makeObservable window.makeAutoObservable = mobx.makeAutoObservable + window.renderReactApp = renderReactApp var globalEval = eval // global scope trick, See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval @@ -41,6 +42,15 @@ function runCodeHelper(code) { } } +function renderReactApp(element) { + var container = document.getElementById("reactjs-app") + if (!window.__cachedReactRoot) { + // Reuse one React 18 root across repeated "Run code" clicks. + window.__cachedReactRoot = ReactDOM.createRoot(container) + } + window.__cachedReactRoot.render(element) +} + function runCode(ids) { $(ids.join(",")).each(function(i, elem) { clearConsole() diff --git a/docs/assets/trace-tips2.png b/docs/assets/trace-tips2.png deleted file mode 100644 index 56f4c17d20..0000000000 Binary files a/docs/assets/trace-tips2.png and /dev/null differ diff --git a/docs/assets/trace.gif b/docs/assets/trace.gif deleted file mode 100644 index 35f171861c..0000000000 Binary files a/docs/assets/trace.gif and /dev/null differ diff --git a/docs/collection-utilities.md b/docs/collection-utilities.md index f97bf9ae9a..d4bf7894be 100644 --- a/docs/collection-utilities.md +++ b/docs/collection-utilities.md @@ -9,7 +9,7 @@ hide_title: true # Collection utilities {🚀} They enable manipulating observable arrays, objects and Maps with the same generic API. -These APIs are fully reactive, which means that even [without `Proxy` support](configuration.md#limitations-without-proxy-support) new property declarations can be detected by MobX if `set` is used to add them, and `values` or `keys` are used to iterate over them. +These APIs are fully reactive and can track keys, values and entries without depending on the concrete collection type. Another benefit of `values`, `keys` and `entries` is that they return arrays rather than iterators, which makes it possible to, for example, immediately call `.map(fn)` on the results. @@ -28,8 +28,6 @@ Mutation: - `has(collection, key)` returns _true_ if the collection has the specified _observable_ property. - `get(collection, key)` returns the child under the specified key. -If you use the access APIs in an environment without `Proxy` support, then also use the mutation APIs so they can detect the changes. - ```javascript import { autorun, get, set, observable, values } from "mobx" diff --git a/docs/computeds-with-args.md b/docs/computeds-with-args.md index c217b28cc1..e848f48fe1 100644 --- a/docs/computeds-with-args.md +++ b/docs/computeds-with-args.md @@ -16,13 +16,11 @@ and the application supports multi-selection. How can we implement a derivation like `store.isSelected(item.id)`? ```javascript -import * as React from 'react' -import { observer } from 'mobx-react-lite' +import * as React from "react" +import { observer } from "mobx-react-lite" const Item = observer(({ item, store }) => ( -
- {item.title} -
+
{item.title}
)) ``` @@ -46,17 +44,13 @@ This is a worst-case example. In general, it is completely fine to have unmarked This is a more efficient implementation compared to the original. ```javascript -import * as React from 'react' -import { computed } from 'mobx' -import { observer } from 'mobx-react-lite' +import * as React from "react" +import { computed } from "mobx" +import { observer } from "mobx-react-lite" const Item = observer(({ item, store }) => { const isSelected = computed(() => store.isSelected(item.id)).get() - return ( -
- {item.title} -
- ) + return
{item.title}
}) ``` diff --git a/docs/computeds.md b/docs/computeds.md index 9e4f78a937..d0f2eb00fb 100644 --- a/docs/computeds.md +++ b/docs/computeds.md @@ -214,22 +214,22 @@ This string is used as a debug name in the [Spy event listeners](analyzing-react ### `equals` -Set to `comparer.default` by default. It acts as a comparison function for comparing the previous value with the next value. If this function considers the values to be equal, then the observers will not be re-evaluated. +Set to `compareDefault` by default. It acts as a comparison function for comparing the previous value with the next value. If this function considers the values to be equal, then the observers will not be re-evaluated. -This is useful when working with structural data and types from other libraries. For example, a computed [moment](https://momentjs.com/) instance could use `(a, b) => a.isSame(b)`. `comparer.structural` and `comparer.shallow` come in handy if you want to use structural / shallow comparison to determine whether the new value is different from the previous value, and as a result notify its observers. +This is useful when working with structural data and types from other libraries. For example, a computed [moment](https://momentjs.com/) instance could use `(a, b) => a.isSame(b)`. `compareStructural` and `compareShallow` come in handy if you want to use structural / shallow comparison to determine whether the new value is different from the previous value, and as a result notify its observers. Check out the [`computed.struct`](#computed-struct) section above. #### Built-in comparers -MobX provides four built-in `comparer` methods which should cover most needs of the `equals` option of `computed`: +MobX provides four built-in comparison functions which should cover most needs of the `equals` option of `computed`: -- `comparer.identity` uses the identity (`===`) operator to determine if two values are the same. -- `comparer.default` is the same as `comparer.identity`, but also considers `NaN` to be equal to `NaN`. -- `comparer.structural` performs deep structural comparison to determine if two values are the same. -- `comparer.shallow` performs shallow structural comparison to determine if two values are the same. +- `compareIdentity` uses the identity (`===`) operator to determine if two values are the same. +- `compareDefault` uses `Object.is` to determine if two values are the same. +- `compareStructural` performs deep structural comparison to determine if two values are the same. +- `compareShallow` performs shallow structural comparison to determine if two values are the same. -You can import `comparer` from `mobx` to access these methods. They can be used for `reaction` as well. +You can import these functions from `mobx`. They can be used for `reaction` as well. ### `requiresReaction` diff --git a/docs/configuration.md b/docs/configuration.md index 0b3486357b..7a4171406a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -13,37 +13,11 @@ Most configuration options can be set by using the `configure` method. ## Proxy support -By default, MobX uses proxies to make arrays and plain objects observable. Proxies provide the best performance and most consistent behavior across environments. -However, if you are targeting an environment that doesn't support proxies, proxy support has to be disabled. -Most notably this is the case when targeting Internet Explorer or React Native without using the Hermes engine. +MobX requires [`Proxy` support](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to make arrays and plain objects observable. +Proxies provide the best performance and most consistent behavior across environments. -Proxy support can be disabled by using `configure`: - -```typescript -import { configure } from "mobx" - -configure({ - useProxies: "never" -}) -``` - -Accepted values for the `useProxies` configuration are: - -- `"always"` (**default**): MobX expects to run only in environments with [`Proxy` support](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) and it will error if such an environment is not available. -- `"never"`: Proxies are not used and MobX falls back on non-proxy alternatives. This is compatible with all ES5 environments, but causes various [limitations](#limitations-without-proxy-support). -- `"ifavailable"` (experimental): Proxies are used if they are available, and otherwise MobX falls back to non-proxy alternatives. The benefit of this mode is that MobX will try to warn if APIs or language features that wouldn't work in ES5 environments are used, triggering errors when hitting an ES5 limitation running on a modern environment. - -**Note:** before MobX 6, one had to pick either MobX 4 for older engines, or MobX 5 for new engines. However, MobX 6 supports both, although polyfills for certain APIs like Map will be required when targetting older JavaScript engines. -Proxies cannot be polyfilled. Even though polyfills do exist, they don't support the full spec and are unsuitable for MobX. Don't use them. - -### Limitations without Proxy support - -1. Observable arrays are not real arrays, so they won't pass the `Array.isArray()` check. The practical consequence is that you often need to `.slice()` the array first (to get a shallow copy of the real array) before passing it to third party libraries. For example, concatenating observable arrays doesn't work as expected, so `.slice()` them first. -2. Adding or deleting properties of existing observable plain objects after creation is not automatically picked up. If you intend to use objects as index based lookup maps, in other words, as dynamic collections of things, use observable Maps instead. - -It is possible to dynamically add properties to objects, and detect their additions, even when Proxies aren't enabled. -This can be achieved by using the [Collection utilities {🚀}](collection-utilities.md). Make sure that (new) properties are set using the `set` utility, and that the objects are iterated using one of the `values` / `keys` or `entries` utilities, rather than the built-in JavaScript mechanisms. -But, since this is really easy to forget, we instead recommend using observable Maps if possible. +MobX 7 does not include the older ES5 fallback implementation and no longer supports `configure({ useProxies })`. +Use MobX 6 if you need to support environments without Proxy support, such as Internet Explorer or older React Native versions. ## Decorator support diff --git a/docs/enabling-decorators.md b/docs/enabling-decorators.md index 29850a8523..98f3696d4e 100644 --- a/docs/enabling-decorators.md +++ b/docs/enabling-decorators.md @@ -90,47 +90,8 @@ class TodoList { Notice the usage of the new `accessor` keyword when using `@observable`. It is part of the 2022.3 spec and is required if you want to use modern decorators. -
Using legacy decorators - -We do not recommend codebases to use TypeScript / Babel legacy decorators since they well never become an official part of the language, but you can still use them. It does require a specific setup for transpilation: - -MobX before version 6 encouraged the use of legacy decorators and mark things as `observable`, `computed` and `action`. -While MobX 6 recommends against using these decorators (and instead use either modern decorators or [`makeObservable` / `makeAutoObservable`](observable-state.md)), it is in the current major version still possible. -Support for legacy decorators will be removed in MobX 7. - -```javascript -import { makeObservable, observable, computed, action } from "mobx" - -class Todo { - id = Math.random() - @observable title = "" - @observable finished = false - - constructor() { - makeObservable(this) - } - - @action - toggle() { - this.finished = !this.finished - } -} - -class TodoList { - @observable todos = [] - - @computed - get unfinishedTodoCount() { - return this.todos.filter(todo => !todo.finished).length - } - - constructor() { - makeObservable(this) - } -} -``` - -
+If you still need legacy TypeScript decorators, use MobX 6. +MobX 7 requires migrating to modern decorators.
Migrating from legacy decorators @@ -146,15 +107,13 @@ Please note that adding `accessor` to a class property will change it into `get`
Decorator changes / gotchas -MobX' 2022.3 Decorators are very similar to the MobX 5 decorators, so usage is mostly the same, but there are some gotchas: +MobX' 2022.3 decorators have some gotchas: - `@observable accessor` decorators are _not_ enumerable. `accessor`s do not have a direct equivalent in the past - they're a new concept in the language. We've chosen to make them non-enumerable, non-own properties in order to better follow the spirit of the ES language and what `accessor` means. The main cases for enumerability seem to have been around serialization and rest destructuring. - Regarding serialization, implicitly serializing all properties probably isn't ideal in an OOP-world anyway, so this doesn't seem like a substantial issue (consider implementing `toJSON` or using `serializr` as possible alternatives) - Addressing rest-destructuring, such is an anti-pattern in MobX - doing so would (likely unwantedly) touch all observables and make the observer overly-reactive). -- `@action some_field = () => {}` was and is valid usage. However, inheritance is different between legacy decorators and modern decorators. - - In legacy decorators, if superclass has a field decorated by `@action`, and subclass tries to override the same field, it will throw a `TypeError: Cannot redefine property`. - - In modern decorators, if superclass has a field decorated by `@action`, and subclass tries to override the same field, it's allowed to override the field. However, the field on subclass is not an action unless it's also decorated with `@action` in subclass declaration. +- `@action some_field = () => {}` is valid usage. If a superclass has a field decorated by `@action`, a subclass can override the field. However, the field on the subclass is not an action unless it is also decorated with `@action` in the subclass declaration.
diff --git a/docs/installation.md b/docs/installation.md index b6bae03c20..e25769bc5d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -8,18 +8,21 @@ hide_title: true # Installation -MobX works in any ES5 environment, which includes browsers and NodeJS. +MobX works in browsers and Node.js environments that provide native `Proxy` support. There are three types of React bindings: -- [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite). Utilities to manually apply observation -- [mobx-react-observer](https://github.com/christianalfoni/mobx-react-observer). Babel/swc plugin to automatically apply observation to components -- [mobx-react](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react). Support for class components -Append the appropriate bindings for your use case to the _Yarn_ or _NPM_ command below: +- [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite). Utilities to manually apply observation +- [mobx-react-observer](https://github.com/christianalfoni/mobx-react-observer). Babel/swc plugin to automatically apply observation to components +- [mobx-react](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react). Support for class components -**Yarn:** `yarn add mobx` +Append the appropriate bindings for your use case to one of the commands below: -**NPM:** `npm install --save mobx` +**npm:** `npm install mobx` + +**pnpm:** `pnpm add mobx` + +**yarn:** `yarn add mobx` **CDN:** https://cdnjs.com/libraries/mobx / https://unpkg.com/mobx/dist/mobx.umd.production.min.js @@ -28,9 +31,7 @@ Append the appropriate bindings for your use case to the _Yarn_ or _NPM_ command ## MobX and Decorators Based on your preference, MobX can be used with or without decorators. -Both the legacy implementation and the standardised TC-39 version of decorators are currently supported. See [enabling-decorators](enabling-decorators.md) for more details on how to enable them. -Legacy decorator support will be removed in MobX 7, in favor of the standard. ## Use spec compliant transpilation for class properties @@ -38,6 +39,7 @@ When using MobX with TypeScript or Babel, and you plan to use classes; make sure - **TypeScript**: Set the compiler option `"useDefineForClassFields": true`. - **Babel**: Make sure to use at least version 7.12, with the following configuration: + ```json { // Babel < 7.13.0 @@ -50,25 +52,14 @@ When using MobX with TypeScript or Babel, and you plan to use classes; make sure } } ``` -For verification insert this piece of code at the beginning of your sources (eg. `index.js`) -```javascript -if (!new class { x }().hasOwnProperty('x')) throw new Error('Transpiler is not configured correctly'); -``` -## MobX on older JavaScript environments - -By default, MobX uses proxies for optimal performance and compatibility. However, on older JavaScript engines `Proxy` is not available (check out [Proxy support](https://compat-table.github.io/compat-table/es6/#test-Proxy)). Examples of such are Internet Explorer (before Edge), Node.js < 6, iOS < 10, Android before RN 0.59. - -In such cases, MobX can fallback to an ES5 compatible implementation which works almost identically, although there are a few [limitations without Proxy support](configuration.md#limitations-without-proxy-support). You will have to explicitly enable the fallback implementation by configuring [`useProxies`](configuration.md#proxy-support): +For verification insert this piece of code at the beginning of your sources (eg. `index.js`) + ```javascript -import { configure } from "mobx" - -configure({ useProxies: "never" }) // Or "ifavailable". +if (!new (class { x })().hasOwnProperty("x")) throw new Error("Transpiler is not configured correctly") ``` -This option will be removed in MobX 7. - ## MobX on other frameworks / platforms - [MobX.dart](https://mobx.netlify.app/): MobX for Flutter / Dart diff --git a/docs/migrating-from-6-to-7.md b/docs/migrating-from-6-to-7.md new file mode 100644 index 0000000000..02921f6c71 --- /dev/null +++ b/docs/migrating-from-6-to-7.md @@ -0,0 +1,295 @@ +--- +title: Migrating to MobX 7 +sidebar_label: Migrating to MobX 7 {🚀} +hide_title: true +--- + + + +# Migrating to MobX 7 {🚀} + +MobX 7 is mostly a cleanup release. Most applications that already use MobX 6 idiomatically can upgrade with minimal changes. + +## Updating React bindings + +MobX 7 keeps the React bindings split: + +- `mobx-react-lite` supports function components and `forwardRef`. +- `mobx-react` is a thin wrapper around `mobx-react-lite` that also supports class components and the `@observer` class decorator. + +`mobx-react-lite` and `mobx-react` require React 18 or later. + +The public React binding surface has been reduced to the APIs that are still recommended: + +- `observer` +- `Observer` +- `useLocalObservable` +- `enableStaticRendering` +- `isUsingStaticRendering` + +The following APIs have been removed: + +| Removed API | Replacement | +| ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `disposeOnUnmount` | Dispose reactions in `componentWillUnmount`, or return a cleanup function from `useEffect`. | +| `PropTypes` | Use TypeScript or the regular `prop-types` package. | +| `useLocalStore` | Use `useLocalObservable`. | +| `useAsObservableSource` | Store the values you need locally and synchronize them from props with `useEffect`. | +| `useObserver` | Wrap the component in `observer`, or use the `` component. | +| `useStaticRendering` | Use `enableStaticRendering`. | +| `observerBatching`, `isObserverBatched`, `batchingForReactDom`, `batchingOptOut`, `batchingForReactNative` | Remove these imports. React 18+ renderers handle batching automatically, and the React Native side-effect import is no longer needed. | +| `Provider`, `inject`, `MobXProviderContext` from `mobx-react` | Use `React.createContext` directly. | + +## Migrating legacy decorators + +MobX 7 supports Stage 3 decorators only. + +To keep decorators, switch the class to Stage 3 decorator syntax: remove `makeObservable(this)` from decorated classes, drop its import when unused, and add `accessor` to observable fields. + +```diff +-import { makeObservable, observable, computed, action } from "mobx" ++import { observable, computed, action } from "mobx" + +class Todo { +- @observable title = "" ++ @observable accessor title = "" +- @observable finished = false ++ @observable accessor finished = false +- +- constructor() { +- makeObservable(this) +- } + + @computed + get label() { + return `${this.finished ? "[DONE]" : "[OPEN]"} ${this.title}` + } + + @action + toggle() { + this.finished = !this.finished + } +} +``` + +Switch your compiler to modern decorators: + +- For TypeScript, use TypeScript 5 or later and disable or remove the `experimentalDecorators` flag. +- For Babel, use `@babel/plugin-proposal-decorators` with the current Stage 3 configuration. See [Enabling decorators {🚀}](enabling-decorators.md) for the exact compiler setup. + +If you don't want to keep decorators, remove them and pass an explicit annotation map to `makeObservable`: + +```javascript +import { makeObservable, observable, computed, action } from "mobx" + +class Todo { + title = "" + finished = false + + constructor() { + makeObservable(this, { + title: observable, + finished: observable, + label: computed, + toggle: action + }) + } + + get label() { + return `${this.finished ? "[DONE]" : "[OPEN]"} ${this.title}` + } + + toggle() { + this.finished = !this.finished + } +} +``` + +## Replacing namespaced APIs + +Namespaced annotation and comparer properties have been replaced by named exports for better tree-shaking: + +| Removed API | Replacement | +| --------------------- | ------------------- | +| `observable.ref` | `observableRef` | +| `observable.shallow` | `observableShallow` | +| `observable.deep` | `observableDeep` | +| `observable.struct` | `observableStruct` | +| `computed.struct` | `computedStruct` | +| `action.bound` | `actionBound` | +| `flow.bound` | `flowBound` | +| `comparer.identity` | `compareIdentity` | +| `comparer.default` | `compareDefault` | +| `comparer.structural` | `compareStructural` | +| `comparer.shallow` | `compareShallow` | + +Annotation map: + +```diff +-import { action, comparer, computed, flow, makeObservable, observable } from "mobx" ++import { actionBound, compareStructural, computed, computedStruct, flowBound, makeObservable, observableRef } from "mobx" + + makeObservable(this, { +- value: observable.ref, ++ value: observableRef, +- total: computed.struct, ++ total: computedStruct, +- rounded: computed({ equals: comparer.structural }), ++ rounded: computed({ equals: compareStructural }), +- save: action.bound, ++ save: actionBound, +- load: flow.bound ++ load: flowBound + }) +``` + +Decorator: + +```diff +-import { action, comparer, computed, flow, observable } from "mobx" ++import { actionBound, compareStructural, computed, computedStruct, flowBound, observableRef } from "mobx" + + class Store { +- @observable.ref accessor value = null ++ @observableRef accessor value = null + +- @computed.struct ++ @computedStruct + get total() { + return { value: this.value } + } + +- @computed({ equals: comparer.structural }) ++ @computed({ equals: compareStructural }) + get rounded() { + return { value: Math.round(this.value) } + } + +- @action.bound ++ @actionBound + save() {} + +- @flow.bound ++ @flowBound + *load() {} + } +``` + +Old structural boolean options should use `equals` explicitly: + +```diff +-computed(() => value, { compareStructural: true }) ++computed(() => value, { equals: compareStructural }) + +-reaction(() => value, effect, { compareStructural: true }) ++reaction(() => value, effect, { equals: compareStructural }) +``` + +## Proxy support is required + +MobX 7 requires native Proxy support and no longer includes the ES5 fallback implementation. + +Remove `useProxies` from `configure` calls: + +```diff + import { configure } from "mobx" + + configure({ + enforceActions: "observed", +- useProxies: "ifavailable" + }) +``` + +Also remove `{ proxy: false }` from `observable`, `observable.object` and `observable.array` options: + +```diff +-const todos = observable.object({}, {}, { proxy: false }) ++const todos = observable.object({}) +``` + +## Removed `trace` + +The `trace` API has been removed. For debugging reactivity, use [`getDependencyTree`](api.md#getdependencytree), [`getObserverTree`](api.md#getobservertree), [`spy`](analyzing-reactivity.md#spy), the MobX developer tools, or packages such as `mobx-log`. + +```javascript +import { autorun, getDependencyTree } from "mobx" + +const disposer = autorun(() => { + console.log(message.title) +}) + +console.log(getDependencyTree(disposer)) +``` + +## Replacing `Provider` and `inject` + +`Provider` and `inject` were removed. Use React context directly. Keep the context value stable and mutate the observable store instead of replacing the provider value. + +Before: + +```javascript +import { Provider, inject, observer } from "mobx-react" + +// prettier-ignore +const UserName = inject("userStore")( + observer(({ userStore }) => {userStore.name}) +) + +const App = ({ userStore }) => ( + + + +) +``` + +After, using a function component: + +```javascript +import React, { createContext, useContext } from "react" +import { observer } from "mobx-react" + +const RootStoreContext = createContext(null) + +export const RootStoreProvider = ({ rootStore, children }) => ( + {children} +) + +export const useRootStore = () => { + const store = useContext(RootStoreContext) + if (!store) { + throw new Error("RootStoreProvider is missing") + } + return store +} + +const UserName = observer(() => { + const { userStore } = useRootStore() + return {userStore.name} +}) + +const App = () => ( + + + +) +``` + +After, using a class component: + +```javascript +import React from "react" +import { observer } from "mobx-react" + +const RootStoreContext = React.createContext(null) + +class UserName extends React.Component { + static contextType = RootStoreContext + + render() { + const { userStore } = this.context + return {userStore.name} + } +} + +const ObservedUserName = observer(UserName) +``` diff --git a/docs/observable-state.md b/docs/observable-state.md index c7311ad984..b2d01c99a0 100644 --- a/docs/observable-state.md +++ b/docs/observable-state.md @@ -20,7 +20,7 @@ The most important annotations are: Usage: -- `makeObservable(target, annotations?, options?)` +- `makeObservable(target, annotations, options?)` This function can be used to make _existing_ object properties observable. Any JavaScript object (including class instances) can be passed into `target`. Typically `makeObservable` is used in the constructor of a class, and its first argument is `this`. @@ -72,7 +72,7 @@ class Doubler { When using modern decorators, there is no need to call `makeObservable`, below is what a decorator based class looks like. -Note that the `@observable` annotation should always be used in combination with the `accessor` keyword. +Note that the `@observable` decorator should always be used in combination with the `accessor` keyword. ```javascript import { observable, computed, action, flow } from "mobx" @@ -147,39 +147,6 @@ tags.push("prio: for fun") In contrast to the first example with `makeObservable`, `observable` supports adding (and removing) _fields_ to an object. This makes `observable` great for collections like dynamically keyed objects, arrays, Maps and Sets. - - -To use legacy decorators, `makeObservable(this)` should be called in the constructor to make sure decorators work. - -```javascript -import { observable, computed, action, flow } from "mobx" - -class Doubler { - @observable value - - constructor(value) { - makeObservable(this) - this.value = value - } - - @computed - get double() { - return this.value * 2 - } - - @action - increment() { - this.value++ - } - - @flow - *fetch() { - const response = yield fetch("/api/value") - this.value = response.json() - } -} -``` - ## `makeAutoObservable` @@ -216,7 +183,7 @@ The `source` object will be cloned and all members will be made observable, simi Likewise, an `overrides` map can be provided to specify the annotations of specific members. Check out the above code block for an example. -The object returned by `observable` will be a Proxy, which means that properties that are added later to the object will be picked up and made observable as well (except when [proxy usage](configuration.md#proxy-support) is disabled). +The object returned by `observable` will be a Proxy, which means that properties that are added later to the object will be picked up and made observable as well. The `observable` method can also be called with collections types like [arrays](api.md#observablearray), [Maps](api.md#observablemap) and [Sets](api.md#observableset). Those will be cloned as well and converted into their observable counterparts. @@ -274,36 +241,35 @@ Making class members observable is considered the responsibility of the class co
-
{🚀} **Tip:** observable (proxied) versus makeObservable (unproxied) +
{🚀} **Tip:** observable clones versus makeObservable in-place updates The primary difference between `make(Auto)Observable` and `observable` is that the first one modifies the object you are passing in as first argument, while `observable` creates a _clone_ that is made observable. -The second difference is that `observable` creates a [`Proxy`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) object, to be able to trap future property additions in case you use the object as a dynamic lookup map. -If the object you want to make observable has a regular structure where all members are known up-front, we recommend to use `makeObservable` as non proxied objects are a little faster, and they are easier to inspect in the debugger and `console.log`. +`observable` creates a [`Proxy`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) object, to be able to trap future property additions in case you use the object as a dynamic lookup map. +If the object you want to make observable has a regular structure where all members are known up-front, `makeObservable` is often the clearer API because it keeps the original object identity. Because of that, `make(Auto)Observable` is the recommended API to use in factory functions. -Note that it is possible to pass `{ proxy: false }` as an option to `observable` to get a non proxied clone.
## Available annotations -| Annotation | Description | -| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Annotation | Description | +| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `observable`
`observable.deep` | Defines a trackable field that stores state. If possible, any value assigned to `observable` is automatically converted to (deep) `observable`, [`autoAction`](#autoAction) or `flow` based on its type. Only `plain object`, `array`, `Map`, `Set`, `function`, `generator function` are convertible. Class instances and others are untouched. | -| `observable.ref` | Like `observable`, but only reassignments will be tracked. The assigned values are completely ignored and will NOT be automatically converted to `observable`/[`autoAction`](#autoAction)/`flow`. For example, use this if you intend to store immutable data in an observable field. | -| `observable.shallow` | Like `observable.ref` but for collections. Any collection assigned will be made observable, but the contents of the collection itself won't become observable. | -| `observable.struct` | Like `observable`, except that any assigned value that is structurally equal to the current value will be ignored. | -| `action` | Mark a method as an action that will modify the state. Check out [actions](actions.md) for more details. Non-writable. | -| `action.bound` | Like action, but will also bind the action to the instance so that `this` will always be set. Non-writable. | -| `computed` | Can be used on a [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) to declare it as a derived value that can be cached. Check out [computeds](computeds.md) for more details. | -| `computed.struct` | Like `computed`, except that if after recomputing the result is structurally equal to the previous result, no observers will be notified. | -| `true` | Infer the best annotation. Check out [makeAutoObservable](#makeautoobservable) for more details. | -| `false` | Explicitly do not annotate this property. | -| `flow` | Creates a `flow` to manage asynchronous processes. Check out [flow](actions.md#using-flow-instead-of-async--await-) for more details. Note that the inferred return type in TypeScript might be off. Non-writable. | -| `flow.bound` | Like flow, but will also bind the flow to the instance so that `this` will always be set. Non-writable. | -| `override` | [Applicable to inherited `action`, `flow`, `computed`, `action.bound` overridden by subclass](subclassing.md). | -| `autoAction` | Should not be used explicitly, but is used under the hood by `makeAutoObservable` to mark methods that can act as action or derivation, based on their calling context. It will be determined at runtime if the function is a derivation or action. | +| `observable.ref` | Like `observable`, but only reassignments will be tracked. The assigned values are completely ignored and will NOT be automatically converted to `observable`/[`autoAction`](#autoAction)/`flow`. For example, use this if you intend to store immutable data in an observable field. | +| `observable.shallow` | Like `observable.ref` but for collections. Any collection assigned will be made observable, but the contents of the collection itself won't become observable. | +| `observable.struct` | Like `observable`, except that any assigned value that is structurally equal to the current value will be ignored. | +| `action` | Mark a method as an action that will modify the state. Check out [actions](actions.md) for more details. Non-writable. | +| `action.bound` | Like action, but will also bind the action to the instance so that `this` will always be set. Non-writable. | +| `computed` | Can be used on a [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) to declare it as a derived value that can be cached. Check out [computeds](computeds.md) for more details. | +| `computed.struct` | Like `computed`, except that if after recomputing the result is structurally equal to the previous result, no observers will be notified. | +| `true` | Infer the best annotation. Check out [makeAutoObservable](#makeautoobservable) for more details. | +| `false` | Explicitly do not annotate this property. | +| `flow` | Creates a `flow` to manage asynchronous processes. Check out [flow](actions.md#using-flow-instead-of-async--await-) for more details. Note that the inferred return type in TypeScript might be off. Non-writable. | +| `flow.bound` | Like flow, but will also bind the flow to the instance so that `this` will always be set. Non-writable. | +| `override` | [Applicable to inherited `action`, `flow`, `computed`, `action.bound` overridden by subclass](subclassing.md). | +| `autoAction` | Should not be used explicitly, but is used under the hood by `makeAutoObservable` to mark methods that can act as action or derivation, based on their calling context. It will be determined at runtime if the function is a derivation or action. | ## Limitations @@ -331,7 +297,6 @@ The above APIs take an optional `options` argument which is an object that suppo - **`autoBind: true`** uses `action.bound`/`flow.bound` by default, rather than `action`/`flow`. Does not affect explicitly annotated members. - **`deep: false`** uses `observable.ref` by default, rather than `observable`. Does not affect explicitly annotated members. - **`name: `** gives the object a debug name that is printed in error messages and reflection APIs. -- **`proxy: false`** forces `observable(thing)` to use non-[**proxy**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) implementation. This is a good option if the shape of the object will not change over time, as non-proxied objects are easier to debug and faster. This option is **not** available for `make(Auto)Observable`, see [avoiding proxies](#avoid-proxies).
**Note:** options are *sticky* and can be provided only once `options` argument can be provided only for `target` that is NOT observable yet.
diff --git a/docs/react-integration.md b/docs/react-integration.md index b86c41a085..8ab2197a0d 100644 --- a/docs/react-integration.md +++ b/docs/react-integration.md @@ -18,7 +18,7 @@ const MyComponent = observer(props => ReactElement) While MobX works independently from React, they are most commonly used together. In [The gist of MobX](the-gist-of-mobx.md) you have already seen the most important part of this integration: the `observer` [HoC](https://reactjs.org/docs/higher-order-components.html) that you can wrap around a React component. -`observer` is provided by a separate React bindings package you choose [during installation](installation.md#installation). In this example, we're going to use the more lightweight [`mobx-react-lite` package](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite). +`observer` is provided by the [`mobx-react-lite` package](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite) for function components. Use [`mobx-react`](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react) if you also need class component support. ```javascript import React from "react" @@ -198,7 +198,7 @@ ReactDOM.render(, document.body) The combination `const [store] = useState(() => observable({ /* something */}))` is -quite common. To make this pattern simpler the [`useLocalObservable`](https://github.com/mobxjs/mobx-react#uselocalobservable-hook) hook is exposed from `mobx-react-lite` package, making it possible to simplify the earlier example to: +quite common. To make this pattern simpler the [`useLocalObservable`](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite#uselocalobservabletinitializer--t-annotations-annotationsmapt-t) hook is exposed from the `mobx-react-lite` package, making it possible to simplify the earlier example to: ```javascript import { observer, useLocalObservable } from "mobx-react-lite" @@ -291,7 +291,7 @@ const TodoView = observer(({ todo }: { todo: Todo }) => Imagine the same example, where `GridRow` takes an `onRender` callback instead. Since `onRender` is part of the rendering cycle of `GridRow`, rather than `TodoView`'s render (even though that is where it syntactically appears), we have to make sure that the callback component uses an `observer` component. -Or, we can create an in-line anonymous observer using [``](https://github.com/mobxjs/mobx-react#observer): +Or, we can create an in-line anonymous observer using [``](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite#observerrenderfn): ```javascript const TodoView = observer(({ todo }: { todo: Todo }) => { @@ -310,20 +310,6 @@ const TodoView = observer(({ todo }: { todo: Todo }) => { If `observer` is used in server side rendering context; make sure to call `enableStaticRendering(true)`, so that `observer` won't subscribe to any observables used, and no GC problems are introduced.
-
**Note:** mobx-react vs. mobx-react-lite -In this documentation we used `mobx-react-lite` as default. -[mobx-react](https://github.com/mobxjs/mobx-react/) is its big brother, which uses `mobx-react-lite` under the hood. -It offers a few more features which are typically not needed anymore in greenfield projects. The additional things offered by mobx-react: - -1. Support for React class components. -1. `Provider` and `inject`. MobX's own React.createContext predecessor which is not needed anymore. -1. Observable specific `propTypes`. - -Note that `mobx-react` fully repackages and re-exports `mobx-react-lite`, including functional component support. -If you use `mobx-react`, there is no need to add `mobx-react-lite` as a dependency or import from it anywhere. - -
-
**Note:** `observer` or `React.memo`? `observer` automatically applies `memo`, so `observer` components never need to be wrapped in `memo`. `memo` can be applied safely to observer components because mutations (deeply) inside the props will be picked up by `observer` anyway if relevant. @@ -331,12 +317,12 @@ If you use `mobx-react`, there is no need to add `mobx-react-lite` as a dependen
**Tip:** `observer` for class based React components -As stated above, class based components are only supported through `mobx-react`, and not `mobx-react-lite`. Briefly, you can wrap class-based components in `observer` just like you can wrap function components: ```javascript import React from "react" +import { observer } from "mobx-react" const TimerView = observer( class TimerView extends React.Component { @@ -368,7 +354,7 @@ then no display name will be visible in the DevTools. The following approaches can be used to fix this: -- use `function` with a name instead of an arrow function. `mobx-react` infers component name from the function name: +- use `function` with a name instead of an arrow function. `observer` infers component name from the function name: ```javascript export const MyComponent = observer(function MyComponent(props) { @@ -390,15 +376,13 @@ The following approaches can be used to fix this: export default observer(MyComponent) ``` -- [**Broken**] Set `displayName` explicitly: +- Set `displayName` explicitly: ```javascript export const MyComponent = observer(props =>
hi
) MyComponent.displayName = "MyComponent" ``` - This is broken in React 16 at the time of writing; mobx-react `observer` uses a React.memo and runs into this bug: https://github.com/facebook/react/issues/18026, but it will be fixed in React 17. - Now you can see component names: ![devtools-withname](assets/devtools-withDisplayName.png) @@ -494,7 +478,7 @@ With MobX that isn't really needed, since MobX has already a way to automaticall Combining `autorun` and coupling it to the life-cycle of the component using `useEffect` is luckily straightforward: ```javascript -import { observer, useLocalObservable, useAsObservableSource } from "mobx-react-lite" +import { observer, useLocalObservable } from "mobx-react-lite" import { useState } from "react" const TimerView = observer(() => { @@ -553,4 +537,4 @@ Help! My component isn't re-rendering... 1. Make sure you grok how tracking works in general. Check out the [Understanding reactivity](understanding-reactivity.md) section. 1. Read the common pitfalls as described above. 1. [Configure](configuration.md#linting-options) MobX to warn you of unsound usage of mechanisms and check the console logs. -1. Use [trace](analyzing-reactivity.md) to verify that you are subscribing to the right things or check what MobX is doing in general using [spy](analyzing-reactivity.md#spy) / the [mobx-log](https://github.com/kubk/mobx-log) package. +1. Use [spy](analyzing-reactivity.md#spy), [`getDependencyTree`](api.md#getdependencytree), or the [mobx-log](https://github.com/kubk/mobx-log) package to inspect what MobX is doing. diff --git a/docs/reactions.md b/docs/reactions.md index 994ec57c5c..c5e2f53a8f 100644 --- a/docs/reactions.md +++ b/docs/reactions.md @@ -358,19 +358,23 @@ class OrderLine { constructor() { makeAutoObservable(this) - this.disposableStack.use(autorun(() => { - doSomethingWith(this.price * this.amount) - })) + this.disposableStack.use( + autorun(() => { + doSomethingWith(this.price * this.amount) + }) + ) - this.disposableStack.use(autorun(() => { - doSomethingWith(this.price * this.amount * vat.value) - })) + this.disposableStack.use( + autorun(() => { + doSomethingWith(this.price * this.amount * vat.value) + }) + ) this.someDisposableResource = this.disposableStack.use(createSomeDisposableResource()) } [Symbol.dispose]() { - this.disposableStack[Symbol.dispose](); + this.disposableStack[Symbol.dispose]() } } ``` @@ -427,6 +431,6 @@ Set a custom scheduler to determine how re-running the autorun function should b ### `equals`: (reaction) -Set to `comparer.default` by default. If specified, this comparer function is used to compare the previous and next values produced by the _data_ function. The _effect_ function is only invoked if this function returns false. +Set to `compareDefault` by default. If specified, this comparison function is used to compare the previous and next values produced by the _data_ function. The _effect_ function is only invoked if this function returns false. Check out the [Built-in comparers](computeds.md#built-in-comparers) section. diff --git a/docs/the-gist-of-mobx.md b/docs/the-gist-of-mobx.md index aba00ddc0c..ba908c93c4 100644 --- a/docs/the-gist-of-mobx.md +++ b/docs/the-gist-of-mobx.md @@ -136,7 +136,7 @@ as making a network request when submitting a form, should be triggered explicit #### 3.3. Reactive React components -If you are using React, you can make your components reactive by wrapping them with the [`observer`](react-integration.md) function from the bindings package you've [chosen during installation](installation.md#installation). In this example, we're going to use the more lightweight `mobx-react-lite` package. +If you are using React, you can make your function components reactive by wrapping them with the [`observer`](react-integration.md) function from the `mobx-react-lite` package. ```javascript import * as React from "react" @@ -197,7 +197,6 @@ To learn more about how MobX determines which observables need to be reacted to, MobX uses a uni-directional data flow where _actions_ change the _state_, which in turn updates all affected _views_. - ![Action, State, View](assets/action-state-view.png) 1. All _derivations_ are updated **automatically** and **atomically** when the _state_ changes. As a result, it is never possible to observe intermediate values. diff --git a/docs/understanding-reactivity.md b/docs/understanding-reactivity.md index 4bdb0ed036..2a2d3b5dc9 100644 --- a/docs/understanding-reactivity.md +++ b/docs/understanding-reactivity.md @@ -12,7 +12,7 @@ MobX usually reacts to exactly the things you expect it to, which means that in However, at some point you will encounter a case where it does not do what you expected. At that point it is invaluable to understand how MobX determines what to react to. -> MobX reacts to any _existing_ **observable** _property_ that is read during the execution of a tracked function. +> MobX reacts to any **observable** property that is read during the execution of a tracked function. - _"reading"_ is dereferencing an object's property, which can be done through "dotting into" it (eg. `user.name`) or using the bracket notation (eg. `user['name']`, `todos[3]`) or destructuring (eg. `const {name} = user`). - _"tracked functions"_ are the expression of `computed`, the _rendering_ of an `observer` React function component, the `render()` method of an `observer` based React class component, and the functions that are passed as the first param to `autorun`, `reaction` and `when`. @@ -68,32 +68,17 @@ message.updateTitle("Bar") This will react as expected. The `.title` property was dereferenced by the autorun, and changed afterwards, so this change is detected. -You can verify what MobX will track by calling [`trace()`](analyzing-reactivity.md) inside the tracked function. In the case of the above function it outputs the following: +You can inspect what MobX tracks by calling [`getDependencyTree`](api.md#getdependencytree) with the disposer returned by `autorun`: ```javascript -import { trace } from "mobx" +import { getDependencyTree } from "mobx" const disposer = autorun(() => { console.log(message.title) - trace() }) -// Outputs: -// [mobx.trace] 'Autorun@2' tracing enabled -message.updateTitle("Hello") // Outputs: -// [mobx.trace] 'Autorun@2' is invalidated due to a change in: 'Message@1.title' -Hello -``` - -It is also possible to get the internal dependency (or observer) tree by using `getDependencyTree`: - -```javascript -import { getDependencyTree } from "mobx" - -// Prints the dependency tree of the reaction coupled to the disposer. console.log(getDependencyTree(disposer)) -// Outputs: // { name: 'Autorun@2', dependencies: [ { name: 'Message@1.title' } ] } ``` @@ -275,8 +260,7 @@ runInAction(() => { This **will** react. Observable maps support observing entries that may not exist. Note that this will initially print `undefined`. You can check for the existence of an entry first by using `twitterUrls.has("Sara")`. -So in an environment without Proxy support for dynamically keyed collections always use observable maps. If you do have Proxy support you can use observable maps as well, -but you also have the option to use plain objects. +Observable maps are a good fit for dynamically keyed collections, although plain observable objects can track added properties as well. #### MobX does not track asynchronously accessed data @@ -324,64 +308,9 @@ runInAction(() => { }) ``` -This **will** react if you run React in an environment that supports Proxy. +This **will** react for objects created with `observable` or `observable.object`. Note that this is only done for objects created with `observable` or `observable.object`. New properties on class instances will not be made observable automatically. -_Environments without Proxy support_ - -This will **not** react. MobX can only track observable properties, and 'age' has not been defined as observable property above. - -However, it is possible to use the `get` and `set` methods as exposed by MobX to work around this: - -```javascript -import { get, set } from "mobx" - -autorun(() => { - console.log(get(message.author, "age")) -}) -set(message.author, "age", 10) -``` - -#### [Without Proxy support] Incorrect: using not yet existing observable object properties - -```javascript -autorun(() => { - console.log(message.author.age) -}) -extendObservable(message.author, { - age: 10 -}) -``` - -This will **not** react. MobX will not react to observable properties that did not exist when tracking started. -If the two statements are swapped, or if any other observable causes the `autorun` to re-run, the `autorun` will start tracking the `age` as well. - -#### [Without Proxy support] Correct: using MobX utilities to read / write to objects - -If you are in an environment without proxy support and still want to use observable -objects as a dynamic collection, you can handle them using the MobX `get` and `set` -API. - -The following will react as well: - -```javascript -import { get, set, observable } from "mobx" - -const twitterUrls = observable.object({ - Joe: "twitter.com/joey" -}) - -autorun(() => { - console.log(get(twitterUrls, "Sara")) // `get` can track not yet existing properties. -}) - -runInAction(() => { - set(twitterUrls, { Sara: "twitter.com/horsejs" }) -}) -``` - -Check out the [Collection utilities API](api.md#collection-utilities-) for more details. - #### TL;DR -> MobX reacts to any _existing_ **observable** _property_ that is read during the execution of a tracked function. +> MobX reacts to any **observable** property that is read during the execution of a tracked function. diff --git a/package-lock.json b/package-lock.json index 812d355475..4b8038e585 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,21 +13,32 @@ "packages/mobx-undecorate" ], "devDependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/preset-env": "^7.16.4", "@changesets/changelog-github": "^0.2.7", "@changesets/cli": "^2.11.0", + "@rollup/plugin-babel": "^5.3.0", + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "13.0.6", + "@rollup/plugin-replace": "^2.4.2", + "@rollup/plugin-terser": "^1.0.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^16.1.0", "@types/jest": "^30.0.0", "@types/node": "18", - "@types/prop-types": "^15.5.2", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", + "babel-plugin-annotate-pure-calls": "^0.4.0", + "babel-plugin-dev-expression": "^0.2.3", "coveralls": "^3.1.0", "eslint": "^6.8.0", "execa": "^4.1.0", + "expose-gc": "^1.0.0", "fs-extra": "9.0.1", "husky": "^4.2.5", "import-size": "^1.0.2", @@ -37,18 +48,18 @@ "jest-mock-console": "^1.0.1", "lint-staged": "^10.1.7", "lodash": "^4.17.4", - "minimist": "^1.2.5", "mkdirp": "1.0.4", "prettier": "^2.8.4", "pretty-quick": "3.1.0", - "prop-types": "15.6.2", "react": "19.0.0", "react-dom": "19.0.0", "react-test-renderer": "19.0.0", + "rollup": "^2.60.2", + "rollup-plugin-sourcemaps": "^0.6.3", + "rollup-plugin-typescript2": "^0.27.3", "serializr": "^2.0.3", "tape": "^5.0.1", "ts-jest": "^29.4.6", - "tsdx": "^0.14.1", "typescript": "^5.9.2" } }, @@ -108,7 +119,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -488,7 +498,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.24.7", @@ -504,7 +513,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -517,7 +525,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", @@ -532,7 +539,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "1.1.3" @@ -542,14 +548,12 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, "license": "MIT" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" @@ -559,7 +563,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -569,7 +572,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -1876,7 +1878,6 @@ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/compat-data": "^7.24.7", "@babel/helper-compilation-targets": "^7.24.7", @@ -2939,7 +2940,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -2963,7 +2963,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -3081,7 +3080,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -3102,7 +3100,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -3112,7 +3109,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=4" @@ -3122,7 +3118,6 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^7.4.0", @@ -3137,7 +3132,6 @@ "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, "license": "MIT", "dependencies": { "type-fest": "^0.20.2" @@ -3153,7 +3147,6 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -3163,7 +3156,6 @@ "version": "3.14.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -3177,7 +3169,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" @@ -3285,7 +3276,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", "deprecated": "Use @eslint/config-array instead", - "dev": true, "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^1.2.0", @@ -3315,7 +3305,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@humanwhocodes/retry": { @@ -3936,7 +3925,6 @@ "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.3.0", @@ -4005,6 +3993,17 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -4329,6 +4328,99 @@ "rollup": "^1.20.0 || ^2.0.0" } }, + "node_modules/@rollup/plugin-terser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", + "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^7.0.3", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser/node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@rollup/plugin-terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.7.tgz", + "integrity": "sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@rollup/plugin-terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@rollup/plugin-terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@rollup/plugin-terser/node_modules/terser": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz", + "integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", @@ -4387,7 +4479,6 @@ "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4499,7 +4590,6 @@ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -4539,13 +4629,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/estree": { "version": "0.0.39", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", @@ -4656,13 +4739,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -4676,7 +4752,6 @@ "integrity": "sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -4701,7 +4776,6 @@ "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4713,7 +4787,6 @@ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/react": "*" } @@ -4824,103 +4897,12 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", - "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.34.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", - "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/parser": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -5590,9 +5572,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5604,7 +5584,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -5638,9 +5617,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5683,7 +5660,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5709,7 +5685,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5719,7 +5694,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -5823,27 +5797,6 @@ "node": ">=8" } }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -5883,19 +5836,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.5", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -5904,135 +5858,24 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" + "safer-buffer": "~2.1.0" } }, "node_modules/asn1.js": { @@ -6114,13 +5957,6 @@ "node": ">=4" } }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true, - "license": "MIT" - }, "node_modules/ast-types/node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6132,7 +5968,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6159,13 +5994,6 @@ "dev": true, "license": "MIT" }, - "node_modules/asyncro": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/asyncro/-/asyncro-3.0.0.tgz", - "integrity": "sha512-nEnWYfrBmA3taTiuiOoZYmgJ/CNrSoQLeLs29SeLcPu60yaw/mHDBHV0iOZ051fTvsTHxpCY+gXibqT9wbQYfg==", - "dev": true, - "license": "MIT" - }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -6251,26 +6079,6 @@ "dev": true, "license": "MIT" }, - "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -6432,38 +6240,6 @@ "node": ">=0.10.0" } }, - "node_modules/babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "eslint": ">= 4.12.1" - } - }, - "node_modules/babel-eslint/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, "node_modules/babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", @@ -6570,7 +6346,6 @@ "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/transform": "30.3.0", "@types/babel__core": "^7.20.5", @@ -6860,13 +6635,6 @@ "dev": true, "license": "MIT" }, - "node_modules/babel-plugin-transform-rename-import": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-rename-import/-/babel-plugin-transform-rename-import-2.3.0.tgz", - "integrity": "sha512-dPgJoT57XC0PqSnLgl2FwNvxFrWlspatX2dkk7yjKQj5HHGw071vAcOf+hqW8ClqcBDMvEbm6mevn5yHAD8mlQ==", - "dev": true, - "license": "MIT" - }, "node_modules/babel-plugin-transform-simplify-comparison-operators": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", @@ -7399,7 +7167,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7593,7 +7360,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7675,7 +7441,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -7894,19 +7659,6 @@ "node": ">=8" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-truncate": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", @@ -8041,7 +7793,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -8054,7 +7805,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/color-string": { @@ -8186,13 +7936,6 @@ "dev": true, "license": "MIT" }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true, - "license": "MIT" - }, "node_modules/console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", @@ -8600,13 +8343,6 @@ "dev": true, "license": "MIT" }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -8803,7 +8539,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, "license": "MIT" }, "node_modules/deepmerge": { @@ -8816,19 +8551,6 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -8938,16 +8660,6 @@ "node": ">=8" } }, - "node_modules/diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.3" - } - }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -8984,7 +8696,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" @@ -9126,7 +8837,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/emojis-list": { @@ -9181,7 +8891,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1", @@ -9344,32 +9053,6 @@ "dev": true, "license": "MIT" }, - "node_modules/es-iterator-helpers": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", - "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -9399,16 +9082,6 @@ "node": ">= 0.4" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - } - }, "node_modules/es-to-primitive": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", @@ -9440,7 +9113,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -9456,7 +9128,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "ajv": "^6.10.0", @@ -10348,395 +10019,86 @@ "node": ">= 8" } }, - "node_modules/eslint-config-prettier": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", - "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", - "dev": true, - "license": "MIT", + "node_modules/eslint-plugin-mobx": { + "resolved": "packages/eslint-plugin-mobx", + "link": true + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", "dependencies": { - "get-stdin": "^6.0.0" - }, - "bin": { - "eslint-config-prettier-check": "bin/cli.js" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, - "peerDependencies": { - "eslint": ">=3.14.1" + "engines": { + "node": ">=8.0.0" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" + "license": "Apache-2.0", + "engines": { + "node": ">=4" } }, - "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, + "license": "Apache-2.0", "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/eslint/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": ">=6" } }, - "node_modules/eslint-plugin-flowtype": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.13.0.tgz", - "integrity": "sha512-bhewp36P+t7cEV0b6OdmoRWJCBYRiHFlqPZAG1oS3SF+Y0LQkeDvFSM4oxoxvczD1OdONCXMlJfQFiWLcV9urw==", + "node_modules/eslint/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "BSD-3-Clause", - "peer": true, + "license": "MIT", "dependencies": { - "lodash": "^4.17.15" + "color-convert": "^1.9.0" }, "engines": { "node": ">=4" - }, - "peerDependencies": { - "eslint": ">=5.0.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-plugin-mobx": { - "resolved": "packages/eslint-plugin-mobx", - "link": true - }, - "node_modules/eslint-plugin-prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", - "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=5.0.0", - "prettier": ">=1.13.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.19", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", - "object.values": "^1.2.0", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.1.tgz", - "integrity": "sha512-Y2c4b55R+6ZzwtTppKwSmK/Kar8AdLiC2f9NADCuxbcTgPPg41Gyqa6b9GppgXSvCtkRw43ZE86CT5sejKC6/g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=7" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/eslint/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", "dependencies": { @@ -10936,7 +10298,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -10949,7 +10310,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -10959,7 +10319,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -10972,7 +10331,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -10982,7 +10340,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -11303,16 +10660,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -11334,14 +10683,12 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, "license": "MIT" }, "node_modules/fastparse": { @@ -11424,7 +10771,6 @@ "integrity": "sha512-N+uhF3mswIFeziHQjGScJ/yHXYt3DiLBeC+9vWW+WjUBiClMSOlV1YrXQi+7KM2aA3Rn4Bybgv+uXFQbfkzpvg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "loader-utils": "^1.0.2" } @@ -11829,7 +11175,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true, "license": "MIT" }, "node_modules/functions-have-names": { @@ -11917,16 +11262,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -12005,7 +11340,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -12040,13 +11374,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "dev": true, - "license": "MIT" - }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -12068,13 +11395,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -12225,7 +11545,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12501,13 +11820,6 @@ "node": ">=8.12.0" } }, - "node_modules/humanize-duration": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.32.1.tgz", - "integrity": "sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==", - "dev": true, - "license": "Unlicense" - }, "node_modules/husky": { "version": "4.3.8", "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz", @@ -12817,7 +12129,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -12834,7 +12145,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -12977,16 +12287,6 @@ "node": ">= 0.4" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -13143,19 +12443,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -13244,7 +12531,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13283,7 +12569,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13319,7 +12604,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -13328,16 +12612,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -13690,7 +12964,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -13800,20 +13073,6 @@ "dev": true, "license": "MIT" }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -13836,7 +13095,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -14392,16 +13650,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.3" - } - }, "node_modules/jest-haste-map": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", @@ -15027,264 +14275,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jest-watch-typeahead": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.5.0.tgz", - "integrity": "sha512-4r36w9vU8+rdg48hj0Z7TvcSqVP6Ao8dk04grlHQNgduyCB0SqrI0xWIl85ZhXrzYvxQ0N5H+rRLAejkQzEHeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "jest-regex-util": "^25.2.1", - "jest-watcher": "^25.2.4", - "slash": "^3.0.0", - "string-length": "^3.1.0", - "strip-ansi": "^6.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "15.0.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watch-typeahead/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/jest-watch-typeahead/node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.5.0.tgz", - "integrity": "sha512-XrSfJnVASEl+5+bb51V0Q7WQx65dTSk7NL4yDdVjPnRNpM0hG+ncFmDYJo9O8jaSRcAitVbuVawyXCRoxGrT5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^25.5.0", - "@jest/types": "^25.5.0", - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "jest-util": "^25.5.0", - "string-length": "^3.1.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-watch-typeahead/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/stack-utils": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", - "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", - "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "astral-regex": "^1.0.0", - "strip-ansi": "^5.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/jest-watcher": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", @@ -15338,13 +14328,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jpjs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jpjs/-/jpjs-1.2.1.tgz", - "integrity": "sha512-GxJWybWU4NV0RNKi6EIqk6IRPOTqd/h+U7sbtyuD7yUISUzV78LdHnq2xkevJsTlz/EImux4sWj+wfMiwKLkiw==", - "dev": true, - "license": "MIT" - }, "node_modules/js-base64": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", @@ -15593,7 +14576,6 @@ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -15644,7 +14626,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, "license": "MIT" }, "node_modules/json-parse-better-errors": { @@ -15672,14 +14653,12 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, "license": "MIT" }, "node_modules/json-stringify-safe": { @@ -15740,27 +14719,10 @@ "node": ">=0.6.0" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -15778,26 +14740,6 @@ "node": ">=0.10.0" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/lcov-parse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", @@ -16020,7 +14962,6 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, "license": "MIT" }, "node_modules/lodash.some": { @@ -16041,7 +14982,6 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true, "license": "MIT" }, "node_modules/lodash.uniq": { @@ -16143,23 +15083,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lower-case/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -16730,7 +15653,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, "license": "MIT" }, "node_modules/natural-compare-lite": { @@ -16753,28 +15675,10 @@ "dev": true, "license": "MIT" }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/no-case/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/node-dir": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", + "node_modules/node-dir": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", + "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", "license": "MIT", "dependencies": { "minimatch": "^3.0.2" @@ -17073,73 +15977,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -17152,24 +15989,6 @@ "node": ">=0.10.0" } }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -17223,134 +16042,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz", - "integrity": "sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.2.0", - "is-interactive": "^1.0.0", - "log-symbols": "^3.0.0", - "mute-stream": "0.0.8", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/ora/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ora/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/log-symbols/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/log-symbols/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/log-symbols/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -17547,7 +16238,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -17627,24 +16317,6 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/pascal-case/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -18805,7 +17477,6 @@ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin-prettier.js" }, @@ -18816,19 +17487,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -18926,285 +17584,36 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" } }, - "node_modules/progress-estimator": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/progress-estimator/-/progress-estimator-0.2.2.tgz", - "integrity": "sha512-GF76Ac02MTJD6o2nMNtmtOFjwWCnHcvXyn5HOWPQnEMO8OTLw7LAvNmrwe8LmdsB+eZhwUu9fX/c9iQnBxWaFA==", + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^2.4.1", - "cli-spinners": "^1.3.1", - "humanize-duration": "^3.15.3", - "log-update": "^2.3.0" - } + "license": "ISC" }, - "node_modules/progress-estimator/node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/progress-estimator/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/progress-estimator/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/cli-spinners": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", - "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/progress-estimator/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/progress-estimator/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/progress-estimator/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress-estimator/node_modules/wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "license": "MIT" - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true, - "license": "MIT" - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -19261,7 +17670,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -19393,7 +17801,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -19404,7 +17811,6 @@ "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.25.0" }, @@ -19563,18 +17969,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -19881,7 +18275,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -20005,7 +18398,6 @@ "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -20039,61 +18431,6 @@ } } }, - "node_modules/rollup-plugin-terser": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.3.1.tgz", - "integrity": "sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w==", - "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.5.5", - "jest-worker": "^24.9.0", - "rollup-pluginutils": "^2.8.2", - "serialize-javascript": "^4.0.0", - "terser": "^4.6.2" - }, - "peerDependencies": { - "rollup": ">=0.66.0 <3" - } - }, - "node_modules/rollup-plugin-terser/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/rollup-plugin-terser/node_modules/jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/rollup-plugin-terser/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/rollup-plugin-typescript2": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.27.3.tgz", @@ -20167,23 +18504,6 @@ "node": ">= 4.0.0" } }, - "node_modules/rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "estree-walker": "^0.6.1" - } - }, - "node_modules/rollup-pluginutils/node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true, - "license": "MIT" - }, "node_modules/rrweb-cssom": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", @@ -20248,19 +18568,6 @@ "npm": ">=2.0.0" } }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/safe-array-concat": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", @@ -20570,24 +18877,6 @@ "node": ">=0.10.0" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -20695,7 +18984,17 @@ "node": ">=8" } }, - "node_modules/snapdragon": { + "node_modules/smob": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", + "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", @@ -21001,7 +19300,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/sshpk": { @@ -21181,7 +19479,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -21208,33 +19505,6 @@ "node": ">=8" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/string.prototype.trim": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", @@ -21313,7 +19583,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -21373,7 +19642,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -21386,7 +19654,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -21971,7 +20238,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, "license": "MIT" }, "node_modules/through": { @@ -22005,17 +20271,6 @@ "node": ">=0.6.0" } }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -22147,767 +20402,154 @@ "node_modules/to-regex/node_modules/extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/to-regex/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.3", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tsdx": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/tsdx/-/tsdx-0.14.1.tgz", - "integrity": "sha512-keHmFdCL2kx5nYFlBdbE3639HQ2v9iGedAFAajobrUTH2wfX0nLPdDhbHv+GHLQZqf0c5ur1XteE8ek/+Eyj5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.4.4", - "@babel/helper-module-imports": "^7.0.0", - "@babel/parser": "^7.11.5", - "@babel/plugin-proposal-class-properties": "^7.4.4", - "@babel/preset-env": "^7.11.0", - "@babel/traverse": "^7.11.5", - "@rollup/plugin-babel": "^5.1.0", - "@rollup/plugin-commonjs": "^11.0.0", - "@rollup/plugin-json": "^4.0.0", - "@rollup/plugin-node-resolve": "^9.0.0", - "@rollup/plugin-replace": "^2.2.1", - "@types/jest": "^25.2.1", - "@typescript-eslint/eslint-plugin": "^2.12.0", - "@typescript-eslint/parser": "^2.12.0", - "ansi-escapes": "^4.2.1", - "asyncro": "^3.0.0", - "babel-eslint": "^10.0.3", - "babel-plugin-annotate-pure-calls": "^0.4.0", - "babel-plugin-dev-expression": "^0.2.1", - "babel-plugin-macros": "^2.6.1", - "babel-plugin-polyfill-regenerator": "^0.0.4", - "babel-plugin-transform-rename-import": "^2.3.0", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "enquirer": "^2.3.4", - "eslint": "^6.1.0", - "eslint-config-prettier": "^6.0.0", - "eslint-config-react-app": "^5.2.1", - "eslint-plugin-flowtype": "^3.13.0", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-prettier": "^3.1.0", - "eslint-plugin-react": "^7.14.3", - "eslint-plugin-react-hooks": "^2.2.0", - "execa": "^4.0.3", - "fs-extra": "^9.0.0", - "jest": "^25.3.0", - "jest-watch-typeahead": "^0.5.0", - "jpjs": "^1.2.1", - "lodash.merge": "^4.6.2", - "ora": "^4.0.3", - "pascal-case": "^3.1.1", - "prettier": "^1.19.1", - "progress-estimator": "^0.2.2", - "regenerator-runtime": "^0.13.7", - "rollup": "^1.32.1", - "rollup-plugin-sourcemaps": "^0.6.2", - "rollup-plugin-terser": "^5.1.2", - "rollup-plugin-typescript2": "^0.27.3", - "sade": "^1.4.2", - "semver": "^7.1.1", - "shelljs": "^0.8.3", - "tiny-glob": "^0.2.6", - "ts-jest": "^25.3.1", - "tslib": "^1.9.3", - "typescript": "^3.7.3" - }, - "bin": { - "tsdx": "dist/index.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tsdx/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.0.3.tgz", - "integrity": "sha512-dULDd/APiP4JowYDAMosecKOi/1v+UId99qhBGiO3myM29KtAVKS/R3x3OJJNBR0FeYB1BcYb2dCwkhqvxWXXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/traverse": "^7.11.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/tsdx/node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/tsdx/node_modules/@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/tsdx/node_modules/@jest/types/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tsdx/node_modules/@rollup/plugin-commonjs": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-11.1.0.tgz", - "integrity": "sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^3.0.8", - "commondir": "^1.0.1", - "estree-walker": "^1.0.1", - "glob": "^7.1.2", - "is-reference": "^1.1.2", - "magic-string": "^0.25.2", - "resolve": "^1.11.0" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/tsdx/node_modules/@rollup/plugin-node-resolve": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz", - "integrity": "sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.17.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/tsdx/node_modules/@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/tsdx/node_modules/@types/jest": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz", - "integrity": "sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-diff": "^25.2.1", - "pretty-format": "^25.2.1" - } - }, - "node_modules/tsdx/node_modules/@types/yargs": { - "version": "15.0.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/tsdx/node_modules/@typescript-eslint/eslint-plugin": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", - "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "2.34.0", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^3.0.0", - "tsutils": "^3.17.1" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^2.0.0", - "eslint": "^5.0.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tsdx/node_modules/@typescript-eslint/parser": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", - "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.34.0", - "@typescript-eslint/typescript-estree": "2.34.0", - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tsdx/node_modules/@typescript-eslint/typescript-estree": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", - "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tsdx/node_modules/babel-plugin-macros": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", - "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.7.2", - "cosmiconfig": "^6.0.0", - "resolve": "^1.12.0" - } - }, - "node_modules/tsdx/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.0.4.tgz", - "integrity": "sha512-+/uCzO9JTYVZVGCpZpVAQkgPGt2zkR0VYiZvJ4aVoCe4ccgpKvNQqcjzAgQzSsjK64Jhc5hvrCR3l0087BevkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.0.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/tsdx/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tsdx/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tsdx/node_modules/eslint-config-react-app": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz", - "integrity": "sha512-pGIZ8t0mFLcV+6ZirRgYK6RVqUIKRIi9MmgzUEmrIknsn3AdO0I32asO86dJgloHq+9ZPl8UIg8mYrvgP5u2wQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "confusing-browser-globals": "^1.0.9" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "2.x", - "@typescript-eslint/parser": "2.x", - "babel-eslint": "10.x", - "eslint": "6.x", - "eslint-plugin-flowtype": "3.x || 4.x", - "eslint-plugin-import": "2.x", - "eslint-plugin-jsx-a11y": "6.x", - "eslint-plugin-react": "7.x", - "eslint-plugin-react-hooks": "1.x || 2.x" - } - }, - "node_modules/tsdx/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/tsdx/node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tsdx/node_modules/jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/tsdx/node_modules/jest-diff/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tsdx/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "license": "MIT", "dependencies": { - "minimist": "^1.2.6" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/tsdx/node_modules/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true, + "node_modules/to-regex/node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/tsdx/node_modules/pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "license": "MIT", "dependencies": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "is-plain-object": "^2.0.4" }, "engines": { - "node": ">= 8.3" + "node": ">=0.10.0" } }, - "node_modules/tsdx/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tsdx/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tsdx/node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "engines": { + "node": ">=16" } }, - "node_modules/tsdx/node_modules/rollup": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", - "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@types/estree": "*", - "@types/node": "*", - "acorn": "^7.1.0" + "punycode": "^2.3.1" }, - "bin": { - "rollup": "dist/bin/rollup" + "engines": { + "node": ">=18" } }, - "node_modules/tsdx/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/tsdx/node_modules/ts-jest": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.5.1.tgz", - "integrity": "sha512-kHEUlZMK8fn8vkxDjwbHlxXRB9dHYpyzqKIGDNxbzs+Rz+ssNDSDNusEK8Fk/sDd4xE6iKoQLfFkFVaskmTJyw==", + "node_modules/ts-jest": { + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", "dev": true, "license": "MIT", "dependencies": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "micromatch": "4.x", - "mkdirp": "0.x", - "semver": "6.x", - "yargs-parser": "18.x" + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" }, "engines": { - "node": ">= 8" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { - "jest": ">=25 <26", - "typescript": ">=3.4 <4.0" + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } } }, - "node_modules/tsdx/node_modules/ts-jest/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - } - }, - "node_modules/tsdx/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/tsdx/node_modules/yargs-parser/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, - "license": "MIT", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=6" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/tslib": { @@ -23087,7 +20729,6 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -23377,7 +21018,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -23447,15 +21087,6 @@ "node": ">=0.10.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -23495,7 +21126,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", - "dev": true, "license": "MIT" }, "node_modules/v8-to-istanbul": { @@ -23911,16 +21541,6 @@ "node": ">=0.10.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -24394,7 +22014,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -24641,17 +22260,8 @@ "version": "0.0.14", "license": "MIT", "devDependencies": { - "@babel/core": "^7.16.0", - "@babel/preset-env": "^7.16.4", - "@rollup/plugin-babel": "^5.3.0", - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "13.0.6", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^7.0.0", "eslint-7": "npm:eslint@^7.0.0", - "eslint-9": "npm:eslint@^9.0.0", - "rollup": "^2.60.2" + "eslint-9": "npm:eslint@^9.0.0" }, "funding": { "type": "opencollective", @@ -24665,8 +22275,8 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/highlight": "^7.10.4" } @@ -24675,8 +22285,8 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -24685,8 +22295,8 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -24701,8 +22311,8 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -24759,8 +22369,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "eslint-visitor-keys": "^1.1.0" }, @@ -24775,8 +22385,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=4" } @@ -24785,8 +22395,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=10" } @@ -24795,8 +22405,8 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", @@ -24810,8 +22420,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=4" } @@ -24820,8 +22430,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -24833,8 +22443,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -24848,15 +22458,15 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "packages/eslint-plugin-mobx/node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -24871,8 +22481,8 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } @@ -24881,8 +22491,8 @@ "version": "3.14.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -24895,15 +22505,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "packages/eslint-plugin-mobx/node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -24916,8 +22526,8 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -24934,8 +22544,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -24944,8 +22554,8 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -24954,8 +22564,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -24968,8 +22578,8 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, "license": "ISC", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -24984,8 +22594,8 @@ "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -24997,8 +22607,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -25010,8 +22620,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -25020,8 +22630,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -25038,8 +22648,8 @@ "version": "6.8.2", "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", - "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", @@ -25055,8 +22665,8 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", @@ -25072,8 +22682,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -25085,8 +22695,8 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -25098,8 +22708,8 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", + "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -25111,16 +22721,9 @@ } }, "packages/mobx": { - "version": "6.16.0", + "version": "6.16.1", "license": "MIT", "devDependencies": { - "@babel/core": "^7.9.0", - "@babel/plugin-proposal-class-properties": "^7.8.3", - "@babel/plugin-proposal-decorators": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.9.0", - "@babel/preset-env": "^7.9.5", - "@babel/preset-typescript": "^7.9.0", - "@babel/runtime": "^7.9.2", "conditional-type-checks": "^1.0.5" }, "funding": { @@ -25132,10 +22735,9 @@ "version": "9.2.2", "license": "MIT", "dependencies": { - "mobx-react-lite": "^4.1.1" + "mobx-react-lite": "^5.0.0" }, "devDependencies": { - "expose-gc": "^1.0.0", "mobx": "^6.15.0", "mobx-react-lite": "^4.1.1" }, @@ -25144,26 +22746,14 @@ "url": "https://opencollective.com/mobx" }, "peerDependencies": { - "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "mobx": "^7.0.0", + "react": "^18 || ^19" } }, "packages/mobx-react-lite": { "version": "4.1.1", "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.4.0" - }, "devDependencies": { - "expose-gc": "^1.0.0", "mobx": "^6.15.0" }, "funding": { @@ -25171,16 +22761,8 @@ "url": "https://opencollective.com/mobx" }, "peerDependencies": { - "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "mobx": "^7.0.0", + "react": "^18 || ^19" } }, "packages/mobx-undecorate": { diff --git a/package.json b/package.json index 7cc8f493d9..86e7dfa978 100644 --- a/package.json +++ b/package.json @@ -21,15 +21,15 @@ "build": "npm run build --workspaces --if-present", "build:check": "npm run build:check --workspaces --if-present", "build:test": "npm run build:test --workspaces --if-present", + "check-size": "node scripts/size/index.js", "test": "jest", "coverage": "jest --coverage", "lint": "eslint packages/*/src/**/* --ext .js,.ts,.tsx", "prettier": "prettier --write **/*.{js,ts,md}", "release": "npm run prepublishOnly --workspaces --if-present && changeset publish", - "test:size": "npm run test:size --workspaces --if-present", "mobx": "npm -w mobx run", - "mobx-react": "npm -w mobx-react run", "mobx-react-lite": "npm -w mobx-react-lite run", + "mobx-react": "npm -w mobx-react run", "mobx-undecorate": "npm -w mobx-undecorate run", "eslint-plugin-mobx": "npm -w eslint-plugin-mobx run", "docs:build": "npm --prefix website run build", @@ -39,21 +39,32 @@ "dedup": "npm dedupe" }, "devDependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/preset-env": "^7.16.4", "@changesets/changelog-github": "^0.2.7", "@changesets/cli": "^2.11.0", + "@rollup/plugin-babel": "^5.3.0", + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "13.0.6", + "@rollup/plugin-replace": "^2.4.2", + "@rollup/plugin-terser": "^1.0.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^16.1.0", "@types/jest": "^30.0.0", "@types/node": "18", - "@types/prop-types": "^15.5.2", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", + "babel-plugin-annotate-pure-calls": "^0.4.0", + "babel-plugin-dev-expression": "^0.2.3", "coveralls": "^3.1.0", "eslint": "^6.8.0", "execa": "^4.1.0", + "expose-gc": "^1.0.0", "fs-extra": "9.0.1", "husky": "^4.2.5", "import-size": "^1.0.2", @@ -63,18 +74,18 @@ "jest-mock-console": "^1.0.1", "lint-staged": "^10.1.7", "lodash": "^4.17.4", - "minimist": "^1.2.5", "mkdirp": "1.0.4", "prettier": "^2.8.4", "pretty-quick": "3.1.0", - "prop-types": "15.6.2", "react": "19.0.0", "react-dom": "19.0.0", "react-test-renderer": "19.0.0", + "rollup": "^2.60.2", + "rollup-plugin-sourcemaps": "^0.6.3", + "rollup-plugin-typescript2": "^0.27.3", "serializr": "^2.0.3", "tape": "^5.0.1", "ts-jest": "^29.4.6", - "tsdx": "^0.14.1", "typescript": "^5.9.2" }, "husky": { diff --git a/packages/eslint-plugin-mobx/package.json b/packages/eslint-plugin-mobx/package.json index c1c662eebc..8c075ce50c 100644 --- a/packages/eslint-plugin-mobx/package.json +++ b/packages/eslint-plugin-mobx/package.json @@ -28,17 +28,8 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" }, "devDependencies": { - "@babel/core": "^7.16.0", - "@babel/preset-env": "^7.16.4", - "@rollup/plugin-babel": "^5.3.0", - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "13.0.6", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^7.0.0", "eslint-7": "npm:eslint@^7.0.0", - "eslint-9": "npm:eslint@^9.0.0", - "rollup": "^2.60.2" + "eslint-9": "npm:eslint@^9.0.0" }, "keywords": [ "eslint", diff --git a/packages/mobx-react-lite/README.md b/packages/mobx-react-lite/README.md index 30ad4b190f..209516a8b5 100644 --- a/packages/mobx-react-lite/README.md +++ b/packages/mobx-react-lite/README.md @@ -6,18 +6,18 @@ [![Discuss on Github](https://img.shields.io/badge/discuss%20on-GitHub-orange)](https://github.com/mobxjs/mobx/discussions) [![View changelog](https://img.shields.io/badge/changelogs.xyz-Explore%20Changelog-brightgreen)](https://changelogs.xyz/mobx-react-lite) -This is a lighter version of [mobx-react](https://github.com/mobxjs/mobx-react) which supports React **functional components only** and as such makes the library slightly faster and smaller (_only 1.5kB gzipped_). Note however that it is possible to use `` inside the render of class components. -Unlike `mobx-react`, it doesn't `Provider`/`inject`, as `useContext` can be used instead. +This package supports React **function components only**. Use [mobx-react](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react) if you also need class component support. It is still possible to use `` inside the render of class components. ## Compatibility table (major versions) | mobx | mobx-react-lite | Browser | | ---- | --------------- | ---------------------------------------------- | +| 7 | 5 | Modern browsers with native Proxy support | | 6 | 3 | Modern browsers (IE 11+ in compatibility mode) | | 5 | 2 | Modern browsers | | 4 | 2 | IE 11+, RN w/o Proxy support | -`mobx-react-lite` requires React 16.8 or higher. +`mobx-react-lite` version 5 requires React 18 or higher. ## User Guide 👉 https://mobx.js.org/react-integration.html @@ -38,7 +38,7 @@ Is a React component, which applies observer to an anonymous region in your comp Creates an observable object with the given properties, methods and computed values. -Note that computed values cannot directly depend on non-observable values, but only on observable values, so it might be needed to sync properties into the observable using `useEffect` (see the example below at `useAsObservableSource`). +Note that computed values cannot directly depend on non-observable values, but only on observable values, so it might be needed to sync properties into the observable using `useEffect`. `useLocalObservable` is a short-hand for: @@ -50,108 +50,9 @@ Call `enableStaticRendering(true)` when running in an SSR environment, in which --- -## Deprecated APIs +## Removed APIs in version 5 -### **`useObserver(fn: () => T, baseComponentName = "observed", options?: IUseObserverOptions): T`** (deprecated) - -_This API is deprecated in 3.\*. It is often used wrong (e.g. to select data rather than for rendering, and `` better decouples the rendering from the component updates_ - -```ts -interface IUseObserverOptions { - // optional custom hook that should make a component re-render (or not) upon changes - // Supported in 2.x only - useForceUpdate: () => () => void -} -``` - -It allows you to use an observer like behaviour, but still allowing you to optimize the component in any way you want (e.g. using memo with a custom areEqual, using forwardRef, etc.) and to declare exactly the part that is observed (the render phase). - -### **`useLocalStore(initializer: () => T, source?: S): T`** (deprecated) - -_This API is deprecated in 3.\*. Use `useLocalObservable` instead. They do roughly the same, but `useLocalObservable` accepts an set of annotations as second argument, rather than a `source` object. Using `source` is not recommended, see the deprecation message at `useAsObservableSource` for details_ - -Local observable state can be introduced by using the useLocalStore hook, that runs its initializer function once to create an observable store and keeps it around for a lifetime of a component. - -The annotations are similar to the annotations that are passed in to MobX's [`observable`](https://mobx.js.org/observable.html#available-annotations) API, and can be used to override the automatic member inference of specific fields. - -### **`useAsObservableSource(source: T): T`** (deprecated) - -The useAsObservableSource hook can be used to turn any set of values into an observable object that has a stable reference (the same object is returned every time from the hook). - -_This API is deprecated in 3.\* as it relies on observables to be updated during rendering which is an anti-pattern. Instead, use `useEffect` to synchronize non-observable values with values. Example:_ - -```javascript -// Before: -function Measurement({ unit }) { - const observableProps = useAsObservableSource({ unit }) - const state = useLocalStore(() => ({ - length: 0, - get lengthWithUnit() { - // lengthWithUnit can only depend on observables, hence the above conversion with `useAsObservableSource` - return observableProps.unit === "inch" - ? `${this.length / 2.54} inch` - : `${this.length} cm` - } - })) - - return

{state.lengthWithUnit}

-} - -// After: -function Measurement({ unit }) { - const state = useLocalObservable(() => ({ - unit, // the initial unit - length: 0, - get lengthWithUnit() { - // lengthWithUnit can only depend on observables, hence the above conversion with `useAsObservableSource` - return this.unit === "inch" ? `${this.length / 2.54} inch` : `${this.length} cm` - } - })) - - useEffect(() => { - // sync the unit from 'props' into the observable 'state' - state.unit = unit - }, [unit]) - - return

{state.lengthWithUnit}

-} -``` - -Note that, at your own risk, it is also possible to not use `useEffect`, but do `state.unit = unit` instead in the rendering. -This is closer to the old behavior, but React will warn correctly about this if this would affect the rendering of other components. - -## Observer batching (deprecated) - -_Note: configuring observer batching is only needed when using `mobx-react-lite` 2.0.* or 2.1.*. From 2.2 onward it will be configured automatically based on the availability of react-dom / react-native packages_ - -[Check out the elaborate explanation](https://github.com/mobxjs/mobx-react/pull/787#issuecomment-573599793). - -In short without observer batching the React doesn't guarantee the order component rendering in some cases. We highly recommend that you configure batching to avoid these random surprises. - -Import one of these before any React rendering is happening, typically `index.js/ts`. For Jest tests you can utilize [setupFilesAfterEnv](https://jestjs.io/docs/en/configuration#setupfilesafterenv-array). - -**React DOM:** - -> import 'mobx-react-lite/batchingForReactDom' - -**React Native:** - -> import 'mobx-react-lite/batchingForReactNative' - -### Opt-out - -To opt-out from batching in some specific cases, simply import the following to silence the warning. - -> import 'mobx-react-lite/batchingOptOut' - -### Custom batched updates - -Above imports are for a convenience to utilize standard versions of batching. If you for some reason have customized version of batched updates, you can do the following instead. - -```js -import { observerBatching } from "mobx-react-lite" -observerBatching(customBatchedUpdates) -``` +`useObserver`, `useLocalStore`, `useAsObservableSource`, `useStaticRendering`, batching imports, `observerBatching`, and `isObserverBatched` have been removed. Use `observer`, ``, `useLocalObservable`, and `enableStaticRendering`; React 18 renderers handle batching. ## Testing diff --git a/packages/mobx-react-lite/__tests__/ObserverComponent.test.tsx b/packages/mobx-react-lite/__tests__/ObserverComponent.test.tsx index b533ed756e..8f5a0446dc 100644 --- a/packages/mobx-react-lite/__tests__/ObserverComponent.test.tsx +++ b/packages/mobx-react-lite/__tests__/ObserverComponent.test.tsx @@ -1,12 +1,10 @@ import mockConsole from "jest-mock-console" import * as mobx from "mobx" import * as React from "react" -import { act, cleanup, render } from "@testing-library/react" +import { act, render } from "@testing-library/react" import { Observer } from "../src" -afterEach(cleanup) - describe("regions should rerender component", () => { const execute = () => { const data = mobx.observable.box("hi") @@ -43,13 +41,13 @@ it("renders null if no children/render prop is supplied a function", () => { restoreConsole() }) -it.skip("prop types checks for children/render usage", () => { +it("prop types checks for children/render usage", () => { const Comp = () => ( + // @ts-expect-error render and children are mutually exclusive children}>{() => children} ) const restoreConsole = mockConsole("error") render() - // tslint:disable-next-line:no-console expect(console.error).toHaveBeenCalledWith( expect.stringContaining("Do not use children and render in the same time") ) diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/observer.test.tsx.snap b/packages/mobx-react-lite/__tests__/__snapshots__/observer.test.tsx.snap index aa7385fc12..802eb2588b 100644 --- a/packages/mobx-react-lite/__tests__/__snapshots__/observer.test.tsx.snap +++ b/packages/mobx-react-lite/__tests__/__snapshots__/observer.test.tsx.snap @@ -64,52 +64,4 @@ exports[`issue 12 run transaction 2`] = `
`; -exports[`issue 309 isObserverBatched is still defined and yields true by default 1`] = ` -[MockFunction] { - "calls": [ - [ - "[MobX] Deprecated", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} -`; - -exports[`issue 309 isObserverBatched is still defined and yields true by default 2`] = ` -[MockFunction] { - "calls": [ - [ - "[MobX] Deprecated", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} -`; - -exports[`observer(cmp, { forwardRef: true }) + useImperativeHandle 1`] = ` -[MockFunction] { - "calls": [ - [ - "[mobx-react-lite] \`observer(fn, { forwardRef: true })\` is deprecated, use \`observer(React.forwardRef(fn))\`", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} -`; - exports[`useImperativeHandle and forwardRef should work with useObserver 1`] = `[MockFunction]`; diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/printDebugValue.test.ts.snap b/packages/mobx-react-lite/__tests__/__snapshots__/printDebugValue.test.ts.snap deleted file mode 100644 index 5cd288b65e..0000000000 --- a/packages/mobx-react-lite/__tests__/__snapshots__/printDebugValue.test.ts.snap +++ /dev/null @@ -1,26 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`printDebugValue 1`] = ` -{ - "dependencies": [ - { - "name": "ObservableObject@1.euro", - }, - { - "dependencies": [ - { - "name": "ObservableObject@1.euro", - }, - ], - "name": "ObservableObject@1.pound", - }, - ], - "name": "Autorun@2", -} -`; - -exports[`printDebugValue 2`] = ` -{ - "name": "Autorun@2", -} -`; diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/useAsObservableSource.deprecated.test.tsx.snap b/packages/mobx-react-lite/__tests__/__snapshots__/useAsObservableSource.deprecated.test.tsx.snap deleted file mode 100644 index 88ba3f79b2..0000000000 --- a/packages/mobx-react-lite/__tests__/__snapshots__/useAsObservableSource.deprecated.test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`base useAsObservableSource should work with 1`] = ` -[MockFunction] { - "calls": [ - [ - "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples.", - ], - [ - "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead.", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - { - "type": "return", - "value": undefined, - }, - ], -} -`; diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/useLocalStore.deprecated.test.tsx.snap b/packages/mobx-react-lite/__tests__/__snapshots__/useLocalStore.deprecated.test.tsx.snap deleted file mode 100644 index 5e9338971b..0000000000 --- a/packages/mobx-react-lite/__tests__/__snapshots__/useLocalStore.deprecated.test.tsx.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`base useLocalStore should work 1`] = ` -[MockFunction] { - "calls": [ - [ - "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead.", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} -`; - -exports[`is used to keep observable within component body with props and useObserver 1`] = ` -[MockFunction] { - "calls": [ - [ - "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples.", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} -`; diff --git a/packages/mobx-react-lite/__tests__/api.test.ts b/packages/mobx-react-lite/__tests__/api.test.tsx similarity index 71% rename from packages/mobx-react-lite/__tests__/api.test.ts rename to packages/mobx-react-lite/__tests__/api.test.tsx index a2f7cf57e6..d14bd4138e 100644 --- a/packages/mobx-react-lite/__tests__/api.test.ts +++ b/packages/mobx-react-lite/__tests__/api.test.tsx @@ -12,13 +12,7 @@ test("correct api should be exposed", function () { "observer", "Observer", "useLocalObservable", - "useLocalStore", - "useAsObservableSource", "clearTimers", - "useObserver", - "isObserverBatched", - "observerBatching", - "useStaticRendering", "_observerFinalizationRegistry" ].sort() ) diff --git a/packages/mobx-react-lite/__tests__/assertEnvironment.test.ts b/packages/mobx-react-lite/__tests__/assertEnvironment.test.ts deleted file mode 100644 index 33c414e844..0000000000 --- a/packages/mobx-react-lite/__tests__/assertEnvironment.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -afterEach(() => { - jest.resetModules() - jest.resetAllMocks() -}) - -it("throws if react is not installed", () => { - jest.mock("react", () => ({})) - expect(() => require("../src/utils/assertEnvironment.ts")).toThrowErrorMatchingInlineSnapshot( - `"mobx-react-lite requires React with Hooks support"` - ) -}) - -it("throws if mobx is not installed", () => { - jest.mock("react", () => ({ useState: true })) - jest.mock("mobx", () => ({})) - expect(() => require("../src/utils/assertEnvironment.ts")).toThrowErrorMatchingInlineSnapshot( - `"mobx-react-lite@3 requires mobx at least version 6 to be available"` - ) -}) - -export default "Cannot use import statement outside a module" diff --git a/packages/mobx-react-lite/__tests__/assertEnvironment.test.tsx b/packages/mobx-react-lite/__tests__/assertEnvironment.test.tsx new file mode 100644 index 0000000000..64ebec3afa --- /dev/null +++ b/packages/mobx-react-lite/__tests__/assertEnvironment.test.tsx @@ -0,0 +1,31 @@ +afterEach(() => { + jest.resetModules() + jest.resetAllMocks() +}) + +it("throws if react is not installed", () => { + jest.mock("react", () => ({})) + expect(() => require("../src/utils/assertEnvironment.ts")).toThrowErrorMatchingInlineSnapshot( + `"mobx-react-lite requires React 18 or later"` + ) +}) + +it("throws if mobx is not installed", () => { + jest.mock("react", () => ({ useState: true, useSyncExternalStore: true })) + jest.mock("mobx", () => ({})) + expect(() => require("../src/utils/assertEnvironment.ts")).toThrowErrorMatchingInlineSnapshot( + `"mobx-react-lite requires mobx at least version 7 to be available"` + ) +}) + +it("throws if mobx is older than version 7", () => { + jest.mock("react", () => ({ useState: true, useSyncExternalStore: true })) + jest.mock("mobx", () => ({ + _getGlobalState: () => ({ version: 6 }) + })) + expect(() => require("../src/utils/assertEnvironment.ts")).toThrowErrorMatchingInlineSnapshot( + `"mobx-react-lite requires mobx at least version 7 to be available"` + ) +}) + +export default "Cannot use import statement outside a module" diff --git a/packages/mobx-react-lite/__tests__/enforceActions.test.tsx b/packages/mobx-react-lite/__tests__/enforceActions.test.tsx index c26c51b1f0..1758129dc3 100644 --- a/packages/mobx-react-lite/__tests__/enforceActions.test.tsx +++ b/packages/mobx-react-lite/__tests__/enforceActions.test.tsx @@ -1,18 +1,10 @@ import * as mobx from "mobx" -import { _resetGlobalState } from "mobx" import * as React from "react" import { useEffect } from "react" -import { observer, useLocalObservable } from "mobx-react" +import { observer, useLocalObservable } from "../src" import { render } from "@testing-library/react" let consoleWarnMock: jest.SpyInstance | undefined -afterEach(() => { - consoleWarnMock?.mockRestore() -}) - -afterEach(() => { - _resetGlobalState() -}) describe("enforcing actions", () => { it("'never' should work", () => { diff --git a/packages/mobx-react-lite/__tests__/observer.test.tsx b/packages/mobx-react-lite/__tests__/observer.test.tsx index 1083c94a04..f522c2e73a 100644 --- a/packages/mobx-react-lite/__tests__/observer.test.tsx +++ b/packages/mobx-react-lite/__tests__/observer.test.tsx @@ -1,18 +1,14 @@ -import { act, cleanup, fireEvent, render } from "@testing-library/react" +import { act, fireEvent, render } from "@testing-library/react" import mockConsole from "jest-mock-console" import * as mobx from "mobx" import React from "react" -import { observer, useObserver, isObserverBatched, enableStaticRendering } from "../src" +import { observer, enableStaticRendering } from "../src" +import { useObserver } from "../src/useObserver" const getDNode = (obj: any, prop?: string) => mobx.getObserverTree(obj, prop) -afterEach(cleanup) - let consoleWarnMock: jest.SpyInstance | undefined -afterEach(() => { - consoleWarnMock?.mockRestore() -}) function runTestSuite(mode: "observer" | "useObserver") { function obsComponent

( @@ -274,14 +270,6 @@ function runTestSuite(mode: "observer" | "useObserver") { }) }) - describe("issue 309", () => { - test("isObserverBatched is still defined and yields true by default", () => { - consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) - expect(isObserverBatched()).toBe(true) - expect(consoleWarnMock).toMatchSnapshot() - }) - }) - test("changing state in render should fail", () => { // This test is most likely obsolete ... exception is not thrown const data = mobx.observable.box(2) @@ -303,7 +291,6 @@ function runTestSuite(mode: "observer" | "useObserver") { data.set(3) }) expect(container).toMatchSnapshot() - mobx._resetGlobalState() }) describe("should render component even if setState called with exactly the same props", () => { @@ -489,43 +476,6 @@ function runTestSuite(mode: "observer" | "useObserver") { runTestSuite("observer") runTestSuite("useObserver") -test("observer(cmp, { forwardRef: true }) + useImperativeHandle", () => { - consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) - - interface IMethods { - focus(): void - } - - interface IProps { - value: string - ref: React.Ref - } - - const FancyInput = observer( - (props: IProps, ref: React.Ref) => { - const inputRef = React.useRef(null) - React.useImperativeHandle( - ref, - () => ({ - focus: () => { - inputRef.current!.focus() - } - }), - [] - ) - return - }, - { forwardRef: true } - ) - - const cr = React.createRef() - render() - expect(cr).toBeTruthy() - expect(cr.current).toBeTruthy() - expect(typeof cr.current!.focus).toBe("function") - expect(consoleWarnMock).toMatchSnapshot() -}) - test("observer(forwardRef(cmp)) + useImperativeHandle", () => { interface IMethods { focus(): void @@ -734,23 +684,7 @@ it("should have overload for props with children", () => { // this test has no `expect` calls as it verifies whether such component compiles or not }) -it("should have overload for empty options", () => { - // empty options are not really making sense now, but we shouldn't rely on `forwardRef` - // being specified in case other options are added in the future - - interface IProps { - value: string - } - const TestComponent = observer(({ value }) => { - return null - }, {}) - - render() - - // this test has no `expect` calls as it verifies whether such component compiles or not -}) - -it("should have overload for props with children when forwardRef", () => { +it("should have overload for props with children when using forwardRef", () => { interface IMethods { focus(): void } @@ -758,11 +692,10 @@ it("should have overload for props with children when forwardRef", () => { interface IProps { value: string } - const TestComponent = observer( - ({ value }, ref) => { + const TestComponent = observer( + React.forwardRef(({ value }, ref) => { return null - }, - { forwardRef: true } + }) ) render() @@ -799,38 +732,26 @@ it("should preserve generic parameters", () => { // this test has no `expect` calls as it verifies whether such component compiles or not }) -it("should preserve generic parameters when forwardRef", () => { +it("should preserve concrete props when using forwardRef", () => { interface IMethods { focus(): void } - interface IColor { - name: string - css: string - } - - interface ITestComponentProps { - value: T - callback: (value: T) => void + interface ITestComponentProps { + value: string + callback: (value: string) => void } const TestComponent = observer( - (props: ITestComponentProps, ref: React.Ref) => { + React.forwardRef((props, ref) => { return null - }, - { forwardRef: true } + }) ) function callbackString(value: string) { return } - function callbackColor(value: IColor) { - return - } render() - render( - - ) // this test has no `expect` calls as it verifies whether such component compiles or not }) @@ -1056,21 +977,6 @@ it.skip("Legacy context support", () => { render() }) -it("Throw when trying to set contextType on observer", () => { - const NamedObserver = observer(function TestCmp() { - return null - }) - const AnonymousObserver = observer(() => null) - expect(() => { - ;(NamedObserver as any).contextTypes = {} - }).toThrow(/\[mobx-react-lite\] `TestCmp.contextTypes` must be set before applying `observer`./) - expect(() => { - ;(AnonymousObserver as any).contextTypes = {} - }).toThrow( - /\[mobx-react-lite\] `Component.contextTypes` must be set before applying `observer`./ - ) -}) - test("Anonymous component displayName #3192", () => { // React prints errors even if we catch em const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {}) @@ -1152,6 +1058,4 @@ test("`isolateGlobalState` shouldn't break reactivity #3734", async () => { ) expect(container).toHaveTextContent("1") unmount() - - mobx._resetGlobalState() }) diff --git a/packages/mobx-react-lite/__tests__/printDebugValue.test.ts b/packages/mobx-react-lite/__tests__/printDebugValue.test.ts deleted file mode 100644 index b383d7327b..0000000000 --- a/packages/mobx-react-lite/__tests__/printDebugValue.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { $mobx, autorun, observable } from "mobx" -import { printDebugValue } from "../src/utils/printDebugValue" - -test("printDebugValue", () => { - const money = observable({ - euro: 10, - get pound() { - return this.euro / 1.15 - } - }) - - const disposer = autorun(() => { - const { euro, pound } = money - if (euro === pound) { - // tslint:disable-next-line: no-console - console.log("Weird..") - } - }) - - const value = (disposer as any)[$mobx] - - expect(printDebugValue(value)).toMatchSnapshot() - - disposer() - - expect(printDebugValue(value)).toMatchSnapshot() -}) diff --git a/packages/mobx-react-lite/__tests__/strictAndConcurrentMode.test.tsx b/packages/mobx-react-lite/__tests__/strictAndConcurrentMode.test.tsx index 519fd1193f..ff51212235 100644 --- a/packages/mobx-react-lite/__tests__/strictAndConcurrentMode.test.tsx +++ b/packages/mobx-react-lite/__tests__/strictAndConcurrentMode.test.tsx @@ -1,12 +1,10 @@ -import { act, cleanup, render } from "@testing-library/react" +import { act, render } from "@testing-library/react" import mockConsole from "jest-mock-console" import * as mobx from "mobx" import * as React from "react" import { useObserver } from "../src/useObserver" -afterEach(cleanup) - test("uncommitted observing components should not attempt state changes", () => { const store = mobx.observable({ count: 0 }) @@ -34,7 +32,6 @@ test("uncommitted observing components should not attempt state changes", () => }) // Check to see if any console errors were reported. - // tslint:disable-next-line: no-console expect(console.error).not.toHaveBeenCalled() } finally { restoreConsole() diff --git a/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingFinalizationRegistry.test.tsx b/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingFinalizationRegistry.test.tsx index 7bfac4c0ce..4e4698ed30 100644 --- a/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingFinalizationRegistry.test.tsx +++ b/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingFinalizationRegistry.test.tsx @@ -1,4 +1,4 @@ -import { cleanup, render, waitFor } from "@testing-library/react" +import { render, waitFor } from "@testing-library/react" import * as mobx from "mobx" import * as React from "react" import { useObserver } from "../src/useObserver" @@ -11,8 +11,6 @@ if (typeof globalThis.FinalizationRegistry !== "function") { expect(observerFinalizationRegistry).toBeInstanceOf(globalThis.FinalizationRegistry) -afterEach(cleanup) - function nextFrame() { return new Promise(accept => setTimeout(accept, 1)) } diff --git a/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingTimers.test.tsx b/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingTimers.test.tsx index d7250ccb4b..7fc37b26db 100644 --- a/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingTimers.test.tsx +++ b/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingTimers.test.tsx @@ -1,5 +1,5 @@ import "./utils/killFinalizationRegistry" -import { act, cleanup, render } from "@testing-library/react" +import { act, render } from "@testing-library/react" import * as mobx from "mobx" import * as React from "react" import { useObserver } from "../src/useObserver" @@ -14,8 +14,6 @@ expect(observerFinalizationRegistry).toBeInstanceOf(TimerBasedFinalizationRegist const registry = observerFinalizationRegistry as TimerBasedFinalizationRegistry -afterEach(cleanup) - test("uncommitted components should not leak observations", async () => { registry.finalizeAllImmediately() diff --git a/packages/mobx-react-lite/__tests__/useAsObservableSource.deprecated.test.tsx b/packages/mobx-react-lite/__tests__/useAsObservableSource.deprecated.test.tsx deleted file mode 100644 index 4f6f62a75d..0000000000 --- a/packages/mobx-react-lite/__tests__/useAsObservableSource.deprecated.test.tsx +++ /dev/null @@ -1,348 +0,0 @@ -import { act, cleanup, render, renderHook } from "@testing-library/react" -import { autorun, configure, observable } from "mobx" -import * as React from "react" -import { useEffect, useState } from "react" - -import { Observer, observer, useAsObservableSource, useLocalStore } from "../src" -import { resetMobx } from "./utils" - -afterEach(cleanup) -afterEach(resetMobx) - -let consoleWarnMock: jest.SpyInstance | undefined -afterEach(() => { - consoleWarnMock?.mockRestore() -}) - -describe("base useAsObservableSource should work", () => { - it("with ", () => { - consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) - let counterRender = 0 - let observerRender = 0 - - function Counter({ multiplier }: { multiplier: number }) { - counterRender++ - const observableProps = useAsObservableSource({ multiplier }) - - const store = useLocalStore(() => ({ - count: 10, - get multiplied() { - return observableProps.multiplier * this.count - }, - inc() { - this.count += 1 - } - })) - - return ( - - {() => { - observerRender++ - return ( -

- Multiplied count: {store.multiplied} - -
- ) - }} -
- ) - } - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - expect(observerRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(1) - expect(observerRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(2) - expect(observerRender).toBe(4) - expect(consoleWarnMock).toMatchSnapshot() - }) - - it("with observer()", () => { - let counterRender = 0 - - const Counter = observer(({ multiplier }: { multiplier: number }) => { - counterRender++ - - const observableProps = useAsObservableSource({ multiplier }) - const store = useLocalStore(() => ({ - count: 10, - get multiplied() { - return observableProps.multiplier * this.count - }, - inc() { - this.count += 1 - } - })) - - return ( -
- Multiplied count: {store.multiplied} - -
- ) - }) - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(4) // One from props, second from updating local observable (setState during render) - }) -}) - -test("useAsObservableSource with effects should work", () => { - const multiplierSeenByEffect: number[] = [] - const valuesSeenByEffect: number[] = [] - const thingsSeenByEffect: Array<[number, number, number]> = [] - - function Counter({ multiplier }: { multiplier: number }) { - const observableProps = useAsObservableSource({ multiplier }) - const store = useLocalStore(() => ({ - count: 10, - get multiplied() { - return observableProps.multiplier * this.count - }, - inc() { - this.count += 1 - } - })) - - useEffect( - () => - autorun(() => { - multiplierSeenByEffect.push(observableProps.multiplier) - }), - [] - ) - useEffect( - () => - autorun(() => { - valuesSeenByEffect.push(store.count) - }), - [] - ) - useEffect( - () => - autorun(() => { - thingsSeenByEffect.push([ - observableProps.multiplier, - store.multiplied, - multiplier - ]) // multiplier is trapped! - }), - [] - ) - - return ( - - ) - } - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - - expect(valuesSeenByEffect).toEqual([10, 11]) - expect(multiplierSeenByEffect).toEqual([1, 2]) - expect(thingsSeenByEffect).toEqual([ - [1, 10, 1], - [1, 11, 1], - [2, 22, 1] - ]) -}) - -describe("combining observer with props and stores", () => { - it("keeps track of observable values", () => { - const TestComponent = observer((props: any) => { - const localStore = useLocalStore(() => ({ - get value() { - return props.store.x + 5 * props.store.y - } - })) - - return
{localStore.value}
- }) - const store = observable({ x: 5, y: 1 }) - const { container } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("10") - act(() => { - store.y = 2 - }) - expect(div.textContent).toBe("15") - act(() => { - store.x = 10 - }) - expect(div.textContent).toBe("20") - }) - - it("allows non-observables to be used if specified as as source", () => { - const renderedValues: number[] = [] - - const TestComponent = observer((props: any) => { - const obsProps = useAsObservableSource({ y: props.y }) - const localStore = useLocalStore(() => ({ - get value() { - return props.store.x + 5 * obsProps.y - } - })) - - renderedValues.push(localStore.value) - return
{localStore.value}
- }) - const store = observable({ x: 5 }) - const { container, rerender } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("10") - rerender() - expect(div.textContent).toBe("15") - act(() => { - store.x = 10 - }) - - expect(renderedValues).toEqual([ - 10, - 15, // props change - 15, // local observable change (setState during render) - 20 - ]) - - // TODO: re-enable this line. When debugging, the correct value is returned from render, - // which is also visible with renderedValues, however, querying the dom doesn't show the correct result - // possible a bug with @testing-library/react? - // expect(container.querySelector("div")!.textContent).toBe("20") // TODO: somehow this change is not visible in the tester! - }) -}) - -describe("enforcing actions", () => { - it("'never' should work", () => { - configure({ enforceActions: "never" }) - const onError = jest.fn() - renderHook( - () => { - const [thing, setThing] = React.useState("world") - useAsObservableSource({ hello: thing }) - useEffect(() => setThing("react"), []) - }, - { - wrapper: class extends React.Component { - componentDidCatch = onError - render() { - return this.props.children - } - } - } - ) - expect(onError).not.toHaveBeenCalled() - }) - it("only when 'observed' should work", () => { - configure({ enforceActions: "observed" }) - const onError = jest.fn() - renderHook( - () => { - const [thing, setThing] = React.useState("world") - useAsObservableSource({ hello: thing }) - useEffect(() => setThing("react"), []) - }, - { - wrapper: class extends React.Component { - componentDidCatch = onError - render() { - return this.props.children - } - } - } - ) - expect(onError).not.toHaveBeenCalled() - }) - it("'always' should work", () => { - configure({ enforceActions: "always" }) - const onError = jest.fn() - renderHook( - () => { - const [thing, setThing] = React.useState("world") - useAsObservableSource({ hello: thing }) - useEffect(() => setThing("react"), []) - }, - { - wrapper: class extends React.Component { - componentDidCatch = onError - render() { - return this.props.children - } - } - } - ) - expect(onError).not.toHaveBeenCalled() - }) -}) diff --git a/packages/mobx-react-lite/__tests__/useAsObservableSource.test.tsx b/packages/mobx-react-lite/__tests__/useAsObservableSource.test.tsx deleted file mode 100644 index 353ed55b08..0000000000 --- a/packages/mobx-react-lite/__tests__/useAsObservableSource.test.tsx +++ /dev/null @@ -1,437 +0,0 @@ -import { act, cleanup, render, renderHook } from "@testing-library/react" -import mockConsole from "jest-mock-console" -import { autorun, configure, observable } from "mobx" -import * as React from "react" -import { useEffect, useState } from "react" - -import { Observer, observer, useLocalObservable } from "../src" -import { resetMobx } from "./utils" -import { useObserver } from "../src/useObserver" - -afterEach(cleanup) -afterEach(resetMobx) - -describe("base useAsObservableSource should work", () => { - it("with useObserver", () => { - let counterRender = 0 - let observerRender = 0 - - function Counter({ multiplier }: { multiplier: number }) { - counterRender++ - const observableProps = useLocalObservable(() => ({ multiplier })) - Object.assign(observableProps, { multiplier }) - const store = useLocalObservable(() => ({ - count: 10, - get multiplied() { - return observableProps.multiplier * this.count - }, - inc() { - this.count += 1 - } - })) - - return useObserver( - () => ( - observerRender++, - ( -
- Multiplied count: {store.multiplied} - -
- ) - ) - ) - } - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - expect(observerRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(2) // 1 would be better! - expect(observerRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(4) // TODO: avoid double rendering here! - expect(observerRender).toBe(4) // TODO: avoid double rendering here! - }) - - it("with ", () => { - let counterRender = 0 - let observerRender = 0 - - function Counter({ multiplier }: { multiplier: number }) { - counterRender++ - const store = useLocalObservable(() => ({ - multiplier, - count: 10, - get multiplied() { - return this.multiplier * this.count - }, - inc() { - this.count += 1 - } - })) - - useEffect(() => { - store.multiplier = multiplier - }, [multiplier]) - return ( - - {() => { - observerRender++ - return ( -
- Multiplied count: {store.multiplied} - -
- ) - }} -
- ) - } - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - expect(observerRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(1) - expect(observerRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(2) - expect(observerRender).toBe(4) - }) - - it("with observer()", () => { - let counterRender = 0 - - const Counter = observer(({ multiplier }: { multiplier: number }) => { - counterRender++ - - const store = useLocalObservable(() => ({ - multiplier, - count: 10, - get multiplied() { - return this.multiplier * this.count - }, - inc() { - this.count += 1 - } - })) - - useEffect(() => { - store.multiplier = multiplier - }, [multiplier]) - - return ( -
- Multiplied count: {store.multiplied} - -
- ) - }) - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(4) // One from props, second from updating local observable with effect - }) -}) - -test("useAsObservableSource with effects should work", () => { - const multiplierSeenByEffect: number[] = [] - const valuesSeenByEffect: number[] = [] - const thingsSeenByEffect: Array<[number, number, number]> = [] - - function Counter({ multiplier }: { multiplier: number }) { - const store = useLocalObservable(() => ({ - multiplier, - count: 10, - get multiplied() { - return this.multiplier * this.count - }, - inc() { - this.count += 1 - } - })) - - useEffect(() => { - store.multiplier = multiplier - }, [multiplier]) - - useEffect( - () => - autorun(() => { - multiplierSeenByEffect.push(store.multiplier) - }), - [] - ) - useEffect( - () => - autorun(() => { - valuesSeenByEffect.push(store.count) - }), - [] - ) - useEffect( - () => - autorun(() => { - thingsSeenByEffect.push([store.multiplier, store.multiplied, multiplier]) // multiplier is trapped! - }), - [] - ) - - return ( - - ) - } - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - - expect(valuesSeenByEffect).toEqual([10, 11]) - expect(multiplierSeenByEffect).toEqual([1, 2]) - expect(thingsSeenByEffect).toEqual([ - [1, 10, 1], - [1, 11, 1], - [2, 22, 1] - ]) -}) - -describe("combining observer with props and stores", () => { - it("keeps track of observable values", () => { - const TestComponent = observer((props: any) => { - const localStore = useLocalObservable(() => ({ - get value() { - return props.store.x + 5 * props.store.y - } - })) - - return
{localStore.value}
- }) - const store = observable({ x: 5, y: 1 }) - const { container } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("10") - act(() => { - store.y = 2 - }) - expect(div.textContent).toBe("15") - act(() => { - store.x = 10 - }) - expect(div.textContent).toBe("20") - }) - - it("allows non-observables to be used if specified as as source", () => { - const renderedValues: number[] = [] - - const TestComponent = observer((props: any) => { - const localStore = useLocalObservable(() => ({ - y: props.y, - get value() { - return props.store.x + 5 * this.y - } - })) - localStore.y = props.y - renderedValues.push(localStore.value) - return
{localStore.value}
- }) - const store = observable({ x: 5 }) - const { container, rerender } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("10") - rerender() - expect(div.textContent).toBe("15") - act(() => { - store.x = 10 - }) - - expect(renderedValues).toEqual([ - 10, - 15, // props change - 15, // local observable change during render (localStore.y = props.y) - 20 - ]) - - expect(container.querySelector("div")!.textContent).toBe("20") - }) -}) - -describe("enforcing actions", () => { - it("'never' should work", () => { - configure({ enforceActions: "never" }) - const onError = jest.fn() - renderHook( - () => { - const [thing, setThing] = React.useState("world") - useLocalObservable(() => ({ hello: thing })) - useEffect(() => setThing("react"), []) - }, - { - wrapper: class extends React.Component { - componentDidCatch = onError - render() { - return this.props.children - } - } - } - ) - expect(onError).not.toHaveBeenCalled() - }) - it("only when 'observed' should work", () => { - configure({ enforceActions: "observed" }) - const onError = jest.fn() - renderHook( - () => { - const [thing, setThing] = React.useState("world") - useLocalObservable(() => ({ hello: thing })) - useEffect(() => setThing("react"), []) - }, - { - wrapper: class extends React.Component { - componentDidCatch = onError - render() { - return this.props.children - } - } - } - ) - expect(onError).not.toHaveBeenCalled() - }) - it("'always' should work", () => { - configure({ enforceActions: "always" }) - const onError = jest.fn() - renderHook( - () => { - const [thing, setThing] = React.useState("world") - useLocalObservable(() => ({ hello: thing })) - useEffect(() => setThing("react"), []) - }, - { - wrapper: class extends React.Component { - componentDidCatch = onError - render() { - return this.props.children - } - } - } - ) - expect(onError).not.toHaveBeenCalled() - }) -}) - -it("doesn't update a component while rendering a different component - #274", () => { - // https://github.com/facebook/react/pull/17099 - - const Parent = observer((props: any) => { - const observableProps = useLocalObservable(() => props) - useEffect(() => { - Object.assign(observableProps, props) - }, [props]) - - return - }) - - const Child = observer(({ observableProps }: any) => { - return observableProps.foo - }) - - const { container, rerender } = render() - expect(container.textContent).toBe("1") - - const restoreConsole = mockConsole() - rerender() - expect(console.error).not.toHaveBeenCalled() - restoreConsole() - expect(container.textContent).toBe("2") -}) diff --git a/packages/mobx-react-lite/__tests__/useLocalObservable.test.tsx b/packages/mobx-react-lite/__tests__/useLocalObservable.test.tsx index 2f8af44dba..0f11b499bf 100644 --- a/packages/mobx-react-lite/__tests__/useLocalObservable.test.tsx +++ b/packages/mobx-react-lite/__tests__/useLocalObservable.test.tsx @@ -1,20 +1,15 @@ import * as mobx from "mobx" import * as React from "react" -import { act, cleanup, fireEvent, render, renderHook } from "@testing-library/react" +import { act, fireEvent, render, renderHook } from "@testing-library/react" import { Observer, observer, useLocalObservable } from "../src" import { useEffect, useState } from "react" import { autorun } from "mobx" import { useObserver } from "../src/useObserver" -afterEach(cleanup) - let consoleWarnMock: jest.SpyInstance | undefined -afterEach(() => { - consoleWarnMock?.mockRestore() -}) -test("base useLocalStore should work", () => { +test("base useLocalObservable should work", () => { let counterRender = 0 let observerRender = 0 let outerStoreRef: any diff --git a/packages/mobx-react-lite/__tests__/useLocalStore.deprecated.test.tsx b/packages/mobx-react-lite/__tests__/useLocalStore.deprecated.test.tsx deleted file mode 100644 index 0971047848..0000000000 --- a/packages/mobx-react-lite/__tests__/useLocalStore.deprecated.test.tsx +++ /dev/null @@ -1,441 +0,0 @@ -import * as React from "react" -import { act, cleanup, fireEvent, render } from "@testing-library/react" - -import { Observer, observer, useLocalStore } from "../src" -import { useEffect, useState } from "react" -import { autorun } from "mobx" - -afterEach(cleanup) - -let consoleWarnMock: jest.SpyInstance | undefined -afterEach(() => { - consoleWarnMock?.mockRestore() -}) - -test("base useLocalStore should work", () => { - consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) - let counterRender = 0 - let observerRender = 0 - let outerStoreRef: any - - function Counter() { - counterRender++ - const store = (outerStoreRef = useLocalStore(() => ({ - count: 0, - count2: 0, // not used in render - inc() { - this.count += 1 - } - }))) - - return ( - - {() => { - observerRender++ - return ( -
- Count: {store.count} - -
- ) - }} -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("0") - expect(counterRender).toBe(1) - expect(observerRender).toBe(1) - - act(() => { - container.querySelector("button")!.click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("1") - expect(counterRender).toBe(1) - expect(observerRender).toBe(2) - - act(() => { - outerStoreRef.count++ - }) - expect(container.querySelector("span")!.innerHTML).toBe("2") - expect(counterRender).toBe(1) - expect(observerRender).toBe(3) - - act(() => { - outerStoreRef.count2++ - }) - // No re-render! - expect(container.querySelector("span")!.innerHTML).toBe("2") - expect(counterRender).toBe(1) - expect(observerRender).toBe(3) - expect(consoleWarnMock).toMatchSnapshot() -}) - -describe("is used to keep observable within component body", () => { - it("value can be changed over renders", () => { - const TestComponent = () => { - const obs = useLocalStore(() => ({ - x: 1, - y: 2 - })) - return ( -
(obs.x += 1)}> - {obs.x}-{obs.y} -
- ) - } - const { container, rerender } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("1-2") - fireEvent.click(div) - // observer not used, need to render from outside - rerender() - expect(div.textContent).toBe("2-2") - }) - - it("works with observer as well", () => { - let renderCount = 0 - - const TestComponent = observer(() => { - renderCount++ - - const obs = useLocalStore(() => ({ - x: 1, - y: 2 - })) - return ( -
(obs.x += 1)}> - {obs.x}-{obs.y} -
- ) - }) - const { container } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("1-2") - fireEvent.click(div) - expect(div.textContent).toBe("2-2") - fireEvent.click(div) - expect(div.textContent).toBe("3-2") - - expect(renderCount).toBe(3) - }) - - it("actions can be used", () => { - const TestComponent = observer(() => { - const obs = useLocalStore(() => ({ - x: 1, - y: 2, - inc() { - obs.x += 1 - } - })) - return ( -
- {obs.x}-{obs.y} -
- ) - }) - const { container } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("1-2") - fireEvent.click(div) - expect(div.textContent).toBe("2-2") - }) - - it("computed properties works as well", () => { - const TestComponent = observer(() => { - const obs = useLocalStore(() => ({ - x: 1, - y: 2, - get z() { - return obs.x + obs.y - } - })) - return
(obs.x += 1)}>{obs.z}
- }) - const { container } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("3") - fireEvent.click(div) - expect(div.textContent).toBe("4") - }) - - it("computed properties can use local functions", () => { - const TestComponent = observer(() => { - const obs = useLocalStore(() => ({ - x: 1, - y: 2, - getMeThatX() { - return this.x - }, - get z() { - return this.getMeThatX() + obs.y - } - })) - return
(obs.x += 1)}>{obs.z}
- }) - const { container } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("3") - fireEvent.click(div) - expect(div.textContent).toBe("4") - }) - - it("transactions are respected", () => { - const seen: number[] = [] - - const TestComponent = observer(() => { - const obs = useLocalStore(() => ({ - x: 1, - inc(delta: number) { - this.x += delta - this.x += delta - } - })) - - useEffect( - () => - autorun(() => { - seen.push(obs.x) - }), - [] - ) - - return ( -
{ - obs.inc(2) - }} - > - Test -
- ) - }) - const { container } = render() - const div = container.querySelector("div")! - fireEvent.click(div) - expect(seen).toEqual([1, 5]) // No 3! - }) - - it("Map can used instead of object", () => { - const TestComponent = observer(() => { - const map = useLocalStore(() => new Map([["initial", 10]])) - return ( -
map.set("later", 20)}> - {Array.from(map).map(([key, value]) => ( -
- {key} - {value} -
- ))} -
- ) - }) - const { container } = render() - const div = container.querySelector("div")! - expect(div.textContent).toBe("initial - 10") - fireEvent.click(div) - expect(div.textContent).toBe("initial - 10later - 20") - }) - - describe("with props", () => { - it("and useObserver", () => { - consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) - let counterRender = 0 - let observerRender = 0 - - function Counter({ multiplier }: { multiplier: number }) { - counterRender++ - - const store = useLocalStore( - props => ({ - count: 10, - get multiplied() { - return props.multiplier * this.count - }, - inc() { - this.count += 1 - } - }), - { multiplier } - ) - - return ( - - {() => ( - observerRender++, - ( -
- Multiplied count: {store.multiplied} - -
- ) - )} -
- ) - } - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - expect(observerRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(1) // or 2 - expect(observerRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(2) - expect(observerRender).toBe(4) - expect(consoleWarnMock).toMatchSnapshot() - }) - - it("with ", () => { - let counterRender = 0 - let observerRender = 0 - - function Counter({ multiplier }: { multiplier: number }) { - counterRender++ - - const store = useLocalStore( - props => ({ - count: 10, - get multiplied() { - return props.multiplier * this.count - }, - inc() { - this.count += 1 - } - }), - { multiplier } - ) - - return ( - - {() => { - observerRender++ - return ( -
- Multiplied count: {store.multiplied} - -
- ) - }} -
- ) - } - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - expect(observerRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(1) - expect(observerRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(2) - expect(observerRender).toBe(4) - }) - - it("with observer()", () => { - let counterRender = 0 - - const Counter = observer(({ multiplier }: { multiplier: number }) => { - counterRender++ - - const store = useLocalStore( - props => ({ - count: 10, - get multiplied() { - return props.multiplier * this.count - }, - inc() { - this.count += 1 - } - }), - { multiplier } - ) - - return ( -
- Multiplied count: {store.multiplied} - -
- ) - }) - - function Parent() { - const [multiplier, setMultiplier] = useState(1) - - return ( -
- -
- ) - } - - const { container } = render() - - expect(container.querySelector("span")!.innerHTML).toBe("10") - expect(counterRender).toBe(1) - - act(() => { - ;(container.querySelector("#inc")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("11") - expect(counterRender).toBe(2) - - act(() => { - ;(container.querySelector("#incmultiplier")! as any).click() - }) - expect(container.querySelector("span")!.innerHTML).toBe("22") - expect(counterRender).toBe(4) // One from props, second from updating source (setState during render) - }) - }) -}) diff --git a/packages/mobx-react-lite/__tests__/utils.ts b/packages/mobx-react-lite/__tests__/utils.ts deleted file mode 100644 index d15906b4ba..0000000000 --- a/packages/mobx-react-lite/__tests__/utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { configure } from "mobx" - -export function resetMobx(): void { - configure({ enforceActions: "never" }) -} - -export function enableDevEnvironment() { - process.env.NODE_ENV === "development" - return function () { - process.env.NODE_ENV === "production" - } -} - -export function sleep(time: number) { - return new Promise(res => { - setTimeout(res, time) - }) -} diff --git a/packages/mobx-react-lite/batchingForReactDom.js b/packages/mobx-react-lite/batchingForReactDom.js deleted file mode 100644 index c5193da50b..0000000000 --- a/packages/mobx-react-lite/batchingForReactDom.js +++ /dev/null @@ -1,3 +0,0 @@ -if ("production" !== process.env.NODE_ENV) { - console.warn("[mobx-react-lite] importing batchingForReactDom is no longer needed") -} diff --git a/packages/mobx-react-lite/batchingForReactNative.js b/packages/mobx-react-lite/batchingForReactNative.js deleted file mode 100644 index fbfe34dad5..0000000000 --- a/packages/mobx-react-lite/batchingForReactNative.js +++ /dev/null @@ -1,3 +0,0 @@ -if ("production" !== process.env.NODE_ENV) { - console.warn("[mobx-react-lite] importing batchingForReactNative is no longer needed") -} diff --git a/packages/mobx-react-lite/batchingOptOut.js b/packages/mobx-react-lite/batchingOptOut.js deleted file mode 100644 index 8b6e963c56..0000000000 --- a/packages/mobx-react-lite/batchingOptOut.js +++ /dev/null @@ -1,3 +0,0 @@ -if ("production" !== process.env.NODE_ENV) { - console.warn("[mobx-react-lite] importing batchingOptOut is no longer needed") -} diff --git a/packages/mobx-react-lite/jest.setup.ts b/packages/mobx-react-lite/jest.setup.ts index c02f52ea96..4a2d3994c5 100644 --- a/packages/mobx-react-lite/jest.setup.ts +++ b/packages/mobx-react-lite/jest.setup.ts @@ -1,9 +1,31 @@ import "@testing-library/jest-dom/extend-expect" -import { configure } from "mobx" +import { cleanup } from "@testing-library/react" +import { configure, _getGlobalState, _resetGlobalState } from "mobx" global.setImmediate = global.setImmediate || ((fn, ...args) => global.setTimeout(fn, 0, ...args)) -configure({ enforceActions: "never" }) +function resetMobxReactTestState() { + _resetGlobalState() + _getGlobalState().spyListeners = [] + configure({ + enforceActions: "never", + computedRequiresReaction: false, + reactionRequiresObservable: false, + observableRequiresReaction: false, + disableErrorBoundaries: false, + safeDescriptors: true + }) +} -// @ts-ignore -global.__DEV__ = true +beforeEach(() => { + // @ts-ignore + global.__DEV__ = true + resetMobxReactTestState() +}) + +afterEach(() => { + cleanup() + jest.useRealTimers() + jest.restoreAllMocks() + resetMobxReactTestState() +}) diff --git a/packages/mobx-react-lite/package.json b/packages/mobx-react-lite/package.json index 359e17829c..8df75e7d44 100644 --- a/packages/mobx-react-lite/package.json +++ b/packages/mobx-react-lite/package.json @@ -1,26 +1,43 @@ { "name": "mobx-react-lite", "version": "4.1.1", - "description": "Lightweight React bindings for MobX based on React 16.8+ and Hooks", + "description": "Lightweight React bindings for MobX based on function components and Hooks", "source": "src/index.ts", + "type": "commonjs", "main": "dist/index.js", - "umd:main": "dist/mobxreact.umd.production.min.js", + "umd:main": "dist/mobxreactlite.umd.production.min.js", "unpkg": "dist/mobxreactlite.umd.production.min.js", "jsdelivr": "dist/mobxreactlite.umd.production.min.js", "jsnext:main": "dist/mobxreactlite.esm.production.min.js", - "module": "es/index.js", - "react-native": "es/index.js", + "module": "dist/mobxreactlite.esm.js", + "react-native": "dist/mobxreactlite.esm.js", "types": "dist/index.d.ts", "typings": "dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "react-native": { + "types": "./dist/index.d.ts", + "default": "./dist/mobxreactlite.esm.js" + }, + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/mobxreactlite.mjs" + }, + "default": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "./dist/*": "./dist/*", + "./src/*": "./src/*" + }, "files": [ "src", - "dist/", - "lib/", - "es/", + "dist", "LICENSE", "CHANGELOG.md", - "README.md", - "batching*" + "README.md" ], "repository": { "type": "git", @@ -36,24 +53,12 @@ "url": "https://github.com/mobxjs/mobx/issues" }, "homepage": "https://mobx.js.org", - "dependencies": { - "use-sync-external-store": "^1.4.0" - }, "peerDependencies": { - "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "mobx": "^7.0.0", + "react": "^18 || ^19" }, "devDependencies": { - "mobx": "^6.15.0", - "expose-gc": "^1.0.0" + "mobx": "^6.15.0" }, "keywords": [ "mobx", @@ -68,14 +73,12 @@ ], "scripts": { "lint": "eslint src/**/* --ext .js,.ts,.tsx", - "build": "node ../../scripts/build.js mobxReactLite", - "build:test": "npm run build -- --target test", - "build:cjs": "tsc --project tsconfig.build.cjs.json", - "build:es": "tsc --project tsconfig.build.es.json", + "build": "rollup --config rollup.config.mjs", + "build:test": "npm run build -- --environment TARGET:test", "test": "jest", "test:size": "import-size --report . observer useLocalObservable", "test:types": "tsc --noEmit", "test:check": "npm run test:types", - "prepublishOnly": "cd ../mobx && npm run build -- --target publish && cd ../mobx-react-lite && npm run build -- --target publish && npm run build:cjs && npm run build:es" + "prepublishOnly": "npm run build -- --environment TARGET:publish" } } diff --git a/packages/mobx-react-lite/rollup.config.mjs b/packages/mobx-react-lite/rollup.config.mjs new file mode 100644 index 0000000000..91446e47e1 --- /dev/null +++ b/packages/mobx-react-lite/rollup.config.mjs @@ -0,0 +1,11 @@ +import createRollupConfig from "../../scripts/create-rollup-config.mjs" + +export default createRollupConfig({ + packageName: "mobxReactLite", + packageBase: "mobxreactlite", + input: "src/index.ts", + globals: { + react: "React", + mobx: "mobx" + } +}) diff --git a/packages/mobx-react-lite/src/ObserverComponent.ts b/packages/mobx-react-lite/src/ObserverComponent.ts index 59b9ff7105..09778c8f2e 100644 --- a/packages/mobx-react-lite/src/ObserverComponent.ts +++ b/packages/mobx-react-lite/src/ObserverComponent.ts @@ -1,11 +1,10 @@ import { useObserver } from "./useObserver" +import type { ReactNode } from "react" -// TODO: this type could be improved in the next major release: -// type IObserverProps = { children: () => React.ReactNode, render?: never } | { children?: never, render: () => React.ReactNode } -interface IObserverProps { - children?(): React.ReactElement | null - render?(): React.ReactElement | null -} +type IObserverProps = + | { children: () => ReactNode; render?: never } + | { children?: never; render: () => ReactNode } + | { children?: never; render?: never } function ObserverComponent({ children, render }: IObserverProps) { if (children && render) { @@ -19,43 +18,6 @@ function ObserverComponent({ children, render }: IObserverProps) { } return useObserver(component) } -if ("production" !== process.env.NODE_ENV) { - ObserverComponent.propTypes = { - children: ObserverPropsCheck, - render: ObserverPropsCheck - } -} ObserverComponent.displayName = "Observer" export { ObserverComponent as Observer } - -function ObserverPropsCheck( - props: { [k: string]: any }, - key: string, - componentName: string, - location: any, - propFullName: string -) { - const extraKey = key === "children" ? "render" : "children" - const hasProp = typeof props[key] === "function" - const hasExtraProp = typeof props[extraKey] === "function" - if (hasProp && hasExtraProp) { - return new Error( - "MobX Observer: Do not use children and render in the same time in`" + componentName - ) - } - - if (hasProp || hasExtraProp) { - return null - } - return new Error( - "Invalid prop `" + - propFullName + - "` of type `" + - typeof props[key] + - "` supplied to" + - " `" + - componentName + - "`, expected `function`." - ) -} diff --git a/packages/mobx-react-lite/src/index.ts b/packages/mobx-react-lite/src/index.ts index f80826ac87..0ffe271ed6 100644 --- a/packages/mobx-react-lite/src/index.ts +++ b/packages/mobx-react-lite/src/index.ts @@ -1,40 +1,11 @@ import "./utils/assertEnvironment" -import { unstable_batchedUpdates as batch } from "./utils/reactBatchedUpdates" -import { observerBatching } from "./utils/observerBatching" -import { useDeprecated } from "./utils/utils" -import { useObserver as useObserverOriginal } from "./useObserver" -import { enableStaticRendering } from "./staticRendering" import { observerFinalizationRegistry } from "./utils/observerFinalizationRegistry" -observerBatching(batch) - export { isUsingStaticRendering, enableStaticRendering } from "./staticRendering" -export { observer, IObserverOptions } from "./observer" +export { observer } from "./observer" export { Observer } from "./ObserverComponent" export { useLocalObservable } from "./useLocalObservable" -export { useLocalStore } from "./useLocalStore" -export { useAsObservableSource } from "./useAsObservableSource" export { observerFinalizationRegistry as _observerFinalizationRegistry } export const clearTimers = observerFinalizationRegistry["finalizeAllImmediately"] ?? (() => {}) - -export function useObserver(fn: () => T, baseComponentName: string = "observed"): T { - if ("production" !== process.env.NODE_ENV) { - useDeprecated( - "[mobx-react-lite] 'useObserver(fn)' is deprecated. Use `{fn}` instead, or wrap the entire component in `observer`." - ) - } - return useObserverOriginal(fn, baseComponentName) -} - -export { isObserverBatched, observerBatching } from "./utils/observerBatching" - -export function useStaticRendering(enable: boolean) { - if ("production" !== process.env.NODE_ENV) { - console.warn( - "[mobx-react-lite] 'useStaticRendering' is deprecated, use 'enableStaticRendering' instead" - ) - } - enableStaticRendering(enable) -} diff --git a/packages/mobx-react-lite/src/observer.ts b/packages/mobx-react-lite/src/observer.ts index 6d9948d637..c43931d6c9 100644 --- a/packages/mobx-react-lite/src/observer.ts +++ b/packages/mobx-react-lite/src/observer.ts @@ -3,9 +3,6 @@ import { forwardRef, memo } from "react" import { isUsingStaticRendering } from "./staticRendering" import { useObserver } from "./useObserver" -let warnObserverOptionsDeprecated = true -let warnLegacyContextTypes = true - const hasSymbol = typeof Symbol === "function" && Symbol.for const isFunctionNameConfigurable = Object.getOwnPropertyDescriptor(() => {}, "name")?.configurable ?? false @@ -19,35 +16,13 @@ const ReactMemoSymbol = hasSymbol ? Symbol.for("react.memo") : typeof memo === "function" && memo((props: any) => null)["$$typeof"] -/** - * @deprecated Observer options will be removed in the next major version of mobx-react-lite. - * Look at the individual properties for alternatives. - */ -export interface IObserverOptions { - /** - * @deprecated Pass a `React.forwardRef` component to observer instead of using the options object - * e.g. `observer(React.forwardRef(fn))` - */ - readonly forwardRef?: boolean -} - -export function observer

( - baseComponent: React.ForwardRefRenderFunction, - options: IObserverOptions & { - /** - * @deprecated Pass a `React.forwardRef` component to observer instead of using the options object - * e.g. `observer(React.forwardRef(fn))` - */ - forwardRef: true - } -): React.MemoExoticComponent< - React.ForwardRefExoticComponent & React.RefAttributes> -> +export function observer>( + baseComponent: C +): C & React.MemoExoticComponent export function observer

( - baseComponent: React.FunctionComponent

, - options?: IObserverOptions -): React.FunctionComponent

+ baseComponent: React.FunctionComponent

+): React.FunctionComponent

& React.MemoExoticComponent> export function observer

( baseComponent: React.ForwardRefExoticComponent< @@ -56,39 +31,14 @@ export function observer

( ): React.MemoExoticComponent< React.ForwardRefExoticComponent & React.RefAttributes> > -export function observer< - C extends React.FunctionComponent | React.ForwardRefRenderFunction, - Options extends IObserverOptions ->( - baseComponent: C, - options?: Options -): Options extends { forwardRef: true } - ? C extends React.ForwardRefRenderFunction - ? C & - React.MemoExoticComponent< - React.ForwardRefExoticComponent< - React.PropsWithoutRef

& React.RefAttributes - > - > - : never /* forwardRef set for a non forwarding component */ - : C & { displayName: string } // n.b. base case is not used for actual typings or exported in the typing files export function observer

( baseComponent: | React.ForwardRefRenderFunction | React.FunctionComponent

- | React.ForwardRefExoticComponent & React.RefAttributes>, - // TODO remove in next major - options?: IObserverOptions + | React.ForwardRefExoticComponent & React.RefAttributes> ) { - if (process.env.NODE_ENV !== "production" && warnObserverOptionsDeprecated && options) { - warnObserverOptionsDeprecated = false - console.warn( - `[mobx-react-lite] \`observer(fn, { forwardRef: true })\` is deprecated, use \`observer(React.forwardRef(fn))\`` - ) - } - if (ReactMemoSymbol && baseComponent["$$typeof"] === ReactMemoSymbol) { throw new Error( `[mobx-react-lite] You are trying to use \`observer\` on a function component wrapped in either another \`observer\` or \`React.memo\`. The observer already applies 'React.memo' for you.` @@ -100,7 +50,7 @@ export function observer

( return baseComponent } - let useForwardRef = options?.forwardRef ?? false + let useForwardRef = false let render = baseComponent const baseComponentName = baseComponent.displayName || baseComponent.name @@ -132,20 +82,6 @@ export function observer

( }) } - // Support legacy context: `contextTypes` must be applied before `memo` - if ((baseComponent as any).contextTypes) { - ;(observerComponent as React.FunctionComponent).contextTypes = ( - baseComponent as any - ).contextTypes - - if (process.env.NODE_ENV !== "production" && warnLegacyContextTypes) { - warnLegacyContextTypes = false - console.warn( - `[mobx-react-lite] Support for Legacy Context in function components will be removed in the next major release.` - ) - } - } - if (useForwardRef) { // `forwardRef` must be applied prior `memo` // `forwardRef(observer(cmp))` throws: @@ -160,18 +96,6 @@ export function observer

( copyStaticProperties(baseComponent, observerComponent) - if ("production" !== process.env.NODE_ENV) { - Object.defineProperty(observerComponent, "contextTypes", { - set() { - throw new Error( - `[mobx-react-lite] \`${ - this.displayName || this.type?.displayName || this.type?.name || "Component" - }.contextTypes\` must be set before applying \`observer\`.` - ) - } - }) - } - return observerComponent } diff --git a/packages/mobx-react-lite/src/useAsObservableSource.ts b/packages/mobx-react-lite/src/useAsObservableSource.ts deleted file mode 100644 index ffda077f68..0000000000 --- a/packages/mobx-react-lite/src/useAsObservableSource.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useDeprecated } from "./utils/utils" -import { observable, runInAction } from "mobx" -import { useState } from "react" - -export function useAsObservableSource(current: TSource): TSource { - if ("production" !== process.env.NODE_ENV) - useDeprecated( - "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples." - ) - // We're deliberately not using idiomatic destructuring for the hook here. - // Accessing the state value as an array element prevents TypeScript from generating unnecessary helpers in the resulting code. - // For further details, please refer to mobxjs/mobx#3842. - const res = useState(() => observable(current, {}, { deep: false }))[0] - runInAction(() => { - Object.assign(res, current) - }) - return res -} diff --git a/packages/mobx-react-lite/src/useLocalObservable.ts b/packages/mobx-react-lite/src/useLocalObservable.ts index fde49eac61..665d3d2b25 100644 --- a/packages/mobx-react-lite/src/useLocalObservable.ts +++ b/packages/mobx-react-lite/src/useLocalObservable.ts @@ -1,7 +1,7 @@ import { observable, AnnotationsMap } from "mobx" import { useState } from "react" -export function useLocalObservable>( +export function useLocalObservable( initializer: () => TStore, annotations?: AnnotationsMap ): TStore { diff --git a/packages/mobx-react-lite/src/useLocalStore.ts b/packages/mobx-react-lite/src/useLocalStore.ts deleted file mode 100644 index 9060ee1110..0000000000 --- a/packages/mobx-react-lite/src/useLocalStore.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { observable } from "mobx" -import { useState } from "react" - -import { useDeprecated } from "./utils/utils" -import { useAsObservableSource } from "./useAsObservableSource" - -export function useLocalStore>(initializer: () => TStore): TStore -export function useLocalStore, TSource extends object>( - initializer: (source: TSource) => TStore, - current: TSource -): TStore -export function useLocalStore, TSource extends object>( - initializer: (source?: TSource) => TStore, - current?: TSource -): TStore { - if ("production" !== process.env.NODE_ENV) { - useDeprecated( - "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead." - ) - } - const source = current && useAsObservableSource(current) - return useState(() => observable(initializer(source), undefined, { autoBind: true }))[0] -} diff --git a/packages/mobx-react-lite/src/useObserver.ts b/packages/mobx-react-lite/src/useObserver.ts index 886073467f..08da5b53fe 100644 --- a/packages/mobx-react-lite/src/useObserver.ts +++ b/packages/mobx-react-lite/src/useObserver.ts @@ -1,9 +1,7 @@ -import { Reaction } from "mobx" +import { getDependencyTree, Reaction } from "mobx" import React from "react" -import { printDebugValue } from "./utils/printDebugValue" import { isUsingStaticRendering } from "./staticRendering" import { observerFinalizationRegistry } from "./utils/observerFinalizationRegistry" -import { useSyncExternalStore } from "use-sync-external-store/shim" // Do not store `admRef` (even as part of a closure!) on this object, // otherwise it will prevent GC and therefore reaction disposal via FinalizationRegistry. @@ -89,9 +87,9 @@ export function useObserver(render: () => T, baseComponentName: string = "obs observerFinalizationRegistry.register(admRef, adm, adm) } - React.useDebugValue(adm.reaction!, printDebugValue) + React.useDebugValue(adm.reaction!, getDependencyTree) - useSyncExternalStore( + React.useSyncExternalStore( // Both of these must be stable, otherwise it would keep resubscribing every render. adm.subscribe, adm.getSnapshot, diff --git a/packages/mobx-react-lite/src/utils/assertEnvironment.ts b/packages/mobx-react-lite/src/utils/assertEnvironment.ts index 339dfb29d2..4fa7aad31d 100644 --- a/packages/mobx-react-lite/src/utils/assertEnvironment.ts +++ b/packages/mobx-react-lite/src/utils/assertEnvironment.ts @@ -1,9 +1,9 @@ -import { makeObservable } from "mobx" -import { useState } from "react" +import { _getGlobalState } from "mobx" +import { useState, useSyncExternalStore } from "react" -if (!useState) { - throw new Error("mobx-react-lite requires React with Hooks support") +if (!useState || !useSyncExternalStore) { + throw new Error("mobx-react-lite requires React 18 or later") } -if (!makeObservable) { - throw new Error("mobx-react-lite@3 requires mobx at least version 6 to be available") +if (!(_getGlobalState?.()?.version >= 7)) { + throw new Error("mobx-react-lite requires mobx at least version 7 to be available") } diff --git a/packages/mobx-react-lite/src/utils/observerBatching.ts b/packages/mobx-react-lite/src/utils/observerBatching.ts deleted file mode 100644 index 42ce876251..0000000000 --- a/packages/mobx-react-lite/src/utils/observerBatching.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { configure } from "mobx" - -export function defaultNoopBatch(callback: () => void) { - callback() -} - -export function observerBatching(reactionScheduler: any) { - if (!reactionScheduler) { - reactionScheduler = defaultNoopBatch - if ("production" !== process.env.NODE_ENV) { - console.warn( - "[MobX] Failed to get unstable_batched updates from react-dom / react-native" - ) - } - } - configure({ reactionScheduler }) -} - -export const isObserverBatched = () => { - if ("production" !== process.env.NODE_ENV) { - console.warn("[MobX] Deprecated") - } - - return true -} diff --git a/packages/mobx-react-lite/src/utils/printDebugValue.ts b/packages/mobx-react-lite/src/utils/printDebugValue.ts deleted file mode 100644 index 8ef487fd64..0000000000 --- a/packages/mobx-react-lite/src/utils/printDebugValue.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { getDependencyTree, Reaction } from "mobx" - -export function printDebugValue(v: Reaction) { - return getDependencyTree(v) -} diff --git a/packages/mobx-react-lite/src/utils/reactBatchedUpdates.native.ts b/packages/mobx-react-lite/src/utils/reactBatchedUpdates.native.ts deleted file mode 100644 index a8e25fbcf7..0000000000 --- a/packages/mobx-react-lite/src/utils/reactBatchedUpdates.native.ts +++ /dev/null @@ -1,2 +0,0 @@ -// @ts-ignore -export { unstable_batchedUpdates } from "react-native" diff --git a/packages/mobx-react-lite/src/utils/reactBatchedUpdates.ts b/packages/mobx-react-lite/src/utils/reactBatchedUpdates.ts deleted file mode 100644 index 8c64462b07..0000000000 --- a/packages/mobx-react-lite/src/utils/reactBatchedUpdates.ts +++ /dev/null @@ -1 +0,0 @@ -export { unstable_batchedUpdates } from "react-dom" diff --git a/packages/mobx-react-lite/tsconfig.build.cjs.json b/packages/mobx-react-lite/tsconfig.build.cjs.json deleted file mode 100644 index 1bceb718ed..0000000000 --- a/packages/mobx-react-lite/tsconfig.build.cjs.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.build.json", - "compilerOptions": { - "outDir": "lib", - "module": "CommonJS" - } -} diff --git a/packages/mobx-react-lite/tsconfig.build.es.json b/packages/mobx-react-lite/tsconfig.build.es.json deleted file mode 100644 index 1561876960..0000000000 --- a/packages/mobx-react-lite/tsconfig.build.es.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.build.json", - "compilerOptions": { - "outDir": "es", - "module": "ESNext" - } -} diff --git a/packages/mobx-react-lite/tsconfig.build.json b/packages/mobx-react-lite/tsconfig.build.json deleted file mode 100644 index 5687524039..0000000000 --- a/packages/mobx-react-lite/tsconfig.build.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "esModuleInterop": true, - "target": "ES5", - "noEmit": false, - "declaration": false - } -} diff --git a/packages/mobx-react-lite/tsdx.config.js b/packages/mobx-react-lite/tsdx.config.js deleted file mode 100644 index f2cf8dd75a..0000000000 --- a/packages/mobx-react-lite/tsdx.config.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - rollup(config) { - return { - ...config, - output: { - ...config.output, - globals: { - react: "React", - mobx: "mobx", - "react-dom": "ReactDOM" - } - } - } - } -} diff --git a/packages/mobx-react/README.md b/packages/mobx-react/README.md index 4bdf704af9..387fbe90ff 100644 --- a/packages/mobx-react/README.md +++ b/packages/mobx-react/README.md @@ -17,21 +17,20 @@ Only the latest version is actively maintained. If you're missing a fix or a fea | NPM Version | Support MobX version | Supported React versions | Added support for: | | ----------- | -------------------- | ------------------------ | -------------------------------------------------------------------------------- | +| v10 | 7.\* | >=18 | MobX 7, Hooks, React 18 strict mode | | v9 | 6.\* | >16.8 | Hooks, React 18.2 in strict mode | | v7 | 6.\* | >16.8 < 18.2 | Hooks | | v6 | 4.\* / 5.\* | >16.8 <17 | Hooks | | v5 | 4.\* / 5.\* | >0.13 <17 | No, but it is possible to use `` sections inside hook based components | -mobx-react 6 / 7 is a repackage of the smaller [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite) package + following features from the `mobx-react@5` package added: +`mobx-react` is a wrapper around `mobx-react-lite` for applications that also need class component support: +- Support for function components through `mobx-react-lite` - Support for class based components for `observer` and `@observer` -- `Provider / inject` to pass stores around (but consider to use `React.createContext` instead) -- `PropTypes` to describe observable based property checkers (but consider to use TypeScript instead) -- The `disposeOnUnmount` utility / decorator to easily clean up resources such as reactions created in your class based components. ## Installation -`npm install mobx-react --save` +`npm install mobx-react` Or CDN: https://unpkg.com/mobx-react (UMD namespace: `mobxReact`) @@ -42,7 +41,7 @@ import { observer } from "mobx-react" This package provides the bindings for MobX and React. See the [official documentation](https://mobx.js.org/react-integration.html) for how to get started. -For greenfield projects you might want to consider to use [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite), if you intend to only use function based components. `React.createContext` can be used to pass stores around. +Use `React.createContext` to pass stores around. ## API documentation @@ -226,7 +225,7 @@ person.name = "Mike" // will cause the Observer region to re-render Local observable state can be introduced by using the `useLocalObservable` hook, that runs once to create an observable store. A quick example would be: ```javascript -import { useLocalObservable, Observer } from "mobx-react-lite" +import { useLocalObservable, Observer } from "mobx-react" const Todo = () => { const todo = useLocalObservable(() => ({ @@ -241,7 +240,7 @@ const Todo = () => { {() => (

- {todo.title} {todo.done ? "[DONE]" : "[TODO]"} + {todo.title} {todo.done ? "[DONE]" : "[OPEN]"}

)}
@@ -253,7 +252,7 @@ When using `useLocalObservable`, all properties of the returned object will be m It is important to realize that the store is created only once! It is not possible to specify dependencies to force re-creation, _nor should you directly be referring to props for the initializer function_, as changes in those won't propagate. -Instead, if your store needs to refer to props (or `useState` based local state), the `useLocalObservable` should be combined with the `useAsObservableSource` hook, see below. +Instead, if your store needs to refer to props (or `useState` based local state), sync those values into the store with `useEffect`. Note that in many cases it is possible to extract the initializer function to a function outside the component definition. Which makes it possible to test the store itself in a more straight-forward manner, and avoids creating the initializer closure on each re-render. @@ -287,261 +286,6 @@ Decorators are currently a stage-2 ESNext feature. How to enable them is documen See this [thread](https://www.reddit.com/r/reactjs/comments/4vnxg5/free_eggheadio_course_learn_mobx_react_in_30/d61oh0l). TL;DR: the conceptual distinction makes a lot of sense when using MobX as well, but use `observer` on all components. -### `PropTypes` - -MobX-react provides the following additional `PropTypes` which can be used to validate against MobX structures: - -- `observableArray` -- `observableArrayOf(React.PropTypes.number)` -- `observableMap` -- `observableObject` -- `arrayOrObservableArray` -- `arrayOrObservableArrayOf(React.PropTypes.number)` -- `objectOrObservableObject` - -Use `import { PropTypes } from "mobx-react"` to import them, then use for example `PropTypes.observableArray` - -### `Provider` and `inject` - -_Note: usually there is no need anymore to use `Provider` / `inject` in new code bases; most of its features are now covered by `React.createContext`._ - -`Provider` is a component that can pass stores (or other stuff) using React's context mechanism to child components. -This is useful if you have things that you don't want to pass through multiple layers of components explicitly. - -`inject` can be used to pick up those stores. It is a higher order component that takes a list of strings and makes those stores available to the wrapped component. - -Example (based on the official [context docs](https://facebook.github.io/react/docs/context.html#passing-info-automatically-through-a-tree)): - -```javascript -@inject("color") -@observer -class Button extends React.Component { - render() { - return - } -} - -class Message extends React.Component { - render() { - return ( -
- {this.props.text} -
- ) - } -} - -class MessageList extends React.Component { - render() { - const children = this.props.messages.map(message => ) - return ( - -
{children}
-
- ) - } -} -``` - -Notes: - -- It is possible to read the stores provided by `Provider` using `React.useContext`, by using the `MobXProviderContext` context that can be imported from `mobx-react`. -- If a component asks for a store and receives a store via a property with the same name, the property takes precedence. Use this to your advantage when testing! -- When using both `@inject` and `@observer`, make sure to apply them in the correct order: `observer` should be the inner decorator, `inject` the outer. There might be additional decorators in between. -- The original component wrapped by `inject` is available as the `wrappedComponent` property of the created higher order component. - -#### "The set of provided stores has changed" error - -Values provided through `Provider` should be final. Make sure that if you put things in `context` that might change over time, that they are `@observable` or provide some other means to listen to changes, like callbacks. However, if your stores will change over time, like an observable value of another store, MobX will throw an error. -This restriction exists mainly for legacy reasons. If you have a scenario where you need to modify the set of stores, please leave a comment about it in this issue https://github.com/mobxjs/mobx-react/issues/745. Or a preferred way is to [use React Context](https://reactjs.org/docs/context.html) directly which does not have this restriction. - -#### Inject as function - -The above example in ES5 would start like: - -```javascript -var Button = inject("color")( - observer( - class Button extends Component { - /* ... etc ... */ - } - ) -) -``` - -A functional stateless component would look like: - -```javascript -var Button = inject("color")( - observer(({ color }) => { - /* ... etc ... */ - }) -) -``` - -#### Customizing inject - -Instead of passing a list of store names, it is also possible to create a custom mapper function and pass it to inject. -The mapper function receives all stores as argument, the properties with which the components are invoked and the context, and should produce a new set of properties, -that are mapped into the original: - -`mapperFunction: (allStores, props, context) => additionalProps` - -Since version 4.0 the `mapperFunction` itself is tracked as well, so it is possible to do things like: - -```javascript -const NameDisplayer = ({ name }) =>

{name}

- -const UserNameDisplayer = inject(stores => ({ - name: stores.userStore.name -}))(NameDisplayer) - -const user = mobx.observable({ - name: "Noa" -}) - -const App = () => ( - - - -) - -ReactDOM.render(, document.body) -``` - -_N.B. note that in this *specific* case neither `NameDisplayer` nor `UserNameDisplayer` needs to be decorated with `observer`, since the observable dereferencing is done in the mapper function_ - -#### Using `PropTypes` and `defaultProps` and other static properties in combination with `inject` - -Inject wraps a new component around the component you pass into it. -This means that assigning a static property to the resulting component, will be applied to the HoC, and not to the original component. -So if you take the following example: - -```javascript -const UserName = inject("userStore")(({ userStore, bold }) => someRendering()) - -UserName.propTypes = { - bold: PropTypes.boolean.isRequired, - userStore: PropTypes.object.isRequired // will always fail -} -``` - -The above propTypes are incorrect, `bold` needs to be provided by the caller of the `UserName` component and is checked by React. -However, `userStore` does not need to be required! Although it is required for the original stateless function component, it is not -required for the resulting inject component. After all, the whole point of that component is to provide that `userStore` itself. - -So if you want to make assertions on the data that is being injected (either stores or data resulting from a mapper function), the propTypes -should be defined on the _wrapped_ component. Which is available through the static property `wrappedComponent` on the inject component: - -```javascript -const UserName = inject("userStore")(({ userStore, bold }) => someRendering()) - -UserName.propTypes = { - bold: PropTypes.boolean.isRequired // could be defined either here ... -} - -UserName.wrappedComponent.propTypes = { - // ... or here - userStore: PropTypes.object.isRequired // correct -} -``` - -The same principle applies to `defaultProps` and other static React properties. -Note that it is not allowed to redefine `contextTypes` on `inject` components (but is possible to define it on `wrappedComponent`) - -Finally, mobx-react will automatically move non React related static properties from wrappedComponent to the inject component so that all static fields are -actually available to the outside world without needing `.wrappedComponent`. - -#### Strongly typing inject - -##### With TypeScript - -`inject` also accepts a function (`(allStores, nextProps, nextContext) => additionalProps`) that can be used to pick all the desired stores from the available stores like this. -The `additionalProps` will be merged into the original `nextProps` before being provided to the next component. - -```typescript -import { IUserStore } from "myStore" - -@inject(allStores => ({ - userStore: allStores.userStore as IUserStore -})) -class MyComponent extends React.Component<{ userStore?: IUserStore; otherProp: number }, {}> { - /* etc */ -} -``` - -Make sure to mark `userStore` as an optional property. It should not (necessarily) be passed in by parent components at all! - -Note: If you have strict null checking enabled, you could muffle the nullable type by using the `!` operator: - -``` -public render() { - const {a, b} = this.store! - // ... -} -``` - -#### Testing store injection - -It is allowed to pass any declared store in directly as a property as well. This makes it easy to set up individual component tests without a provider. - -So if you have in your app something like: - -```javascript - - - -``` - -In your test you can easily test the `Person` component by passing the necessary store as prop directly: - -``` -const profile = new Profile() -const mountedComponent = mount( - -) -``` - -Bear in mind that using shallow rendering won't provide any useful results when testing injected components; only the injector will be rendered. -To test with shallow rendering, instantiate the `wrappedComponent` instead: `shallow()` - -### disposeOnUnmount(componentInstance, propertyKey | function | function[]) - -Function (and decorator) that makes sure a function (usually a disposer such as the ones returned by `reaction`, `autorun`, etc.) is automatically executed as part of the componentWillUnmount lifecycle event. - -```javascript -import { disposeOnUnmount } from "mobx-react" - -class SomeComponent extends React.Component { - // decorator version - @disposeOnUnmount - someReactionDisposer = reaction(...) - - // decorator version with arrays - @disposeOnUnmount - someReactionDisposers = [ - reaction(...), - reaction(...) - ] - - - // function version over properties - someReactionDisposer = disposeOnUnmount(this, reaction(...)) - - // function version inside methods - componentDidMount() { - // single function - disposeOnUnmount(this, reaction(...)) - - // or function array - disposeOnUnmount(this, [ - reaction(...), - reaction(...) - ]) - } -} -``` - ## DevTools `mobx-react@6` and higher are no longer compatible with the mobx-react-devtools. diff --git a/packages/mobx-react/__tests__/ObserverComponent.test.tsx b/packages/mobx-react/__tests__/ObserverComponent.test.tsx new file mode 100644 index 0000000000..0f80fe8321 --- /dev/null +++ b/packages/mobx-react/__tests__/ObserverComponent.test.tsx @@ -0,0 +1,42 @@ +import mockConsole from "jest-mock-console" +import * as mobx from "mobx" +import * as React from "react" +import { act, render } from "@testing-library/react" + +import { Observer } from "../src" + +describe("regions should rerender component", () => { + const execute = () => { + const data = mobx.observable.box("hi") + const Comp = () => ( +
+ {() => {data.get()}} +
  • {data.get()}
  • +
    + ) + return { ...render(), data } + } + + test("init state is correct", () => { + const { container } = execute() + expect(container.querySelector("span")!.innerHTML).toBe("hi") + expect(container.querySelector("li")!.innerHTML).toBe("hi") + }) + + test("set the data to hello", async () => { + const { container, data } = execute() + act(() => { + data.set("hello") + }) + expect(container.querySelector("span")!.innerHTML).toBe("hello") + expect(container.querySelector("li")!.innerHTML).toBe("hi") + }) +}) + +it("renders null if no children/render prop is supplied a function", () => { + const restoreConsole = mockConsole() + const Comp = () => + const { container } = render() + expect(container).toMatchInlineSnapshot(`
    `) + restoreConsole() +}) diff --git a/packages/mobx-react/__tests__/Provider.test.tsx b/packages/mobx-react/__tests__/Provider.test.tsx deleted file mode 100644 index effea7a52f..0000000000 --- a/packages/mobx-react/__tests__/Provider.test.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from "react" -import { Provider } from "../src" -import { render } from "@testing-library/react" -import { MobXProviderContext } from "../src/Provider" -import { withConsole } from "./utils/withConsole" - -describe("Provider", () => { - it("should work in a simple case", () => { - function A() { - return ( - - {({ foo }) => foo} - - ) - } - - const { container } = render() - expect(container).toHaveTextContent("bar") - }) - - it("should not provide the children prop", () => { - function A() { - return ( - - - {stores => - Reflect.has(stores, "children") - ? "children was provided" - : "children was not provided" - } - - - ) - } - - const { container } = render() - expect(container).toHaveTextContent("children was not provided") - }) - - it("supports overriding stores", () => { - function B() { - return ( - - {({ overridable, nonOverridable }) => `${overridable} ${nonOverridable}`} - - ) - } - - function A() { - return ( - - - - - - - ) - } - const { container } = render() - expect(container).toMatchInlineSnapshot(` -
    - original original - overridden original -
    -`) - }) - - it("should throw an error when changing stores", () => { - function A({ foo }) { - return ( - - {({ foo }) => foo} - - ) - } - - const { rerender } = render(
    ) - - withConsole(() => { - expect(() => { - rerender() - }).toThrow("The set of provided stores has changed.") - }) - }) -}) diff --git a/packages/mobx-react/__tests__/__snapshots__/hooks.test.tsx.snap b/packages/mobx-react/__tests__/__snapshots__/hooks.test.tsx.snap deleted file mode 100644 index 9b95cca6aa..0000000000 --- a/packages/mobx-react/__tests__/__snapshots__/hooks.test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`computed properties react to props when using hooks 1`] = ` -[MockFunction] { - "calls": [ - [ - "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples.", - ], - [ - "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead.", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - { - "type": "return", - "value": undefined, - }, - ], -} -`; diff --git a/packages/mobx-react/__tests__/api.test.tsx b/packages/mobx-react/__tests__/api.test.tsx new file mode 100644 index 0000000000..d14bd4138e --- /dev/null +++ b/packages/mobx-react/__tests__/api.test.tsx @@ -0,0 +1,19 @@ +const api = require("../src/index.ts") + +test("correct api should be exposed", function () { + expect( + Object.keys(api) + .filter(key => api[key] !== undefined) + .sort() + ).toEqual( + [ + "isUsingStaticRendering", + "enableStaticRendering", + "observer", + "Observer", + "useLocalObservable", + "clearTimers", + "_observerFinalizationRegistry" + ].sort() + ) +}) diff --git a/packages/mobx-react/__tests__/context.test.tsx b/packages/mobx-react/__tests__/context.test.tsx deleted file mode 100644 index e88dbda85d..0000000000 --- a/packages/mobx-react/__tests__/context.test.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React from "react" -import { observable } from "mobx" -import { Provider, observer, inject } from "../src" -import { withConsole } from "./utils/withConsole" -import { render, act } from "@testing-library/react" -import { any } from "prop-types" - -test("no warnings in modern react", () => { - const box = observable.box(3) - const Child = inject("store")( - observer( - class Child extends React.Component { - render() { - return ( -
    - {this.props.store} + {box.get()} -
    - ) - } - } - ) - ) - - class App extends React.Component { - render() { - return ( -
    - - - - - -
    - ) - } - } - - const { container } = render() - expect(container).toHaveTextContent("42 + 3") - - withConsole(["info", "warn", "error"], () => { - act(() => { - box.set(4) - }) - expect(container).toHaveTextContent("42 + 4") - - expect(console.info).not.toHaveBeenCalled() - expect(console.warn).not.toHaveBeenCalled() - expect(console.error).not.toHaveBeenCalled() - }) -}) - -test("getDerivedStateFromProps works #447", () => { - class Main extends React.Component { - static getDerivedStateFromProps(nextProps, prevState) { - return { - count: prevState.count + 1 - } - } - - state = { - count: 0 - } - - render() { - return ( -
    -

    {`${this.state.count ? "One " : "No "}${this.props.thing}`}

    -
    - ) - } - } - - const MainInjected = inject(({ store }: { store: { thing: number } }) => ({ thing: store.thing }))(Main) - - const store = { thing: 3 } - - const App = () => ( - - - - ) - - const { container } = render() - expect(container).toHaveTextContent("One 3") -}) - -test("no double runs for getDerivedStateFromProps", () => { - let derived = 0 - @observer - class Main extends React.Component { - state = { - activePropertyElementMap: {} - } - - constructor(props) { - // console.log("CONSTRUCTOR") - super(props) - } - - static getDerivedStateFromProps() { - derived++ - // console.log("PREVSTATE", nextProps) - return null - } - - render() { - return
    Test-content
    - } - } - // This results in - //PREVSTATE - //CONSTRUCTOR - //PREVSTATE - let MainInjected = inject(() => ({ - componentProp: "def" - }))(Main) - // Uncomment the following line to see default behaviour (without inject) - //CONSTRUCTOR - //PREVSTATE - //MainInjected = Main; - - const store = {} - - const App = () => ( - - - - ) - - const { container } = render() - expect(container).toHaveTextContent("Test-content") - expect(derived).toBe(1) -}) diff --git a/packages/mobx-react/__tests__/disposeOnUnmount.test.tsx b/packages/mobx-react/__tests__/disposeOnUnmount.test.tsx deleted file mode 100644 index e28e20f01a..0000000000 --- a/packages/mobx-react/__tests__/disposeOnUnmount.test.tsx +++ /dev/null @@ -1,489 +0,0 @@ -import React from "react" -import { disposeOnUnmount, observer } from "../src" -import { render } from "@testing-library/react" -import { MockedComponentClass } from "react-dom/test-utils" - -interface ClassC extends MockedComponentClass { - methodA?: any - methodB?: any - methodC?: any - methodD?: any -} - -function testComponent(C: ClassC, afterMount?: Function, afterUnmount?: Function) { - const ref = React.createRef() - const { unmount } = render() - - let cref = ref.current - expect(cref?.methodA).not.toHaveBeenCalled() - expect(cref?.methodB).not.toHaveBeenCalled() - if (afterMount) { - afterMount(cref) - } - - unmount() - - expect(cref?.methodA).toHaveBeenCalledTimes(1) - expect(cref?.methodB).toHaveBeenCalledTimes(1) - if (afterUnmount) { - afterUnmount(cref) - } -} - -describe("without observer", () => { - test("class without componentWillUnmount", async () => { - class C extends React.Component { - @disposeOnUnmount - methodA = jest.fn() - @disposeOnUnmount - methodB = jest.fn() - @disposeOnUnmount - methodC = null - @disposeOnUnmount - methodD = undefined - - render() { - return null - } - } - - testComponent(C) - }) - - test("class with componentWillUnmount in the prototype", () => { - let called = 0 - - class C extends React.Component { - @disposeOnUnmount - methodA = jest.fn() - @disposeOnUnmount - methodB = jest.fn() - @disposeOnUnmount - methodC = null - @disposeOnUnmount - methodD = undefined - - render() { - return null - } - - componentWillUnmount() { - called++ - } - } - - testComponent( - C, - () => { - expect(called).toBe(0) - }, - () => { - expect(called).toBe(1) - } - ) - }) - - test.skip("class with componentWillUnmount as an arrow function", () => { - let called = 0 - - class C extends React.Component { - @disposeOnUnmount - methodA = jest.fn() - @disposeOnUnmount - methodB = jest.fn() - @disposeOnUnmount - methodC = null - @disposeOnUnmount - methodD = undefined - - render() { - return null - } - - componentWillUnmount = () => { - called++ - } - } - - testComponent( - C, - () => { - expect(called).toBe(0) - }, - () => { - expect(called).toBe(1) - } - ) - }) - - test("class without componentWillUnmount using non decorator version", () => { - let methodC = jest.fn() - let methodD = jest.fn() - class C extends React.Component { - render() { - return null - } - - methodA = disposeOnUnmount(this, jest.fn()) - methodB = disposeOnUnmount(this, jest.fn()) - - constructor(props) { - super(props) - disposeOnUnmount(this, [methodC, methodD]) - } - } - - testComponent( - C, - () => { - expect(methodC).not.toHaveBeenCalled() - expect(methodD).not.toHaveBeenCalled() - }, - () => { - expect(methodC).toHaveBeenCalledTimes(1) - expect(methodD).toHaveBeenCalledTimes(1) - } - ) - }) -}) - -describe("with observer", () => { - test("class without componentWillUnmount", () => { - @observer - class C extends React.Component { - @disposeOnUnmount - methodA = jest.fn() - @disposeOnUnmount - methodB = jest.fn() - @disposeOnUnmount - methodC = null - @disposeOnUnmount - methodD = undefined - - render() { - return null - } - } - - testComponent(C) - }) - - test("class with componentWillUnmount in the prototype", () => { - let called = 0 - - @observer - class C extends React.Component { - @disposeOnUnmount - methodA = jest.fn() - @disposeOnUnmount - methodB = jest.fn() - @disposeOnUnmount - methodC = null - @disposeOnUnmount - methodD = undefined - - render() { - return null - } - - componentWillUnmount() { - called++ - } - } - - testComponent( - C, - () => { - expect(called).toBe(0) - }, - () => { - expect(called).toBe(1) - } - ) - }) - - test.skip("class with componentWillUnmount as an arrow function", () => { - let called = 0 - - @observer - class C extends React.Component { - @disposeOnUnmount - methodA = jest.fn() - @disposeOnUnmount - methodB = jest.fn() - @disposeOnUnmount - methodC = null - @disposeOnUnmount - methodD = undefined - - render() { - return null - } - - componentWillUnmount = () => { - called++ - } - } - - testComponent( - C, - () => { - expect(called).toBe(0) - }, - () => { - expect(called).toBe(1) - } - ) - }) - - test("class without componentWillUnmount using non decorator version", () => { - let methodC = jest.fn() - let methodD = jest.fn() - - @observer - class C extends React.Component { - render() { - return null - } - - methodA = disposeOnUnmount(this, jest.fn()) - methodB = disposeOnUnmount(this, jest.fn()) - - constructor(props) { - super(props) - disposeOnUnmount(this, [methodC, methodD]) - } - } - - testComponent( - C, - () => { - expect(methodC).not.toHaveBeenCalled() - expect(methodD).not.toHaveBeenCalled() - }, - () => { - expect(methodC).toHaveBeenCalledTimes(1) - expect(methodD).toHaveBeenCalledTimes(1) - } - ) - }) -}) - -it("componentDidMount should be different between components", () => { - function doTest(withObserver) { - const events: Array = [] - - class A extends React.Component { - didMount - willUnmount - - componentDidMount() { - this.didMount = "A" - events.push("mountA") - } - - componentWillUnmount() { - this.willUnmount = "A" - events.push("unmountA") - } - - render() { - return null - } - } - - class B extends React.Component { - didMount - willUnmount - - componentDidMount() { - this.didMount = "B" - events.push("mountB") - } - - componentWillUnmount() { - this.willUnmount = "B" - events.push("unmountB") - } - - render() { - return null - } - } - - if (withObserver) { - // @ts-ignore - // eslint-disable-next-line no-class-assign - A = observer(A) - // @ts-ignore - // eslint-disable-next-line no-class-assign - B = observer(B) - } - - const aRef = React.createRef
    () - const { rerender, unmount } = render() - const caRef = aRef.current - - expect(caRef?.didMount).toBe("A") - expect(caRef?.willUnmount).toBeUndefined() - expect(events).toEqual(["mountA"]) - - const bRef = React.createRef() - rerender() - const cbRef = bRef.current - - expect(caRef?.didMount).toBe("A") - expect(caRef?.willUnmount).toBe("A") - - expect(cbRef?.didMount).toBe("B") - expect(cbRef?.willUnmount).toBeUndefined() - expect(events).toEqual(["mountA", "unmountA", "mountB"]) - - unmount() - - expect(caRef?.didMount).toBe("A") - expect(caRef?.willUnmount).toBe("A") - - expect(cbRef?.didMount).toBe("B") - expect(cbRef?.willUnmount).toBe("B") - expect(events).toEqual(["mountA", "unmountA", "mountB", "unmountB"]) - } - - doTest(true) - doTest(false) -}) - -test("base cWU should not be called if overridden", () => { - let baseCalled = 0 - let dCalled = 0 - let oCalled = 0 - - class C extends React.Component { - componentWillUnmount() { - baseCalled++ - } - - constructor(props) { - super(props) - this.componentWillUnmount = () => { - oCalled++ - } - } - - render() { - return null - } - - @disposeOnUnmount - fn() { - dCalled++ - } - } - const { unmount } = render() - unmount() - expect(dCalled).toBe(1) - expect(oCalled).toBe(1) - expect(baseCalled).toBe(0) -}) - -test("should error on inheritance", () => { - class C extends React.Component { - render() { - return null - } - } - - expect(() => { - // eslint-disable-next-line no-unused-vars - class B extends C { - @disposeOnUnmount - fn() {} - } - }).toThrow("disposeOnUnmount only supports direct subclasses") -}) - -test("should error on inheritance - 2", () => { - class C extends React.Component { - render() { - return null - } - } - - class B extends C { - fn - constructor(props) { - super(props) - expect(() => { - this.fn = disposeOnUnmount(this, function () {}) - }).toThrow("disposeOnUnmount only supports direct subclasses") - } - } - - render() -}) - -describe("should work with arrays", () => { - test("as a function", () => { - class C extends React.Component { - methodA = jest.fn() - methodB = jest.fn() - - componentDidMount() { - disposeOnUnmount(this, [this.methodA, this.methodB]) - } - - render() { - return null - } - } - - testComponent(C) - }) - - test("as a decorator", () => { - class C extends React.Component { - methodA = jest.fn() - methodB = jest.fn() - - @disposeOnUnmount - disposers = [this.methodA, this.methodB] - - render() { - return null - } - } - - testComponent(C) - }) -}) - -it("runDisposersOnUnmount only runs disposers from the declaring instance", () => { - class A extends React.Component { - @disposeOnUnmount - a = jest.fn() - - b = jest.fn() - - constructor(props) { - super(props) - disposeOnUnmount(this, this.b) - } - - render() { - return null - } - } - - const ref1 = React.createRef() - const ref2 = React.createRef() - const { unmount } = render() - render() - const inst1 = ref1.current - const inst2 = ref2.current - unmount() - - expect(inst1?.a).toHaveBeenCalledTimes(1) - expect(inst1?.b).toHaveBeenCalledTimes(1) - expect(inst2?.a).toHaveBeenCalledTimes(0) - expect(inst2?.b).toHaveBeenCalledTimes(0) -}) diff --git a/packages/mobx-react/__tests__/enforceActions.test.tsx b/packages/mobx-react/__tests__/enforceActions.test.tsx new file mode 100644 index 0000000000..1758129dc3 --- /dev/null +++ b/packages/mobx-react/__tests__/enforceActions.test.tsx @@ -0,0 +1,66 @@ +import * as mobx from "mobx" +import * as React from "react" +import { useEffect } from "react" +import { observer, useLocalObservable } from "../src" +import { render } from "@testing-library/react" + +let consoleWarnMock: jest.SpyInstance | undefined + +describe("enforcing actions", () => { + it("'never' should work", () => { + consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) + mobx.configure({ enforceActions: "never" }) + + const Parent = observer(() => { + const obs = useLocalObservable(() => ({ + x: 1 + })) + useEffect(() => { + obs.x++ + }, []) + + return
    {obs.x}
    + }) + + render() + expect(consoleWarnMock).not.toHaveBeenCalled() + }) + + it("'observed' should work", () => { + consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) + mobx.configure({ enforceActions: "observed" }) + + const Parent = observer(() => { + const obs = useLocalObservable(() => ({ + x: 1 + })) + useEffect(() => { + obs.x++ + }, []) + + return
    {obs.x}
    + }) + + render() + expect(consoleWarnMock).toHaveBeenCalledTimes(1) + }) + + it("'always' should work", () => { + consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) + mobx.configure({ enforceActions: "always" }) + + const Parent = observer(() => { + const obs = useLocalObservable(() => ({ + x: 1 + })) + useEffect(() => { + obs.x++ + }, []) + + return
    {obs.x}
    + }) + + render() + expect(consoleWarnMock).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/mobx-react/__tests__/hooks.test.tsx b/packages/mobx-react/__tests__/hooks.test.tsx deleted file mode 100644 index a56ec15d53..0000000000 --- a/packages/mobx-react/__tests__/hooks.test.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from "react" -import { observer, Observer, useLocalStore, useAsObservableSource } from "../src" -import { render, act } from "@testing-library/react" - -afterEach(() => { - jest.useRealTimers() -}) - -let consoleWarnMock: jest.SpyInstance | undefined -afterEach(() => { - consoleWarnMock?.mockRestore() -}) - -test("computed properties react to props when using hooks", async () => { - jest.useFakeTimers() - consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) - - const seen: Array = [] - - const Child = ({ x }) => { - const props = useAsObservableSource({ x }) - const store = useLocalStore(() => ({ - get getPropX() { - return props.x - } - })) - - return ( - {() => (seen.push(store.getPropX), (
    {store.getPropX}
    ))}
    - ) - } - - const Parent = () => { - const [state, setState] = React.useState({ x: 0 }) - seen.push("parent") - React.useEffect(() => { - setTimeout(() => { - act(() => { - setState({ x: 2 }) - }) - }) - }, []) - return - } - - const { container } = render() - expect(container).toHaveTextContent("0") - - act(() => { - jest.runAllTimers() - }) - expect(seen).toEqual(["parent", 0, "parent", 2, 2]) - expect(container).toHaveTextContent("2") - expect(consoleWarnMock).toMatchSnapshot() -}) - -test("computed properties result in double render when using observer instead of Observer", async () => { - jest.useFakeTimers() - - const seen: Array = [] - - const Child = observer(({ x }) => { - const props = useAsObservableSource({ x }) - const store = useLocalStore(() => ({ - get getPropX() { - return props.x - } - })) - - seen.push(store.getPropX) - return
    {store.getPropX}
    - }) - - const Parent = () => { - const [state, setState] = React.useState({ x: 0 }) - seen.push("parent") - React.useEffect(() => { - setTimeout(() => { - act(() => { - setState({ x: 2 }) - }) - }, 100) - }, []) - return - } - - const { container } = render() - expect(container).toHaveTextContent("0") - - act(() => { - jest.runAllTimers() - }) - expect(seen).toEqual([ - "parent", - 0, - "parent", - 2, // props changed - 2 // observable source changed (setState during render) - ]) - expect(container).toHaveTextContent("2") -}) diff --git a/packages/mobx-react/__tests__/inject.test.tsx b/packages/mobx-react/__tests__/inject.test.tsx deleted file mode 100644 index 6f97ac25e2..0000000000 --- a/packages/mobx-react/__tests__/inject.test.tsx +++ /dev/null @@ -1,553 +0,0 @@ -import React from "react" -import PropTypes from "prop-types" -import { action, observable, makeObservable } from "mobx" -import { observer, inject, Provider } from "../src" -import { IValueMap } from "../src/types/IValueMap" -import { render, act } from "@testing-library/react" -import { withConsole } from "./utils/withConsole" -import { IReactComponent } from "../src/types/IReactComponent" - -describe("inject based context", () => { - test("basic context", () => { - const C = inject("foo")( - observer( - class X extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - ) - const B = () => - const A = () => ( - - - - ) - const { container } = render(
    ) - expect(container).toHaveTextContent("context:bar") - }) - - test("props override context", () => { - const C = inject("foo")( - class T extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - const B = () => - const A = class T extends React.Component { - render() { - return ( - - - - ) - } - } - const { container } = render(
    ) - expect(container).toHaveTextContent("context:42") - }) - - test("wraps displayName of original component", () => { - const A: React.ComponentClass = inject("foo")( - class ComponentA extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - const B: React.ComponentClass = inject()( - class ComponentB extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - const C: React.ComponentClass = inject(() => ({}))( - class ComponentC extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - expect(A.displayName).toBe("inject-with-foo(ComponentA)") - expect(B.displayName).toBe("inject(ComponentB)") - expect(C.displayName).toBe("inject(ComponentC)") - }) - - test("shouldn't change original displayName of component that uses forwardRef", () => { - const FancyComp = React.forwardRef((_: any, ref: React.Ref) => { - return
    - }) - FancyComp.displayName = "FancyComp" - - inject("bla")(FancyComp) - - expect(FancyComp.displayName).toBe("FancyComp") - }) - - // FIXME: see other comments related to error catching in React - // test does work as expected when running manually - test("store should be available", () => { - const C = inject("foo")( - observer( - class C extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - ) - const B = () => - const A = class A extends React.Component { - render() { - return ( - - - - ) - } - } - - withConsole(() => { - expect(() => render(
    )).toThrow( - /Store 'foo' is not available! Make sure it is provided by some Provider/ - ) - }) - }) - - test("store is not required if prop is available", () => { - const C = inject("foo")( - observer( - class C extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - ) - const B = () => - const { container } = render() - expect(container).toHaveTextContent("context:bar") - }) - - test("inject merges (and overrides) props", () => { - const C = inject(() => ({ a: 1 }))( - observer( - class C extends React.Component { - render() { - expect(this.props).toEqual({ a: 1, b: 2 }) - return null - } - } - ) - ) - const B = () => - render() - }) - - test("custom storesToProps", () => { - const C = inject((stores: IValueMap, props: any) => { - expect(stores).toEqual({ foo: "bar" }) - expect(props).toEqual({ baz: 42 }) - return { - zoom: stores.foo, - baz: props.baz * 2 - } - })( - observer( - class C extends React.Component { - render() { - return ( -
    - context: - {this.props.zoom} - {this.props.baz} -
    - ) - } - } - ) - ) - const B = class B extends React.Component { - render() { - return - } - } - const A = () => ( - - - - ) - const { container } = render(
    ) - expect(container).toHaveTextContent("context:bar84") - }) - - test("inject forwards ref", () => { - class FancyComp extends React.Component { - didRender - render() { - this.didRender = true - return null - } - - doSomething() {} - } - - const ref = React.createRef() - render() - expect(typeof ref.current?.doSomething).toBe("function") - expect(ref.current?.didRender).toBe(true) - - const InjectedFancyComp = inject("bla")(FancyComp) - const ref2 = React.createRef() - - render( - - - - ) - - expect(typeof ref2.current?.doSomething).toBe("function") - expect(ref2.current?.didRender).toBe(true) - }) - - test("inject should work with components that use forwardRef", () => { - const FancyComp = React.forwardRef((_: any, ref: React.Ref) => { - return
    - }) - - const InjectedFancyComp = inject("bla")(FancyComp) - const ref = React.createRef() - - render( - - - - ) - - expect(ref.current).not.toBeNull() - expect(ref.current).toBeInstanceOf(HTMLDivElement) - }) - - test("support static hoisting, wrappedComponent and ref forwarding", () => { - class B extends React.Component { - static foo - static bar - testField - - render() { - this.testField = 1 - return null - } - } - ;(B as React.ComponentClass).propTypes = { - x: PropTypes.object - } - B.foo = 17 - B.bar = {} - const C = inject("booh")(B) - expect(C.wrappedComponent).toBe(B) - expect(B.foo).toBe(17) - expect(C.foo).toBe(17) - expect(C.bar === B.bar).toBeTruthy() - expect(Object.keys(C.wrappedComponent.propTypes!)).toEqual(["x"]) - - const ref = React.createRef() - - render() - expect(ref.current?.testField).toBe(1) - }) - - // skipping because `propTypes` and `defaultProps` are dropped in React 19 - test.skip("propTypes and defaultProps are forwarded", () => { - const msg: Array = [] - const baseError = console.error - console.error = m => msg.push(m) - - const C: React.ComponentClass = inject("foo")( - class C extends React.Component { - render() { - expect(this.props.y).toEqual(3) - - expect(this.props.x).toBeUndefined() - return null - } - } - ) - C.propTypes = { - x: PropTypes.func.isRequired, - z: PropTypes.string.isRequired - } - // @ts-ignore - C.wrappedComponent.propTypes = { - a: PropTypes.func.isRequired - } - C.defaultProps = { - y: 3 - } - const B = () => - const A = () => ( - - - - ) - render() - expect(msg.length).toBe(2) - // ! Somehow this got broken with upgraded deps and wasn't worth fixing it :) - // expect(msg[0].split("\n")[0]).toBe( - // "Warning: Failed prop type: The prop `x` is marked as required in `inject-with-foo(C)`, but its value is `undefined`." - // ) - // expect(msg[1].split("\n")[0]).toBe( - // "Warning: Failed prop type: The prop `a` is marked as required in `C`, but its value is `undefined`." - // ) - console.error = baseError - }) - - test("warning is not printed when attaching propTypes to injected component", () => { - let msg = [] - const baseWarn = console.warn - console.warn = m => (msg = m) - - const C: React.ComponentClass = inject("foo")( - class C extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - C.propTypes = {} - - expect(msg.length).toBe(0) - console.warn = baseWarn - }) - - test("warning is not printed when attaching propTypes to wrappedComponent", () => { - let msg = [] - const baseWarn = console.warn - console.warn = m => (msg = m) - const C = inject("foo")( - class C extends React.Component { - render() { - return ( -
    - context: - {this.props.foo} -
    - ) - } - } - ) - C.wrappedComponent.propTypes = {} - expect(msg.length).toBe(0) - console.warn = baseWarn - }) - - test("using a custom injector is reactive", () => { - const user = observable({ name: "Noa" }) - const mapper = stores => ({ name: stores.user.name }) - const DisplayName = props =>

    {props.name}

    - const User = inject(mapper)(DisplayName) - const App = () => ( - - - - ) - const { container } = render() - expect(container).toHaveTextContent("Noa") - - act(() => { - user.name = "Veria" - }) - expect(container).toHaveTextContent("Veria") - }) - - test("using a custom injector is not too reactive", () => { - let listRender = 0 - let itemRender = 0 - let injectRender = 0 - - function connect() { - const args = arguments - return (component: IReactComponent) => - // @ts-ignore - inject.apply(this, args)(observer(component)) - } - - class State { - @observable - highlighted = null - isHighlighted(item) { - return this.highlighted == item - } - - @action - highlight = item => { - this.highlighted = item - } - - constructor() { - makeObservable(this) - } - } - - const items = observable([ - { title: "ItemA" }, - { title: "ItemB" }, - { title: "ItemC" }, - { title: "ItemD" }, - { title: "ItemE" }, - { title: "ItemF" } - ]) - - const state = new State() - - class ListComponent extends React.PureComponent { - render() { - listRender++ - const { items } = this.props - - return ( -
      - {items.map(item => ( - - ))} -
    - ) - } - } - - // @ts-ignore - @connect(({ state }, { item }) => { - injectRender++ - if (injectRender > 6) { - // debugger; - } - return { - // Using - // highlighted: expr(() => state.isHighlighted(item)) // seems to fix the problem - highlighted: state.isHighlighted(item), - highlight: state.highlight - } - }) - class ItemComponent extends React.PureComponent { - highlight = () => { - const { item, highlight } = this.props - highlight(item) - } - - render() { - itemRender++ - const { highlighted, item } = this.props - return ( -
  • - {item.title} {highlighted ? "(highlighted)" : ""}{" "} -
  • - ) - } - } - - const { container } = render( - - - - ) - - expect(listRender).toBe(1) - expect(injectRender).toBe(6) - expect(itemRender).toBe(6) - - act(() => { - container - .querySelectorAll(".hl_ItemB") - .forEach((e: Element) => (e as HTMLElement).click()) - }) - - expect(listRender).toBe(1) - expect(injectRender).toBe(12) // ideally, 7 - expect(itemRender).toBe(7) - act(() => { - container - .querySelectorAll(".hl_ItemF") - .forEach((e: Element) => (e as HTMLElement).click()) - }) - - expect(listRender).toBe(1) - expect(injectRender).toBe(18) // ideally, 9 - expect(itemRender).toBe(9) - }) -}) - -test("#612 - mixed context types", () => { - const SomeContext = React.createContext(true) - - class MainCompClass extends React.Component { - static contextType = SomeContext - render() { - let active = this.context - return active ? this.props.value : "Inactive" - } - } - - const MainComp = inject((stores: any) => ({ - value: stores.appState.value - }))(MainCompClass) - - const appState = observable({ - value: "Something" - }) - - function App() { - return ( - - - - - - ) - } - - render() -}) diff --git a/packages/mobx-react/__tests__/issue21.test.tsx b/packages/mobx-react/__tests__/issue21.test.tsx index 99c8c3d792..13086a5f5f 100644 --- a/packages/mobx-react/__tests__/issue21.test.tsx +++ b/packages/mobx-react/__tests__/issue21.test.tsx @@ -3,6 +3,7 @@ import { computed, isObservable, observable, + observableRef, reaction, transaction, IReactionDisposer, @@ -50,7 +51,7 @@ const wizardModel = observable( } } as any, { - activateNextStep: observable.ref + activateNextStep: observableRef } ) @@ -123,27 +124,23 @@ test("verify issue 21", () => { test("verify prop changes are picked up", () => { function createItem(subid, label) { - const res = observable( - { - subid, - id: 1, - label: label, - get text() { - events.push(["compute", this.subid]) - return ( - this.id + - "." + - this.subid + - "." + - this.label + - "." + - data.items.indexOf(this as any) - ) - } - }, - {}, - { proxy: false } - ) + const res = observable({ + subid, + id: 1, + label: label, + get text() { + events.push(["compute", this.subid]) + return ( + this.id + + "." + + this.subid + + "." + + this.label + + "." + + data.items.indexOf(this as any) + ) + } + }) res.subid = subid // non reactive return res } diff --git a/packages/mobx-react/__tests__/observer.test.tsx b/packages/mobx-react/__tests__/observer.test.tsx index d4c03d7de2..1a3dc4f256 100644 --- a/packages/mobx-react/__tests__/observer.test.tsx +++ b/packages/mobx-react/__tests__/observer.test.tsx @@ -1,10 +1,9 @@ import React, { StrictMode, Suspense } from "react" -import { inject, observer, Observer, enableStaticRendering } from "../src" +import { observer, Observer, enableStaticRendering } from "../src" import { render, act, waitFor } from "@testing-library/react" import { getObserverTree, _getGlobalState, - _resetGlobalState, action, computed, observable, @@ -21,14 +20,7 @@ import { shallowEqual } from "../src/utils/utils" * some test suite is too tedious */ -afterEach(() => { - jest.useRealTimers() -}) - let consoleWarnMock: jest.SpyInstance | undefined -afterEach(() => { - consoleWarnMock?.mockRestore() -}) /* use TestUtils.renderIntoDocument will re-mounted the component with different props @@ -313,37 +305,6 @@ test("changing state in render should fail", () => { render() act(() => data.set(3)) - _resetGlobalState() -}) - -test("observer component can be injected", () => { - const msg: Array = [] - const baseWarn = console.warn - console.warn = m => msg.push(m) - - inject("foo")( - observer( - class T extends React.Component { - render() { - return null - } - } - ) - ) - - // N.B, the injected component will be observer since mobx-react 4.0! - inject(() => ({}))( - observer( - class T extends React.Component { - render() { - return null - } - } - ) - ) - - expect(msg.length).toBe(0) - console.warn = baseWarn }) test("correctly wraps display name of child component", () => { @@ -363,51 +324,37 @@ test("correctly wraps display name of child component", () => { expect((B as any).type.displayName).toEqual(undefined) }) -describe("124 - react to changes in this.props via computed", () => { - class T extends React.Component { - @computed - get computedProp() { - return this.props.x - } - render() { - return ( - - x: - {this.computedProp} - - ) - } - } - - const Comp = observer(T) - - class Parent extends React.Component { - state = { v: 1 } - render() { - return ( -
    this.setState({ v: 2 })}> - -
    - ) - } - } +test("MobX computed getters cannot read class props", () => { + const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {}) - test("init state is correct", () => { - const { container } = render() + const Comp = observer( + class TestCmp extends React.Component<{ x: number }> { + constructor(props) { + super(props) + makeObservable(this, { + computedProp: computed + }) + } - expect(container).toHaveTextContent("x:1") - }) + get computedProp() { + return this.props.x + } - test("change after click", () => { - const { container } = render() + render() { + return {this.computedProp} + } + } + ) - act(() => container.querySelector("div")!.click()) - expect(container).toHaveTextContent("x:2") - }) + try { + expect(() => render()).toThrow( + /^\[mobx-react\] Cannot read "TestCmp.props" in a reactive context/ + ) + } finally { + consoleErrorSpy.mockRestore() + } }) -// Test on skip: since all reactions are now run in batched updates, the original issues can no longer be reproduced -//this test case should be deprecated? test("should stop updating if error was thrown in render (#134)", () => { const data = observable.box(0) let renderingsCount = 0 @@ -517,21 +464,16 @@ describe("should render component even if setState called with exactly the same }) }) -test("it rerenders correctly if some props are non-observables - 1", () => { +test("class observer rerenders from observable prop and rereads plain prop", () => { let odata = observable({ x: 1 }) let data = { y: 1 } @observer class Comp extends React.Component { - @computed - get computed() { - // n.b: data.y would not rerender! shallowly new equal props are not stored - return this.props.odata.x - } render() { return ( - {this.props.odata.x}-{this.props.data.y}-{this.computed} + {this.props.odata.x}-{this.props.data.y}-{this.props.odata.x} ) } @@ -540,7 +482,6 @@ test("it rerenders correctly if some props are non-observables - 1", () => { const Parent = observer( class Parent extends React.Component { render() { - // this.props.odata.x; return } } @@ -562,50 +503,6 @@ test("it rerenders correctly if some props are non-observables - 1", () => { expect(container).toHaveTextContent("3-3-3") }) -test("it rerenders correctly if some props are non-observables - 2", () => { - let renderCount = 0 - let odata = observable({ x: 1 }) - - @observer - class Component extends React.PureComponent { - @computed - get computed() { - return this.props.data.y // should recompute, since props.data is changed - } - - render() { - renderCount++ - return ( - - {this.props.data.y}-{this.computed} - - ) - } - } - - const Parent = observer(props => { - let data = { y: props.odata.x } - return - }) - - function stuff() { - odata.x++ - } - - const { container } = render() - - expect(renderCount).toBe(1) - expect(container).toHaveTextContent("1-1") - - act(() => stuff()) - expect(renderCount).toBe(2) - expect(container).toHaveTextContent("2-2") - - act(() => stuff()) - expect(renderCount).toBe(3) - expect(container).toHaveTextContent("3-3") -}) - describe("Observer regions should react", () => { let data const Comp = () => ( @@ -671,28 +568,34 @@ test("parent / childs render in the right order", () => { let events: Array = [] class User { - @observable name = "User's name" + + constructor() { + makeObservable(this, { + name: observable + }) + } } class Store { - @observable user: User | null = new User() - @action + logout() { this.user = null } + constructor() { - makeObservable(this) + makeObservable(this, { + user: observable, + logout: action + }) } } function tryLogout() { try { - // ReactDOM.unstable_batchedUpdates(() => { store.logout() expect(true).toBeTruthy() - // }); } catch (e) { // t.fail(e) } @@ -722,8 +625,8 @@ test("parent / childs render in the right order", () => { expect(events).toEqual(["parent", "child", "parent"]) }) -describe("use Observer inject and render sugar should work ", () => { - test("use render without inject should be correct", () => { +describe("Observer render sugar should work", () => { + test("use render should be correct", () => { const Comp = () => (
    {123}} /> @@ -733,7 +636,7 @@ describe("use Observer inject and render sugar should work ", () => { expect(container).toHaveTextContent("123") }) - test("use children without inject should be correct", () => { + test("use children should be correct", () => { const Comp = () => (
    {() => {123}} @@ -750,6 +653,7 @@ describe("use Observer inject and render sugar should work ", () => { const Comp = () => (
    + {/* @ts-expect-error render and children are mutually exclusive */} {123}}>{() => {123}}
    ) @@ -789,46 +693,6 @@ test("static on function components are hoisted", () => { expect(Comp2.foo).toBe(3) }) -test("computed properties react to props", () => { - jest.useFakeTimers() - - const seen: Array = [] - @observer - class Child extends React.Component { - @computed - get getPropX() { - return this.props.x - } - - render() { - seen.push(this.getPropX) - return
    {this.getPropX}
    - } - } - - class Parent extends React.Component { - state = { x: 0 } - render() { - seen.push("parent") - return - } - - componentDidMount() { - setTimeout(() => this.setState({ x: 2 }), 100) - } - } - - const { container } = render() - expect(container).toHaveTextContent("0") - - act(() => { - jest.runAllTimers() - }) - expect(container).toHaveTextContent("2") - - expect(seen).toEqual(["parent", 0, "parent", 2]) -}) - test("#692 - componentDidUpdate is triggered", () => { jest.useFakeTimers() @@ -836,15 +700,16 @@ test("#692 - componentDidUpdate is triggered", () => { @observer class Test extends React.Component { - @observable counter = 0 - @action inc = () => this.counter++ constructor(props) { super(props) - makeObservable(this) + makeObservable(this, { + counter: observable, + inc: action + }) setTimeout(() => this.inc(), 300) } @@ -1115,6 +980,35 @@ test(`Observable changes in componenWillUnmount don't cause any warnings or erro consoleWarnSpy.mockRestore() }) +test("Class observer cleans up when componentWillUnmount is assigned on the instance", () => { + const o = observable.box(0) + let unmounts = 0 + + const TestCmp = observer( + class TestCmp extends React.Component { + constructor(props) { + super(props) + ;(this as any).componentWillUnmount = () => { + unmounts++ + } + } + + render() { + return o.get() + } + } + ) + + const { container, unmount } = render() + expect(container).toHaveTextContent("0") + expect(getObserverTree(o).observers!.length).toBe(1) + + unmount() + + expect(unmounts).toBe(1) + expect(getObserverTree(o).observers).toBe(undefined) +}) + test(`Observable prop workaround`, () => { configure({ enforceActions: "observed" diff --git a/packages/mobx-react/__tests__/propTypes.test.ts b/packages/mobx-react/__tests__/propTypes.test.ts deleted file mode 100644 index 6f5a7aa10d..0000000000 --- a/packages/mobx-react/__tests__/propTypes.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -import PropTypes from "prop-types" -import { PropTypes as MRPropTypes } from "../src" -import { observable } from "mobx" - -// Cause `checkPropTypes` caches errors and doesn't print them twice.... -// https://github.com/facebook/prop-types/issues/91 -let testComponentId = 0 - -function typeCheckFail(declaration, value, message) { - const baseError = console.error - let error = "" - console.error = msg => { - error = msg - } - - const props = { testProp: value } - const propTypes = { testProp: declaration } - - const compId = "testComponent" + ++testComponentId - PropTypes.checkPropTypes(propTypes, props, "prop", compId) - - error = error.replace(compId, "testComponent") - expect(error).toBe("Warning: Failed prop type: " + message) - console.error = baseError -} - -function typeCheckFailRequiredValues(declaration) { - const baseError = console.error - let error = "" - console.error = msg => { - error = msg - } - - const propTypes = { testProp: declaration } - const specifiedButIsNullMsg = /but its value is `null`\./ - const unspecifiedMsg = /but its value is `undefined`\./ - - const props1 = { testProp: null } - PropTypes.checkPropTypes(propTypes, props1, "testProp", "testComponent" + ++testComponentId) - expect(specifiedButIsNullMsg.test(error)).toBeTruthy() - - error = "" - const props2 = { testProp: undefined } - PropTypes.checkPropTypes(propTypes, props2, "testProp", "testComponent" + ++testComponentId) - expect(unspecifiedMsg.test(error)).toBeTruthy() - - error = "" - const props3 = {} - PropTypes.checkPropTypes(propTypes, props3, "testProp", "testComponent" + ++testComponentId) - expect(unspecifiedMsg.test(error)).toBeTruthy() - - console.error = baseError -} - -function typeCheckPass(declaration: any, value?: any) { - const props = { testProp: value } - const error = PropTypes.checkPropTypes( - { testProp: declaration }, - props, - "testProp", - "testComponent" + ++testComponentId - ) - expect(error).toBeUndefined() -} - -test("Valid values", () => { - typeCheckPass(MRPropTypes.observableArray, observable([])) - typeCheckPass(MRPropTypes.observableArrayOf(PropTypes.string), observable([""])) - typeCheckPass(MRPropTypes.arrayOrObservableArray, observable([])) - typeCheckPass(MRPropTypes.arrayOrObservableArray, []) - typeCheckPass(MRPropTypes.arrayOrObservableArrayOf(PropTypes.string), observable([""])) - typeCheckPass(MRPropTypes.arrayOrObservableArrayOf(PropTypes.string), [""]) - typeCheckPass(MRPropTypes.observableObject, observable({})) - typeCheckPass(MRPropTypes.objectOrObservableObject, {}) - typeCheckPass(MRPropTypes.objectOrObservableObject, observable({})) - typeCheckPass(MRPropTypes.observableMap, observable(observable.map({}, { deep: false }))) -}) - -test("should be implicitly optional and not warn", () => { - typeCheckPass(MRPropTypes.observableArray) - typeCheckPass(MRPropTypes.observableArrayOf(PropTypes.string)) - typeCheckPass(MRPropTypes.arrayOrObservableArray) - typeCheckPass(MRPropTypes.arrayOrObservableArrayOf(PropTypes.string)) - typeCheckPass(MRPropTypes.observableObject) - typeCheckPass(MRPropTypes.objectOrObservableObject) - typeCheckPass(MRPropTypes.observableMap) -}) - -test("should warn for missing required values, function (test)", () => { - typeCheckFailRequiredValues(MRPropTypes.observableArray.isRequired) - typeCheckFailRequiredValues(MRPropTypes.observableArrayOf(PropTypes.string).isRequired) - typeCheckFailRequiredValues(MRPropTypes.arrayOrObservableArray.isRequired) - typeCheckFailRequiredValues(MRPropTypes.arrayOrObservableArrayOf(PropTypes.string).isRequired) - typeCheckFailRequiredValues(MRPropTypes.observableObject.isRequired) - typeCheckFailRequiredValues(MRPropTypes.objectOrObservableObject.isRequired) - typeCheckFailRequiredValues(MRPropTypes.observableMap.isRequired) -}) - -test("should fail date and regexp correctly", () => { - typeCheckFail( - MRPropTypes.observableObject, - new Date(), - "Invalid prop `testProp` of type `date` supplied to " + - "`testComponent`, expected `mobx.ObservableObject`." - ) - typeCheckFail( - MRPropTypes.observableArray, - /please/, - "Invalid prop `testProp` of type `regexp` supplied to " + - "`testComponent`, expected `mobx.ObservableArray`." - ) -}) - -test("observableArray", () => { - typeCheckFail( - MRPropTypes.observableArray, - [], - "Invalid prop `testProp` of type `array` supplied to " + - "`testComponent`, expected `mobx.ObservableArray`." - ) - typeCheckFail( - MRPropTypes.observableArray, - "", - "Invalid prop `testProp` of type `string` supplied to " + - "`testComponent`, expected `mobx.ObservableArray`." - ) -}) - -test("arrayOrObservableArray", () => { - typeCheckFail( - MRPropTypes.arrayOrObservableArray, - "", - "Invalid prop `testProp` of type `string` supplied to " + - "`testComponent`, expected `mobx.ObservableArray` or javascript `array`." - ) -}) - -test("observableObject", () => { - typeCheckFail( - MRPropTypes.observableObject, - {}, - "Invalid prop `testProp` of type `object` supplied to " + - "`testComponent`, expected `mobx.ObservableObject`." - ) - typeCheckFail( - MRPropTypes.observableObject, - "", - "Invalid prop `testProp` of type `string` supplied to " + - "`testComponent`, expected `mobx.ObservableObject`." - ) -}) - -test("objectOrObservableObject", () => { - typeCheckFail( - MRPropTypes.objectOrObservableObject, - "", - "Invalid prop `testProp` of type `string` supplied to " + - "`testComponent`, expected `mobx.ObservableObject` or javascript `object`." - ) -}) - -test("observableMap", () => { - typeCheckFail( - MRPropTypes.observableMap, - {}, - "Invalid prop `testProp` of type `object` supplied to " + - "`testComponent`, expected `mobx.ObservableMap`." - ) -}) - -test("observableArrayOf", () => { - typeCheckFail( - MRPropTypes.observableArrayOf(PropTypes.string), - 2, - "Invalid prop `testProp` of type `number` supplied to " + - "`testComponent`, expected `mobx.ObservableArray`." - ) - typeCheckFail( - MRPropTypes.observableArrayOf(PropTypes.string), - observable([2]), - "Invalid prop `testProp[0]` of type `number` supplied to " + - "`testComponent`, expected `string`." - ) - typeCheckFail( - MRPropTypes.observableArrayOf({ foo: (MRPropTypes as any).string } as any), - { foo: "bar" }, - "Property `testProp` of component `testComponent` has invalid PropType notation." - ) -}) - -test("arrayOrObservableArrayOf", () => { - typeCheckFail( - MRPropTypes.arrayOrObservableArrayOf(PropTypes.string), - 2, - "Invalid prop `testProp` of type `number` supplied to " + - "`testComponent`, expected `mobx.ObservableArray` or javascript `array`." - ) - typeCheckFail( - MRPropTypes.arrayOrObservableArrayOf(PropTypes.string), - observable([2]), - "Invalid prop `testProp[0]` of type `number` supplied to " + - "`testComponent`, expected `string`." - ) - typeCheckFail( - MRPropTypes.arrayOrObservableArrayOf(PropTypes.string), - [2], - "Invalid prop `testProp[0]` of type `number` supplied to " + - "`testComponent`, expected `string`." - ) - // TODO: - typeCheckFail( - MRPropTypes.arrayOrObservableArrayOf({ foo: (MRPropTypes as any).string } as any), - { foo: "bar" }, - "Property `testProp` of component `testComponent` has invalid PropType notation." - ) -}) diff --git a/packages/mobx-react/__tests__/reactBatching.test.tsx b/packages/mobx-react/__tests__/reactBatching.test.tsx new file mode 100644 index 0000000000..82c4724777 --- /dev/null +++ b/packages/mobx-react/__tests__/reactBatching.test.tsx @@ -0,0 +1,53 @@ +import React from "react" +import { createRoot } from "react-dom/client" +import { flushSync } from "react-dom" +import { action, observable } from "mobx" +import { observer } from "../src" + +test("React batches independent observer updates without a custom reaction scheduler", async () => { + const store = observable({ a: 0, b: 0 }) + const renders: string[] = [] + let commits = 0 + + const A = observer(() => { + renders.push(`A:${store.a}`) + return {store.a} + }) + const B = observer(() => { + renders.push(`B:${store.b}`) + return {store.b} + }) + const App = () => ( + commits++}> +
    + + + ) + + const rootNode = document.createElement("div") + document.body.appendChild(rootNode) + const root = createRoot(rootNode) + + flushSync(() => { + root.render() + }) + + const commitsBeforeUpdate = commits + const rendersBeforeUpdate = renders.length + + setTimeout( + action(() => { + store.a++ + store.b++ + }), + 0 + ) + + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(commits - commitsBeforeUpdate).toBe(1) + expect(renders.slice(rendersBeforeUpdate)).toEqual(["A:1", "B:1"]) + + root.unmount() + rootNode.remove() +}) diff --git a/packages/mobx-react/__tests__/reactContext.test.tsx b/packages/mobx-react/__tests__/reactContext.test.tsx new file mode 100644 index 0000000000..b1fe23a4f6 --- /dev/null +++ b/packages/mobx-react/__tests__/reactContext.test.tsx @@ -0,0 +1,71 @@ +import React from "react" +import { act, render } from "@testing-library/react" +import { observable, runInAction } from "mobx" + +import { observer } from "../src" + +test("observer function component reacts to observable values from React context", () => { + const store = observable({ count: 0 }) + const StoreContext = React.createContext(store) + let renderCount = 0 + + const Counter = observer(function Counter() { + renderCount++ + const contextStore = React.useContext(StoreContext) + return {contextStore.count} + }) + + const { container } = render( + + + + ) + + expect(container.textContent).toBe("0") + expect(renderCount).toBe(1) + + act(() => { + runInAction(() => { + store.count++ + }) + }) + + expect(container.textContent).toBe("1") + expect(renderCount).toBe(2) +}) + +test("observer class component reacts to observable values from contextType", () => { + const store = observable({ count: 0 }) + const StoreContext = React.createContext(store) + let renderCount = 0 + + class Counter extends React.Component { + static contextType = StoreContext + + render() { + renderCount++ + const contextStore = this.context as { count: number } + return {contextStore.count} + } + } + + const ObservedCounter = observer(Counter) + + const { container } = render( + + + + ) + + expect(container.textContent).toBe("0") + expect(renderCount).toBe(1) + + act(() => { + runInAction(() => { + store.count++ + }) + }) + + expect(container.textContent).toBe("1") + expect(renderCount).toBe(2) +}) diff --git a/packages/mobx-react/__tests__/stateless.test.tsx b/packages/mobx-react/__tests__/stateless.test.tsx index febffeece3..91e3156cef 100644 --- a/packages/mobx-react/__tests__/stateless.test.tsx +++ b/packages/mobx-react/__tests__/stateless.test.tsx @@ -1,44 +1,8 @@ import React from "react" -import PropTypes from "prop-types" -import { observer, PropTypes as MRPropTypes } from "../src" +import { observer } from "../src" import { render, act } from "@testing-library/react" import { observable } from "mobx" -const StatelessComp = ({ testProp }) =>
    result: {testProp}
    - -StatelessComp.propTypes = { - testProp: PropTypes.string -} -StatelessComp.defaultProps = { - testProp: "default value for prop testProp" -} - -// skipping because `propTypes` and `defaultProps` are dropped in React 19 -describe.skip("stateless component with propTypes", () => { - const StatelessCompObserver: React.FunctionComponent = observer(StatelessComp) - - test("default property value should be propagated", () => { - expect(StatelessComp.defaultProps.testProp).toBe("default value for prop testProp") - expect(StatelessCompObserver.defaultProps!.testProp).toBe("default value for prop testProp") - }) - - const originalConsoleError = console.error - let beenWarned = false - console.error = () => (beenWarned = true) - // eslint-disable-next-line no-unused-vars - const wrapper = - console.error = originalConsoleError - - test("an error should be logged with a property type warning", () => { - expect(beenWarned).toBeTruthy() - }) - - test("render test correct", async () => { - const { container } = render() - expect(container.textContent).toBe("result: hello world") - }) -}) - test("stateless component with context support", () => { const C = React.createContext({}) @@ -58,29 +22,6 @@ test("stateless component with context support", () => { expect(container.textContent).toBe("context: hello world") }) -// propTypes validation seems to have been removed from class components in React 19: https://react.dev/reference/react/Component -test.skip("component with observable propTypes", () => { - class Comp extends React.Component { - render() { - return null - } - static propTypes = { - a1: MRPropTypes.observableArray, - a2: MRPropTypes.arrayOrObservableArray - } - } - const originalConsoleError = console.error - const warnings: Array = [] - console.error = msg => warnings.push(msg) - // eslint-disable-next-line no-unused-vars - const firstWrapper = - expect(warnings.length).toBe(1) - // eslint-disable-next-line no-unused-vars - const secondWrapper = - expect(warnings.length).toBe(1) - console.error = originalConsoleError -}) - describe("stateless component with forwardRef", () => { const a = observable({ x: 1 diff --git a/packages/mobx-react/__tests__/utils/compile-ts.tsx b/packages/mobx-react/__tests__/utils/compile-ts.tsx index 022f599172..b51ac914ce 100644 --- a/packages/mobx-react/__tests__/utils/compile-ts.tsx +++ b/packages/mobx-react/__tests__/utils/compile-ts.tsx @@ -1,15 +1,5 @@ import React from "react" -import ReactDOM from "react-dom" -import PropTypes from "prop-types" -import { - observer, - Provider, - inject, - Observer, - disposeOnUnmount, - PropTypes as MRPropTypes, - useLocalStore -} from "../src" +import { observer, Observer, useLocalObservable } from "../src" @observer class T1 extends React.Component<{ pizza: number }, {}> { @@ -28,9 +18,6 @@ const T2 = observer(
    ) } - static propTypes = { - zoem: MRPropTypes.arrayOrObservableArray - } } ) @@ -71,130 +58,6 @@ class T7 extends React.Component<{ pizza: number }, {}> { } React.createElement(observer(T7), { pizza: 4 }) -ReactDOM.render(, document.body) - -class ProviderTest extends React.Component { - render() { - return ( - -
    hi
    -
    - ) - } -} - -@inject(() => ({ x: 3 })) -class T11 extends React.Component<{ pizza: number; x?: number }, {}> { - render() { - return ( -
    - {this.props.pizza} - {this.props.x} -
    - ) - } -} - -class T15 extends React.Component<{ pizza: number; x?: number }, {}> { - render() { - return ( -
    - {this.props.pizza} - {this.props.x} -
    - ) - } -} -const T16 = inject(() => ({ x: 3 }))(T15) - -class T17 extends React.Component<{}, {}> { - render() { - return ( -
    - - - - - - -
    - ) - } -} - -@inject("a", "b") -class T12 extends React.Component<{ pizza: number }, {}> { - render() { - return
    {this.props.pizza}
    - } -} - -@inject("a", "b") -@observer -class T13 extends React.Component<{ pizza: number }, {}> { - render() { - return
    {this.props.pizza}
    - } -} - -const LoginContainer = inject((allStores, props) => ({ - store: { y: true, z: 2 }, - z: 7 -}))( - observer( - class _LoginContainer extends React.Component< - { - x: string - store?: { y: boolean; z: number } - }, - {} - > { - static contextTypes: React.ValidationMap = { - router: PropTypes.func.isRequired - } - - render() { - return ( -
    - Hello! - {this.props.x} - {this.props.store!.y} -
    - ) - } - } - ) -) -ReactDOM.render(, document.body) - -@inject(allStores => ({ - store: { y: true, z: 2 } -})) -@observer -class LoginContainer2 extends React.Component< - { - x: string - store?: { y: boolean } - }, - {} -> { - static contextTypes: React.ValidationMap = { - router: PropTypes.func.isRequired - } - - render() { - return ( -
    - Hello! - {this.props.x} - {this.props.store!.y} -
    - ) - } -} - -ReactDOM.render(, document.body) - class ObserverTest extends React.Component { render() { return {() =>
    test
    }
    @@ -225,57 +88,11 @@ const AppInner = observer((props: { a: number }) => { ) }) -const App = inject("store")(AppInner) - -App.wrappedComponent - -@inject("store") -@observer -class App2 extends React.Component<{ a: number }, {}> {} - -class InjectSomeStores extends React.Component<{ x: any }, {}> { - render() { - return
    Hello World
    - } -} - -inject(({ x }) => ({ x }))(InjectSomeStores) - -{ - class T extends React.Component<{ x: number }> { - render() { - return
    - } - } - - const Injected = inject("test")(T) - ; -} - -{ - // just to make sure it compiles - class DisposeOnUnmountComponent extends React.Component<{}> { - @disposeOnUnmount - methodA = () => {} - - methodB = disposeOnUnmount(this, () => {}) - manyMethods = disposeOnUnmount(this, [() => {}, () => {}]) - } - - // manual tests: this should not compile when the decorator is not applied over a react component class - /* - class DisposeOnUnmountNotAComponent { - @disposeOnUnmount - methodA = () => {} - - methodB = disposeOnUnmount(this, () => {}) - } - */ -} +; { const TestComponent = () => { - const observable = useLocalStore(() => ({ + const observable = useLocalObservable(() => ({ test: 3 })) diff --git a/packages/mobx-react/batchingForReactDom.js b/packages/mobx-react/batchingForReactDom.js deleted file mode 100644 index 3c73786c62..0000000000 --- a/packages/mobx-react/batchingForReactDom.js +++ /dev/null @@ -1 +0,0 @@ -require("mobx-react-lite/batchingForReactDom") diff --git a/packages/mobx-react/batchingForReactNative.js b/packages/mobx-react/batchingForReactNative.js deleted file mode 100644 index f6f328548a..0000000000 --- a/packages/mobx-react/batchingForReactNative.js +++ /dev/null @@ -1 +0,0 @@ -require("mobx-react-lite/batchingForReactNative") diff --git a/packages/mobx-react/batchingOptOut.js b/packages/mobx-react/batchingOptOut.js deleted file mode 100644 index f9533d9921..0000000000 --- a/packages/mobx-react/batchingOptOut.js +++ /dev/null @@ -1 +0,0 @@ -require("mobx-react-lite/batchingOptOut") diff --git a/packages/mobx-react/jest.config.js b/packages/mobx-react/jest.config.js index 9a49a001f2..c9f23e2c44 100644 --- a/packages/mobx-react/jest.config.js +++ b/packages/mobx-react/jest.config.js @@ -2,6 +2,9 @@ const buildConfig = require("../../jest.base.config") module.exports = buildConfig(__dirname, { testRegex: "__tests__/.*\\.tsx$", + moduleNameMapper: { + "^mobx-react-lite$": "/../mobx-react-lite/src/index.ts" + }, setupFilesAfterEnv: [`/jest.setup.ts`], testPathIgnorePatterns: ["node_modules", "/__tests__/utils"] }) diff --git a/packages/mobx-react/jest.setup.ts b/packages/mobx-react/jest.setup.ts index c02f52ea96..4a2d3994c5 100644 --- a/packages/mobx-react/jest.setup.ts +++ b/packages/mobx-react/jest.setup.ts @@ -1,9 +1,31 @@ import "@testing-library/jest-dom/extend-expect" -import { configure } from "mobx" +import { cleanup } from "@testing-library/react" +import { configure, _getGlobalState, _resetGlobalState } from "mobx" global.setImmediate = global.setImmediate || ((fn, ...args) => global.setTimeout(fn, 0, ...args)) -configure({ enforceActions: "never" }) +function resetMobxReactTestState() { + _resetGlobalState() + _getGlobalState().spyListeners = [] + configure({ + enforceActions: "never", + computedRequiresReaction: false, + reactionRequiresObservable: false, + observableRequiresReaction: false, + disableErrorBoundaries: false, + safeDescriptors: true + }) +} -// @ts-ignore -global.__DEV__ = true +beforeEach(() => { + // @ts-ignore + global.__DEV__ = true + resetMobxReactTestState() +}) + +afterEach(() => { + cleanup() + jest.useRealTimers() + jest.restoreAllMocks() + resetMobxReactTestState() +}) diff --git a/packages/mobx-react/package.json b/packages/mobx-react/package.json index 43ad918a4f..46a2274ef5 100644 --- a/packages/mobx-react/package.json +++ b/packages/mobx-react/package.json @@ -3,6 +3,7 @@ "version": "9.2.2", "description": "React bindings for MobX. Create fully reactive components.", "source": "src/index.ts", + "type": "commonjs", "main": "dist/index.js", "umd:main": "dist/mobxreact.umd.production.min.js", "unpkg": "dist/mobxreact.umd.production.min.js", @@ -12,13 +13,31 @@ "react-native": "dist/mobxreact.esm.js", "types": "dist/index.d.ts", "typings": "dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "react-native": { + "types": "./dist/index.d.ts", + "default": "./dist/mobxreact.esm.js" + }, + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/mobxreact.mjs" + }, + "default": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "./dist/*": "./dist/*", + "./src/*": "./src/*" + }, "files": [ "src", "dist", "LICENSE", "CHANGELOG.md", - "README.md", - "batching*" + "README.md" ], "sideEffects": false, "repository": { @@ -36,24 +55,15 @@ }, "homepage": "https://mobx.js.org", "dependencies": { - "mobx-react-lite": "^4.1.1" + "mobx-react-lite": "^5.0.0" }, "peerDependencies": { - "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "mobx": "^7.0.0", + "react": "^18 || ^19" }, "devDependencies": { "mobx": "^6.15.0", - "mobx-react-lite": "^4.1.1", - "expose-gc": "^1.0.0" + "mobx-react-lite": "^4.1.1" }, "keywords": [ "mobx", @@ -65,12 +75,12 @@ ], "scripts": { "lint": "eslint src/**/* --ext .js,.ts,.tsx", - "build": "node ../../scripts/build.js mobxReact", - "build:test": "npm run build -- --target test", + "build": "rollup --config rollup.config.mjs", + "build:test": "npm run build -- --environment TARGET:test", "test": "jest", "test:size": "import-size --report . observer", "test:types": "tsc --noEmit", "test:check": "npm run test:types", - "prepublishOnly": "npm run build -- --target publish" + "prepublishOnly": "npm run build -- --environment TARGET:publish" } } diff --git a/packages/mobx-react/rollup.config.mjs b/packages/mobx-react/rollup.config.mjs new file mode 100644 index 0000000000..adf1efd5e5 --- /dev/null +++ b/packages/mobx-react/rollup.config.mjs @@ -0,0 +1,12 @@ +import createRollupConfig from "../../scripts/create-rollup-config.mjs" + +export default createRollupConfig({ + packageName: "mobxReact", + packageBase: "mobxreact", + input: "src/index.ts", + globals: { + react: "React", + mobx: "mobx", + "mobx-react-lite": "mobxReactLite" + } +}) diff --git a/packages/mobx-react/src/Provider.tsx b/packages/mobx-react/src/Provider.tsx deleted file mode 100644 index b40f1da728..0000000000 --- a/packages/mobx-react/src/Provider.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react" -import { shallowEqual } from "./utils/utils" -import { IValueMap } from "./types/IValueMap" - -export const MobXProviderContext = React.createContext({}) - -export interface ProviderProps extends IValueMap { - children: React.ReactNode -} - -export function Provider(props: ProviderProps) { - const { children, ...stores } = props - const parentValue = React.useContext(MobXProviderContext) - const mutableProviderRef = React.useRef({ ...parentValue, ...stores }) - const value = mutableProviderRef.current - - if (__DEV__) { - const newValue = { ...value, ...stores } // spread in previous state for the context based stores - if (!shallowEqual(value, newValue)) { - throw new Error( - "MobX Provider: The set of provided stores has changed. See: https://github.com/mobxjs/mobx-react#the-set-of-provided-stores-has-changed-error." - ) - } - } - - return {children} -} - -Provider.displayName = "MobXProvider" diff --git a/packages/mobx-react/src/disposeOnUnmount.ts b/packages/mobx-react/src/disposeOnUnmount.ts deleted file mode 100644 index 6a1b640433..0000000000 --- a/packages/mobx-react/src/disposeOnUnmount.ts +++ /dev/null @@ -1,111 +0,0 @@ -import React from "react" -import { patch } from "./utils/utils" - -const reactMajorVersion = Number.parseInt(React.version, 10) -let warnedAboutDisposeOnUnmountDeprecated = false - -type Disposer = () => void - -const protoStoreKey = Symbol("disposeOnUnmountProto") -const instStoreKey = Symbol("disposeOnUnmountInst") - -function runDisposersOnWillUnmount() { - ;[...(this[protoStoreKey] || []), ...(this[instStoreKey] || [])].forEach(propKeyOrFunction => { - const prop = - typeof propKeyOrFunction === "string" ? this[propKeyOrFunction] : propKeyOrFunction - if (prop !== undefined && prop !== null) { - if (Array.isArray(prop)) prop.map(f => f()) - else prop() - } - }) -} - -/** - * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher. - */ -export function disposeOnUnmount(target: React.Component, propertyKey: PropertyKey): void - -/** - * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher. - */ -export function disposeOnUnmount>( - target: React.Component, - fn: TF -): TF - -/** - * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher. - */ -export function disposeOnUnmount( - target: React.Component, - propertyKeyOrFunction: PropertyKey | Disposer | Array -): PropertyKey | Disposer | Array | void { - if (Array.isArray(propertyKeyOrFunction)) { - return propertyKeyOrFunction.map(fn => disposeOnUnmount(target, fn)) - } - - if (!warnedAboutDisposeOnUnmountDeprecated) { - if (reactMajorVersion >= 18) { - console.error( - "[mobx-react] disposeOnUnmount is not compatible with React 18 and higher. Don't use it." - ) - } else { - console.warn( - "[mobx-react] disposeOnUnmount is deprecated. It won't work correctly with React 18 and higher." - ) - } - warnedAboutDisposeOnUnmountDeprecated = true - } - - const c = Object.getPrototypeOf(target).constructor - const c2 = Object.getPrototypeOf(target.constructor) - // Special case for react-hot-loader - const c3 = Object.getPrototypeOf(Object.getPrototypeOf(target)) - if ( - !( - c === React.Component || - c === React.PureComponent || - c2 === React.Component || - c2 === React.PureComponent || - c3 === React.Component || - c3 === React.PureComponent - ) - ) { - throw new Error( - "[mobx-react] disposeOnUnmount only supports direct subclasses of React.Component or React.PureComponent." - ) - } - - if ( - typeof propertyKeyOrFunction !== "string" && - typeof propertyKeyOrFunction !== "function" && - !Array.isArray(propertyKeyOrFunction) - ) { - throw new Error( - "[mobx-react] disposeOnUnmount only works if the parameter is either a property key or a function." - ) - } - - // decorator's target is the prototype, so it doesn't have any instance properties like props - const isDecorator = typeof propertyKeyOrFunction === "string" - - // add property key / function we want run (disposed) to the store - const componentWasAlreadyModified = !!target[protoStoreKey] || !!target[instStoreKey] - const store = isDecorator - ? // decorators are added to the prototype store - target[protoStoreKey] || (target[protoStoreKey] = []) - : // functions are added to the instance store - target[instStoreKey] || (target[instStoreKey] = []) - - store.push(propertyKeyOrFunction) - - // tweak the component class componentWillUnmount if not done already - if (!componentWasAlreadyModified) { - patch(target, "componentWillUnmount", runDisposersOnWillUnmount) - } - - // return the disposer as is if invoked as a non decorator - if (typeof propertyKeyOrFunction !== "string") { - return propertyKeyOrFunction - } -} diff --git a/packages/mobx-react/src/index.ts b/packages/mobx-react/src/index.ts index 65abc7ed72..70ec6007a0 100644 --- a/packages/mobx-react/src/index.ts +++ b/packages/mobx-react/src/index.ts @@ -11,20 +11,11 @@ if (!observable) { export { Observer, - useObserver, - useAsObservableSource, - useLocalStore, isUsingStaticRendering, - useStaticRendering, enableStaticRendering, - observerBatching, - useLocalObservable + useLocalObservable, + _observerFinalizationRegistry, + clearTimers } from "mobx-react-lite" export { observer } from "./observer" - -export { MobXProviderContext, Provider, ProviderProps } from "./Provider" -export { inject } from "./inject" -export { disposeOnUnmount } from "./disposeOnUnmount" -export { PropTypes } from "./propTypes" -export { IWrappedComponent } from "./types/IWrappedComponent" diff --git a/packages/mobx-react/src/inject.ts b/packages/mobx-react/src/inject.ts deleted file mode 100644 index 88e0f3341b..0000000000 --- a/packages/mobx-react/src/inject.ts +++ /dev/null @@ -1,107 +0,0 @@ -import React from "react" -import { observer } from "./observer" -import { copyStaticProperties } from "./utils/utils" -import { MobXProviderContext } from "./Provider" -import { IReactComponent } from "./types/IReactComponent" -import { IValueMap } from "./types/IValueMap" -import { IWrappedComponent } from "./types/IWrappedComponent" -import { IStoresToProps } from "./types/IStoresToProps" - -/** - * Store Injection - */ -function createStoreInjector( - grabStoresFn: IStoresToProps, - component: IReactComponent, - injectNames: string, - makeReactive: boolean -): IReactComponent { - // Support forward refs - let Injector: IReactComponent = React.forwardRef((props, ref) => { - const newProps = { ...props } - const context = React.useContext(MobXProviderContext) - Object.assign(newProps, grabStoresFn(context || {}, newProps) || {}) - - if (ref) { - newProps.ref = ref - } - - return React.createElement(component, newProps) - }) - - if (makeReactive) Injector = observer(Injector) - Injector["isMobxInjector"] = true // assigned late to suppress observer warning - - // Static fields from component should be visible on the generated Injector - copyStaticProperties(component, Injector) - Injector["wrappedComponent"] = component - Injector.displayName = getInjectName(component, injectNames) - return Injector -} - -function getInjectName(component: IReactComponent, injectNames: string): string { - let displayName - const componentName = - component.displayName || - component.name || - (component.constructor && component.constructor.name) || - "Component" - if (injectNames) displayName = "inject-with-" + injectNames + "(" + componentName + ")" - else displayName = "inject(" + componentName + ")" - return displayName -} - -function grabStoresByName( - storeNames: Array -): ( - baseStores: IValueMap, - nextProps: React.ClassAttributes -) => React.PropsWithRef | undefined { - return function (baseStores, nextProps) { - storeNames.forEach(function (storeName) { - if ( - storeName in nextProps // prefer props over stores - ) - return - if (!(storeName in baseStores)) - throw new Error( - "MobX injector: Store '" + - storeName + - "' is not available! Make sure it is provided by some Provider" - ) - nextProps[storeName] = baseStores[storeName] - }) - return nextProps - } -} - -export function inject( - ...stores: Array -): >( - target: T -) => T & (T extends IReactComponent ? IWrappedComponent

    : never) -export function inject( - fn: IStoresToProps -): (target: T) => T & IWrappedComponent

    - -/** - * higher order component that injects stores to a child. - * takes either a varargs list of strings, which are stores read from the context, - * or a function that manually maps the available stores from the context to props: - * storesToProps(mobxStores, props, context) => newProps - */ -export function inject(/* fn(stores, nextProps) or ...storeNames */ ...storeNames: Array) { - if (typeof arguments[0] === "function") { - let grabStoresFn = arguments[0] - return (componentClass: React.ComponentClass) => - createStoreInjector(grabStoresFn, componentClass, grabStoresFn.name, true) - } else { - return (componentClass: React.ComponentClass) => - createStoreInjector( - grabStoresByName(storeNames), - componentClass, - storeNames.join("-"), - false - ) - } -} diff --git a/packages/mobx-react/src/observer.tsx b/packages/mobx-react/src/observer.tsx index a16bb13887..fc62f43411 100644 --- a/packages/mobx-react/src/observer.tsx +++ b/packages/mobx-react/src/observer.tsx @@ -7,17 +7,18 @@ import { IReactComponent } from "./types/IReactComponent" /** * Observer function / decorator */ -export function observer(component: T, context: ClassDecoratorContext): void +export function observer( + component: T, + context: ClassDecoratorContext +): void export function observer(component: T): T -export function observer(component: T, context?: ClassDecoratorContext): T { +export function observer( + component: T, + context?: ClassDecoratorContext +): T { if (context && context.kind !== "class") { throw new Error("The @observer decorator can be used on classes only") } - if (component["isMobxInjector"] === true) { - console.warn( - "Mobx observer: You are trying to use `observer` on a component that already has `inject`. Please apply `observer` before applying `inject`" - ) - } if ( Object.prototype.isPrototypeOf.call(React.Component, component) || @@ -27,6 +28,6 @@ export function observer(component: T, context?: Clas return makeClassComponentObserver(component as React.ComponentClass) as T } else { // Function component - return observerLite(component as React.FunctionComponent) as T + return observerLite(component as React.FunctionComponent) as unknown as T } } diff --git a/packages/mobx-react/src/propTypes.ts b/packages/mobx-react/src/propTypes.ts deleted file mode 100644 index 0e6e164bce..0000000000 --- a/packages/mobx-react/src/propTypes.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { isObservableArray, isObservableObject, isObservableMap, untracked } from "mobx" - -// Copied from React.PropTypes -function createChainableTypeChecker(validator: React.Validator): React.Requireable { - function checkType( - isRequired: boolean, - props: any, - propName: string, - componentName: string, - location: string, - propFullName: string, - ...rest: any[] - ) { - return untracked(() => { - componentName = componentName || "<>" - propFullName = propFullName || propName - if (props[propName] == null) { - if (isRequired) { - const actual = props[propName] === null ? "null" : "undefined" - return new Error( - "The " + - location + - " `" + - propFullName + - "` is marked as required " + - "in `" + - componentName + - "`, but its value is `" + - actual + - "`." - ) - } - return null - } else { - // @ts-ignore rest arg is necessary for some React internals - fails tests otherwise - return validator(props, propName, componentName, location, propFullName, ...rest) - } - }) - } - - const chainedCheckType: any = checkType.bind(null, false) - // Add isRequired to satisfy Requirable - chainedCheckType.isRequired = checkType.bind(null, true) - return chainedCheckType -} - -// Copied from React.PropTypes -function isSymbol(propType: any, propValue: any): boolean { - // Native Symbol. - if (propType === "symbol") { - return true - } - - // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol' - if (propValue["@@toStringTag"] === "Symbol") { - return true - } - - // Fallback for non-spec compliant Symbols which are polyfilled. - if (typeof Symbol === "function" && propValue instanceof Symbol) { - return true - } - - return false -} - -// Copied from React.PropTypes -function getPropType(propValue: any): string { - const propType = typeof propValue - if (Array.isArray(propValue)) { - return "array" - } - if (propValue instanceof RegExp) { - // Old webkits (at least until Android 4.0) return 'function' rather than - // 'object' for typeof a RegExp. We'll normalize this here so that /bla/ - // passes PropTypes.object. - return "object" - } - if (isSymbol(propType, propValue)) { - return "symbol" - } - return propType -} - -// This handles more types than `getPropType`. Only used for error messages. -// Copied from React.PropTypes -function getPreciseType(propValue: any): string { - const propType = getPropType(propValue) - if (propType === "object") { - if (propValue instanceof Date) { - return "date" - } else if (propValue instanceof RegExp) { - return "regexp" - } - } - return propType -} - -function createObservableTypeCheckerCreator( - allowNativeType: any, - mobxType: any -): React.Requireable { - return createChainableTypeChecker((props, propName, componentName, location, propFullName) => { - return untracked(() => { - if (allowNativeType) { - if (getPropType(props[propName]) === mobxType.toLowerCase()) return null - } - let mobxChecker - switch (mobxType) { - case "Array": - mobxChecker = isObservableArray - break - case "Object": - mobxChecker = isObservableObject - break - case "Map": - mobxChecker = isObservableMap - break - default: - throw new Error(`Unexpected mobxType: ${mobxType}`) - } - const propValue = props[propName] - if (!mobxChecker(propValue)) { - const preciseType = getPreciseType(propValue) - const nativeTypeExpectationMessage = allowNativeType - ? " or javascript `" + mobxType.toLowerCase() + "`" - : "" - return new Error( - "Invalid prop `" + - propFullName + - "` of type `" + - preciseType + - "` supplied to" + - " `" + - componentName + - "`, expected `mobx.Observable" + - mobxType + - "`" + - nativeTypeExpectationMessage + - "." - ) - } - return null - }) - }) -} - -function createObservableArrayOfTypeChecker( - allowNativeType: boolean, - typeChecker: React.Validator -) { - return createChainableTypeChecker( - (props, propName, componentName, location, propFullName, ...rest) => { - return untracked(() => { - if (typeof typeChecker !== "function") { - return new Error( - "Property `" + - propFullName + - "` of component `" + - componentName + - "` has " + - "invalid PropType notation." - ) - } else { - let error = createObservableTypeCheckerCreator(allowNativeType, "Array")( - props, - propName, - componentName, - location, - propFullName - ) - - if (error instanceof Error) return error - const propValue = props[propName] - for (let i = 0; i < propValue.length; i++) { - error = (typeChecker as React.Validator)( - propValue, - i as any, - componentName, - location, - propFullName + "[" + i + "]", - ...rest - ) - if (error instanceof Error) return error - } - - return null - } - }) - } - ) -} - -const observableArray = createObservableTypeCheckerCreator(false, "Array") -const observableArrayOf = createObservableArrayOfTypeChecker.bind(null, false) -const observableMap = createObservableTypeCheckerCreator(false, "Map") -const observableObject = createObservableTypeCheckerCreator(false, "Object") -const arrayOrObservableArray = createObservableTypeCheckerCreator(true, "Array") -const arrayOrObservableArrayOf = createObservableArrayOfTypeChecker.bind(null, true) -const objectOrObservableObject = createObservableTypeCheckerCreator(true, "Object") - -export const PropTypes = { - observableArray, - observableArrayOf, - observableMap, - observableObject, - arrayOrObservableArray, - arrayOrObservableArrayOf, - objectOrObservableObject -} diff --git a/packages/mobx-react/src/types/IStoresToProps.ts b/packages/mobx-react/src/types/IStoresToProps.ts deleted file mode 100644 index 44a23a48a0..0000000000 --- a/packages/mobx-react/src/types/IStoresToProps.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IValueMap } from "./IValueMap" -export type IStoresToProps< - S extends IValueMap = {}, - P extends IValueMap = {}, - I extends IValueMap = {}, - C extends IValueMap = {} -> = (stores: S, nextProps: P, context?: C) => I diff --git a/packages/mobx-react/src/types/IValueMap.ts b/packages/mobx-react/src/types/IValueMap.ts deleted file mode 100644 index f7f1ad6cb9..0000000000 --- a/packages/mobx-react/src/types/IValueMap.ts +++ /dev/null @@ -1 +0,0 @@ -export type IValueMap = Record diff --git a/packages/mobx-react/src/types/IWrappedComponent.ts b/packages/mobx-react/src/types/IWrappedComponent.ts deleted file mode 100644 index 72abbae0b7..0000000000 --- a/packages/mobx-react/src/types/IWrappedComponent.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { IReactComponent } from "./IReactComponent" -export type IWrappedComponent

    = { - wrappedComponent: IReactComponent

    -} diff --git a/packages/mobx-react/src/utils/utils.ts b/packages/mobx-react/src/utils/utils.ts index 9571d3b6f0..4be63a9af3 100644 --- a/packages/mobx-react/src/utils/utils.ts +++ b/packages/mobx-react/src/utils/utils.ts @@ -28,52 +28,6 @@ function is(x: any, y: any): boolean { } } -// based on https://github.com/mridgway/hoist-non-react-statics/blob/master/src/index.js -const hoistBlackList = { - $$typeof: 1, - render: 1, - compare: 1, - type: 1, - childContextTypes: 1, - contextType: 1, - contextTypes: 1, - defaultProps: 1, - getDefaultProps: 1, - getDerivedStateFromError: 1, - getDerivedStateFromProps: 1, - mixins: 1, - displayName: 1, - propTypes: 1 -} - -export function copyStaticProperties(base: object, target: object): void { - const protoProps = Object.getOwnPropertyNames(Object.getPrototypeOf(base)) - Object.getOwnPropertyNames(base).forEach(key => { - if (!hoistBlackList[key] && protoProps.indexOf(key) === -1) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(base, key)!) - } - }) -} - -/** - * Helper to set `prop` to `this` as non-enumerable (hidden prop) - * @param target - * @param prop - * @param value - */ -export function setHiddenProp(target: object, prop: any, value: any): void { - if (!Object.hasOwnProperty.call(target, prop)) { - Object.defineProperty(target, prop, { - enumerable: false, - configurable: true, - writable: true, - value - }) - } else { - target[prop] = value - } -} - /** * Utilities for patching componentWillUnmount, to make sure @disposeOnUnmount works correctly icm with user defined hooks * and the handler provided by mobx-react diff --git a/packages/mobx-react/tsdx.config.js b/packages/mobx-react/tsdx.config.js deleted file mode 100644 index 07d57bd69f..0000000000 --- a/packages/mobx-react/tsdx.config.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - rollup(config) { - return { - ...config, - output: { - ...config.output, - globals: { - react: "React", - mobx: "mobx", - "react-dom": "ReactDOM", - "mobx-react-lite": "mobxReactLite" - } - } - } - } -} diff --git a/packages/mobx/__tests__/base/__snapshots__/proxies.js.snap b/packages/mobx/__tests__/base/__snapshots__/proxies.js.snap index d6a79f19f5..9d7ecb58b6 100644 --- a/packages/mobx/__tests__/base/__snapshots__/proxies.js.snap +++ b/packages/mobx/__tests__/base/__snapshots__/proxies.js.snap @@ -69,38 +69,3 @@ exports[`extend proxies 1`] = ` }, } `; - -exports[`non-proxied object 1`] = ` -{ - "b": { - "configurable": true, - "enumerable": true, - "value": 4, - "writable": true, - }, - "double": { - "configurable": true, - "enumerable": false, - "value": [Function], - "writable": false, - }, - "x": { - "configurable": true, - "enumerable": true, - "get": [Function], - "set": [Function], - }, - "y": { - "configurable": true, - "enumerable": false, - "get": [Function], - "set": [Function], - }, - Symbol(mobx administration): { - "configurable": true, - "enumerable": false, - "value": "(omitted)", - "writable": true, - }, -} -`; diff --git a/packages/mobx/__tests__/base/action.js b/packages/mobx/__tests__/base/action.js index 782faa77eb..78352213a8 100644 --- a/packages/mobx/__tests__/base/action.js +++ b/packages/mobx/__tests__/base/action.js @@ -418,7 +418,7 @@ test("extendObservable respects action decorators", () => { }, { a1: mobx.action, - a2: mobx.action.bound, + a2: mobx.actionBound, a3: false } ) @@ -460,7 +460,7 @@ test("bound actions bind", () => { } }, { - z: mobx.action.bound + z: mobx.actionBound } ) diff --git a/packages/mobx/__tests__/base/api.js b/packages/mobx/__tests__/base/api.js index 4f705e0806..4c64fdd142 100644 --- a/packages/mobx/__tests__/base/api.js +++ b/packages/mobx/__tests__/base/api.js @@ -9,19 +9,26 @@ test("correct api should be exposed", function () { [ "$mobx", // adminstration symbol "action", + "actionBound", "_allowStateChanges", "_allowStateChangesInsideComputed", "_allowStateReadsEnd", "_allowStateReadsStart", "_autoAction", + "_autoActionBound", "autorun", - "comparer", + "compareDefault", + "compareIdentity", + "compareShallow", + "compareStructural", "computed", + "computedStruct", "configure", "createAtom", "defineProperty", "extendObservable", "flow", + "flowBound", "isFlow", "flowResult", "FlowCancellationError", @@ -53,6 +60,10 @@ test("correct api should be exposed", function () { "ObservableMap", "ObservableSet", "observable", + "observableDeep", + "observableRef", + "observableShallow", + "observableStruct", "observe", "onReactionError", "onBecomeObserved", @@ -66,7 +77,6 @@ test("correct api should be exposed", function () { "set", "spy", "toJS", - "trace", "transaction", "untracked", "values", diff --git a/packages/mobx/__tests__/base/babel-decorators.js b/packages/mobx/__tests__/base/babel-decorators.js deleted file mode 100644 index c90214e9be..0000000000 --- a/packages/mobx/__tests__/base/babel-decorators.js +++ /dev/null @@ -1,1100 +0,0 @@ -import { - observable, - computed, - transaction, - autorun, - extendObservable, - action, - isObservableObject, - observe, - isObservable, - isObservableProp, - isComputedProp, - spy, - isAction, - configure, - makeObservable -} from "../../src/mobx" -import * as mobx from "../../src/mobx" - -test("babel", function () { - class Box { - @observable uninitialized - @observable height = 20 - @observable sizes = [2] - @observable - someFunc = function () { - return 2 - } - - constructor() { - makeObservable(this) - } - - @computed - get width() { - return this.height * this.sizes.length * this.someFunc() * (this.uninitialized ? 2 : 1) - } - @action - addSize() { - this.sizes.push(3) - this.sizes.push(4) - } - } - - const box = new Box() - const ar = [] - autorun(() => { - ar.push(box.width) - }) - - let s = ar.slice() - expect(s).toEqual([40]) - box.height = 10 - s = ar.slice() - expect(s).toEqual([40, 20]) - box.sizes.push(3, 4) - s = ar.slice() - expect(s).toEqual([40, 20, 60]) - box.someFunc = () => 7 - s = ar.slice() - expect(s).toEqual([40, 20, 60, 210]) - box.uninitialized = true - s = ar.slice() - expect(s).toEqual([40, 20, 60, 210, 420]) - box.addSize() - s = ar.slice() - expect(s).toEqual([40, 20, 60, 210, 420, 700]) -}) - -test("should not be possible to use @action with getters", () => { - class A { - constructor() { - makeObservable(this) - } - - @action - get Test() {} - } - expect(() => { - new A() - }).toThrow(/can only be used on properties with a function value/) - - mobx._resetGlobalState() -}) - -test("babel: parameterized computed decorator", () => { - class TestClass { - @observable x = 3 - @observable y = 3 - - constructor() { - makeObservable(this) - } - - @computed.struct - get boxedSum() { - return { sum: Math.round(this.x) + Math.round(this.y) } - } - } - - const t1 = new TestClass() - const changes = [] - const d = autorun(() => changes.push(t1.boxedSum)) - - t1.y = 4 // change - expect(changes.length).toBe(2) - t1.y = 4.2 // no change - expect(changes.length).toBe(2) - transaction(() => { - t1.y = 3 - t1.x = 4 - }) // no change - expect(changes.length).toBe(2) - t1.x = 6 // change - expect(changes.length).toBe(3) - d() - - expect(changes).toEqual([{ sum: 6 }, { sum: 7 }, { sum: 9 }]) -}) - -test("computed value should be the same around changing which was considered equivalent", () => { - class TestClass { - @observable c = null - defaultCollection = [] - - constructor() { - makeObservable(this) - } - - @computed.struct - get collection() { - return this.c || this.defaultCollection - } - } - - const t1 = new TestClass() - - const d = autorun(() => t1.collection) - - const oldCollection = t1.collection - t1.c = [] - const newCollection = t1.collection - - expect(oldCollection).toBe(newCollection) - - d() -}) - -class Order { - @observable price = 3 - @observable amount = 2 - @observable orders = [] - @observable aFunction = function () {} - - constructor() { - makeObservable(this) - } - - @computed - get total() { - return this.amount * this.price * (1 + this.orders.length) - } -} - -test("issue 191 - shared initializers (babel)", function () { - class Test { - @observable obj = { a: 1 } - @observable array = [2] - - constructor() { - makeObservable(this) - } - } - - const t1 = new Test() - t1.obj.a = 2 - t1.array.push(3) - - const t2 = new Test() - t2.obj.a = 3 - t2.array.push(4) - - expect(t1.obj).not.toBe(t2.obj) - expect(t1.array).not.toBe(t2.array) - expect(t1.obj.a).toBe(2) - expect(t2.obj.a).toBe(3) - - expect(t1.array.slice()).toEqual([2, 3]) - expect(t2.array.slice()).toEqual([2, 4]) -}) - -test("705 - setter undoing caching (babel)", () => { - let recomputes = 0 - let autoruns = 0 - - class Person { - @observable name - @observable title - - constructor() { - makeObservable(this) - } - - set fullName(val) { - // Noop - } - @computed - get fullName() { - recomputes++ - return this.title + " " + this.name - } - } - - let p1 = new Person() - p1.name = "Tom Tank" - p1.title = "Mr." - - expect(recomputes).toBe(0) - expect(autoruns).toBe(0) - - const d1 = autorun(() => { - autoruns++ - p1.fullName - }) - - const d2 = autorun(() => { - autoruns++ - p1.fullName - }) - - expect(recomputes).toBe(1) - expect(autoruns).toBe(2) - - p1.title = "Master" - expect(recomputes).toBe(2) - expect(autoruns).toBe(4) - - d1() - d2() -}) - -function normalizeSpyEvents(events) { - events.forEach(ev => { - delete ev.fn - delete ev.time - }) - return events -} - -test("action decorator (babel)", function () { - class Store { - constructor(multiplier) { - makeObservable(this) - this.multiplier = multiplier - } - - @action - add(a, b) { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(3) - const events = [] - const d = spy(events.push.bind(events)) - expect(store1.add(3, 4)).toBe(14) - expect(store2.add(3, 4)).toBe(21) - expect(store1.add(1, 1)).toBe(4) - - expect(normalizeSpyEvents(events)).toEqual([ - { arguments: [3, 4], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [3, 4], name: "add", spyReportStart: true, object: store2, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [1, 1], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("custom action decorator (babel)", function () { - class Store { - constructor(multiplier) { - makeObservable(this) - this.multiplier = multiplier - } - - @action("zoem zoem") - add(a, b) { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(3) - const events = [] - const d = spy(events.push.bind(events)) - expect(store1.add(3, 4)).toBe(14) - expect(store2.add(3, 4)).toBe(21) - expect(store1.add(1, 1)).toBe(4) - - expect(normalizeSpyEvents(events)).toEqual([ - { - arguments: [3, 4], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [3, 4], - name: "zoem zoem", - spyReportStart: true, - object: store2, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [1, 1], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("action decorator on field (babel)", function () { - class Store { - constructor(multiplier) { - makeObservable(this) - this.multiplier = multiplier - } - - @action - add = (a, b) => { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(7) - - const events = [] - const d = spy(events.push.bind(events)) - expect(store1.add(3, 4)).toBe(14) - expect(store2.add(5, 4)).toBe(63) - expect(store1.add(2, 2)).toBe(8) - - expect(normalizeSpyEvents(events)).toEqual([ - { arguments: [3, 4], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [5, 4], name: "add", spyReportStart: true, object: store2, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [2, 2], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("custom action decorator on field (babel)", function () { - class Store { - constructor(multiplier) { - makeObservable(this) - this.multiplier = multiplier - } - - @action("zoem zoem") - add = (a, b) => { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(7) - - const events = [] - const d = spy(events.push.bind(events)) - expect(store1.add(3, 4)).toBe(14) - expect(store2.add(5, 4)).toBe(63) - expect(store1.add(2, 2)).toBe(8) - - expect(normalizeSpyEvents(events)).toEqual([ - { - arguments: [3, 4], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [5, 4], - name: "zoem zoem", - spyReportStart: true, - object: store2, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [2, 2], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("267 (babel) should be possible to declare properties observable outside strict mode", () => { - configure({ enforceActions: "observed" }) - - class Store { - @observable timer - - constructor() { - makeObservable(this) - } - } - Store // just to avoid linter warning -}) - -test("288 atom not detected for object property", () => { - class Store { - @mobx.observable foo = "" - - constructor() { - makeObservable(this) - } - } - - const store = new Store() - let changed = false - - mobx.observe( - store, - "foo", - () => { - changed = true - }, - true - ) - expect(changed).toBe(true) -}) - -test.skip("observable performance - babel - decorators", () => { - const AMOUNT = 100000 - - class A { - @observable a = 1 - @observable b = 2 - @observable c = 3 - - constructor() { - makeObservable(this) - } - - @computed - get d() { - return this.a + this.b + this.c - } - } - - const objs = [] - const start = Date.now() - - for (let i = 0; i < AMOUNT; i++) objs.push(new A()) - - console.log("created in ", Date.now() - start) - - for (let j = 0; j < 4; j++) { - for (let i = 0; i < AMOUNT; i++) { - const obj = objs[i] - obj.a += 3 - obj.b *= 4 - obj.c = obj.b - obj.a - obj.d - } - } - - console.log("changed in ", Date.now() - start) -}) - -test("unbound methods", () => { - class A { - constructor() { - makeObservable(this) - } - - // shared across all instances - @action - m1() {} - - // per instance - @action m2 = () => {} - } - - const a1 = new A() - const a2 = new A() - - expect(a1.m1).toBe(a2.m1) - expect(a1.m2).not.toBe(a2.m2) - expect(a1.hasOwnProperty("m1")).toBe(false) - expect(a1.hasOwnProperty("m2")).toBe(true) - expect(a2.hasOwnProperty("m1")).toBe(false) - expect(a2.hasOwnProperty("m2")).toBe(true) -}) - -test("inheritance", () => { - class A { - @observable a = 2 - - constructor() { - makeObservable(this) - } - } - - class B extends A { - @observable b = 3 - - constructor() { - super() - makeObservable(this) - } - - @computed - get c() { - return this.a + this.b - } - } - - const b1 = new B() - const b2 = new B() - const values = [] - mobx.autorun(() => values.push(b1.c + b2.c)) - - b1.a = 3 - b1.b = 4 - b2.b = 5 - b2.a = 6 - - expect(values).toEqual([10, 11, 12, 14, 18]) -}) - -test("reusing initializers", () => { - class A { - @observable a = 3 - @observable b = this.a + 2 - - constructor() { - makeObservable(this) - } - - @computed - get c() { - return this.a + this.b - } - @computed - get d() { - return this.c + 1 - } - } - - const a = new A() - const values = [] - mobx.autorun(() => values.push(a.d)) - - a.a = 4 - expect(values).toEqual([9, 10]) -}) - -test("enumerability", () => { - class A { - @observable a = 1 // enumerable, on proto - @observable a2 = 2 - - constructor() { - makeObservable(this) - } - - @computed - get b() { - return this.a - } // non-enumerable, (and, ideally, on proto) - @action - m() {} // non-enumerable, on proto - @action m2 = () => {} // non-enumerable, on self - } - - const a = new A() - - // not initialized yet - let ownProps = Object.keys(a) - let props = [] - for (const key in a) props.push(key) - - expect(ownProps).toEqual(["a", "a2"]) - - expect(props).toEqual(["a", "a2"]) - - expect("a" in a).toBe(true) - expect(a.hasOwnProperty("a")).toBe(true) - expect(a.hasOwnProperty("b")).toBe(true) - expect(a.hasOwnProperty("m")).toBe(false) - expect(a.hasOwnProperty("m2")).toBe(true) - - expect(mobx.isAction(a.m)).toBe(true) - expect(mobx.isAction(a.m2)).toBe(true) - - // after initialization - a.a - a.b - a.m - a.m2 - - ownProps = Object.keys(a) - props = [] - for (const key in a) props.push(key) - - expect(ownProps).toEqual([ - "a", - "a2" // a2 is now initialized as well, altough never accessed! - ]) - - expect(props).toEqual(["a", "a2"]) - - expect("a" in a).toBe(true) - expect(a.hasOwnProperty("a")).toBe(true) - expect(a.hasOwnProperty("a2")).toBe(true) - expect(a.hasOwnProperty("b")).toBe(true) // true would better.. but, #1777 - expect(a.hasOwnProperty("m")).toBe(false) - expect(a.hasOwnProperty("m2")).toBe(true) -}) - -test("enumerability - workaround", () => { - class A { - @observable a = 1 // enumerable, on proto - @observable a2 = 2 - @computed - get b() { - return this.a - } // non-enumerable, (and, ideally, on proto) - @action - m() {} // non-enumerable, on proto - @action m2 = () => {} // non-enumerable, on self - - constructor() { - makeObservable(this) - this.a = 1 - this.a2 = 2 - } - } - - const a = new A() - - const ownProps = Object.keys(a) - const props = [] - for (const key in a) props.push(key) - - expect(ownProps).toEqual([ - "a", - "a2" // a2 is now initialized as well, altough never accessed! - ]) - - expect(props).toEqual(["a", "a2"]) - - expect("a" in a).toBe(true) - expect(a.hasOwnProperty("a")).toBe(true) - expect(a.hasOwnProperty("a2")).toBe(true) - expect(a.hasOwnProperty("b")).toBe(true) // ideally, false, but #1777 - expect(a.hasOwnProperty("m")).toBe(false) - expect(a.hasOwnProperty("m2")).toBe(true) -}) - -test("issue 285 (babel)", () => { - const { observable, toJS } = mobx - - class Todo { - id = 1 - @observable title - @observable finished = false - @observable childThings = [1, 2, 3] - constructor(title) { - makeObservable(this) - this.title = title - } - } - - const todo = new Todo("Something to do") - - expect(toJS(todo)).toEqual({ - id: 1, - title: "Something to do", - finished: false, - childThings: [1, 2, 3] - }) -}) - -test("verify object assign (babel)", () => { - class Todo { - @observable title = "test" - - constructor() { - makeObservable(this) - } - - @computed - get upperCase() { - return this.title.toUpperCase() - } - } - - const todo = new Todo() - expect(Object.assign({}, todo)).toEqual({ - title: "test" - }) -}) - -test("379, inheritable actions (babel)", () => { - class A { - constructor() { - makeObservable(this) - } - - @action - method() { - return 42 - } - } - - class B extends A { - constructor() { - super() - makeObservable(this) - } - - method() { - return super.method() * 2 - } - } - - class C extends B { - constructor() { - super() - makeObservable(this) - } - - method() { - return super.method() + 3 - } - } - - const b = new B() - expect(b.method()).toBe(84) - expect(isAction(b.method)).toBe(true) - - const a = new A() - expect(a.method()).toBe(42) - expect(isAction(a.method)).toBe(true) - - const c = new C() - expect(c.method()).toBe(87) - expect(isAction(c.method)).toBe(true) -}) - -test("505, don't throw when accessing subclass fields in super constructor (babel)", () => { - const values = {} - class A { - @observable a = 1 - constructor() { - makeObservable(this) - values.b = this.b - values.a = this.a - } - } - - class B extends A { - @observable b = 2 - - constructor() { - super() - makeObservable(this) - } - } - - new B() - expect(values).toEqual({ a: 1, b: undefined }) -}) - -test("computed setter should succeed (babel)", function () { - class Bla { - @observable a = 3 - - constructor() { - makeObservable(this) - } - - @computed - get propX() { - return this.a * 2 - } - set propX(v) { - this.a = v - } - } - - const b = new Bla() - expect(b.propX).toBe(6) - b.propX = 4 - expect(b.propX).toBe(8) -}) - -test("issue #701", () => { - class Model { - @observable a = 5 - - constructor() { - makeObservable(this) - } - } - - const model = new Model() - - expect(mobx.toJS(model)).toEqual({ a: 5 }) - expect(mobx.isObservable(model)).toBe(true) - expect(mobx.isObservableObject(model)).toBe(true) -}) - -test("@observable.ref (Babel)", () => { - class A { - @observable.ref ref = { a: 3 } - - constructor() { - makeObservable(this) - } - } - - const a = new A() - expect(a.ref.a).toBe(3) - expect(mobx.isObservable(a.ref)).toBe(false) - expect(mobx.isObservableProp(a, "ref")).toBe(true) -}) - -test("@observable.shallow (Babel)", () => { - class A { - @observable.shallow arr = [{ todo: 1 }] - - constructor() { - makeObservable(this) - } - } - - const a = new A() - const todo2 = { todo: 2 } - a.arr.push(todo2) - expect(mobx.isObservable(a.arr)).toBe(true) - expect(mobx.isObservableProp(a, "arr")).toBe(true) - expect(mobx.isObservable(a.arr[0])).toBe(false) - expect(mobx.isObservable(a.arr[1])).toBe(false) - expect(a.arr[1] === todo2).toBeTruthy() -}) - -test("@observable.deep (Babel)", () => { - class A { - @observable.deep arr = [{ todo: 1 }] - - constructor() { - makeObservable(this) - } - } - - const a = new A() - const todo2 = { todo: 2 } - a.arr.push(todo2) - - expect(mobx.isObservable(a.arr)).toBe(true) - expect(mobx.isObservableProp(a, "arr")).toBe(true) - expect(mobx.isObservable(a.arr[0])).toBe(true) - expect(mobx.isObservable(a.arr[1])).toBe(true) - expect(a.arr[1] !== todo2).toBeTruthy() - expect(isObservable(todo2)).toBe(false) -}) - -test("action.bound binds (Babel)", () => { - class A { - @observable x = 0 - - constructor() { - makeObservable(this) - } - - @action.bound - inc(value) { - this.x += value - } - } - - const a = new A() - const runner = a.inc - runner(2) - - expect(a.x).toBe(2) -}) - -test("@computed.equals (Babel)", () => { - const sameTime = (from, to) => from.hour === to.hour && from.minute === to.minute - class Time { - constructor(hour, minute) { - makeObservable(this) - this.hour = hour - this.minute = minute - } - - @observable hour - @observable minute - - @computed({ equals: sameTime }) - get time() { - return { hour: this.hour, minute: this.minute } - } - } - const time = new Time(9, 0) - - const changes = [] - const disposeAutorun = autorun(() => changes.push(time.time)) - - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 9 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.minute = 0 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 10 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 } - ]) - time.minute = 30 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 }, - { hour: 10, minute: 30 } - ]) - - disposeAutorun() -}) - -// 19.12.2020 @urugator: -// All annotated non-observable fields are not writable. -// All annotated fields of non-plain objects are non-configurable. -// https://github.com/mobxjs/mobx/pull/2641 -test.skip("actions are reassignable", () => { - // See #1398, make actions reassignable to support stubbing - class A { - constructor() { - makeObservable(this) - } - - @action - m1() {} - @action m2 = () => {} - @action.bound - m3() {} - @action.bound m4 = () => {} - } - - const a = new A() - expect(isAction(a.m1)).toBe(true) - expect(isAction(a.m2)).toBe(true) - expect(isAction(a.m3)).toBe(true) - expect(isAction(a.m4)).toBe(true) - a.m1 = () => {} - expect(isAction(a.m1)).toBe(false) - a.m2 = () => {} - expect(isAction(a.m2)).toBe(false) - a.m3 = () => {} - expect(isAction(a.m3)).toBe(false) - a.m4 = () => {} - expect(isAction(a.m4)).toBe(false) -}) - -test("it should support asyncAction (babel)", async () => { - mobx.configure({ enforceActions: "observed" }) - - class X { - @observable a = 1 - - f = mobx.flow(function* f(initial) { - this.a = initial // this runs in action - this.a += yield Promise.resolve(5) - this.a = this.a * 2 - return this.a - }) - - constructor() { - makeObservable(this) - } - } - - const x = new X() - - expect(await x.f(3)).toBe(16) -}) - -test("toJS bug #1413 (babel)", () => { - class X { - @observable - test = { - test1: 1 - } - - constructor() { - makeObservable(this) - } - } - - const x = new X() - const res = mobx.toJS(x.test) - expect(res).toEqual({ test1: 1 }) - expect(res.__mobxDidRunLazyInitializers).toBe(undefined) -}) - -test("computed setter problem", () => { - class Contact { - @observable firstName = "" - @observable lastName = "" - - constructor() { - makeObservable(this) - } - - @computed({ - set(value) { - const [firstName, lastName] = value.split(" ") - - this.firstName = firstName - this.lastName = lastName - } - }) - get fullName() { - return `${this.firstName} ${this.lastName}` - } - - set fullName(value) { - const [firstName, lastName] = value.split(" ") - - this.firstName = firstName - this.lastName = lastName - } - } - - const c = new Contact() - - c.firstName = "Pavan" - c.lastName = "Podila" - - expect(c.fullName).toBe("Pavan Podila") - - c.fullName = "Michel Weststrate" - expect(c.firstName).toBe("Michel") - expect(c.lastName).toBe("Weststrate") -}) - -test("#1740, combining extendObservable & decorators", () => { - class AppState { - constructor(id) { - makeObservable(this) - extendObservable(this, { - id - }) - expect(this.foo).toBe(id) - } - - @computed - get foo() { - return this.id - } - } - - let app = new AppState(1) - expect(app.id).toBe(1) - expect(app.foo).toBe(1) - expect(isObservableProp(app, "id")).toBe(true) - expect(isComputedProp(app, "foo")).toBe(true) - - app = new AppState(2) - expect(app.id).toBe(2) - expect(app.foo).toBe(2) - expect(isObservableProp(app, "id")).toBe(true) - expect(isComputedProp(app, "foo")).toBe(true) -}) diff --git a/packages/mobx/__tests__/base/babel-tests.js b/packages/mobx/__tests__/base/babel-tests.js index 8f7a5d3f99..12185b658c 100644 --- a/packages/mobx/__tests__/base/babel-tests.js +++ b/packages/mobx/__tests__/base/babel-tests.js @@ -1,10 +1,15 @@ import { observable, + observableRef, + observableShallow, + observableDeep, computed, + computedStruct, transaction, autorun, extendObservable, action, + actionBound, isObservableObject, observe, isObservable, @@ -97,7 +102,7 @@ test("babel: parameterized computed decorator", () => { makeObservable(this, { x: observable, y: observable, - boxedSum: computed.struct + boxedSum: computedStruct }) } @@ -134,7 +139,7 @@ test("computed value should be the same around changing which was considered equ constructor() { makeObservable(this, { c: observable, - collection: computed.struct + collection: computedStruct }) } @@ -558,20 +563,14 @@ test.skip("observable performance - babel", () => { console.log("changed in ", Date.now() - start) }) +// Do not remove: slow manual stage-3 decorator benchmark test.skip("observable performance - babel - decorators", () => { const AMOUNT = 100000 class A { - @observable - a = 1 - @observable - b = 2 - @observable - c = 3 - - constructor() { - makeObservable(this) - } + @observable accessor a = 1 + @observable accessor b = 2 + @observable accessor c = 3 @computed get d() { @@ -598,6 +597,7 @@ test.skip("observable performance - babel - decorators", () => { console.log("changed in ", Date.now() - start) }) + test("unbound methods", () => { class A { constructor() { @@ -1029,13 +1029,13 @@ test("issue #701", () => { expect(mobx.isObservableObject(model)).toBe(true) }) -test("@observable.ref (Babel)", () => { +test("@observableRef (Babel)", () => { class A { ref = { a: 3 } constructor() { makeObservable(this, { - ref: observable.ref + ref: observableRef }) } } @@ -1046,13 +1046,13 @@ test("@observable.ref (Babel)", () => { expect(mobx.isObservableProp(a, "ref")).toBe(true) }) -test("@observable.shallow (Babel)", () => { +test("@observableShallow (Babel)", () => { class A { arr = [{ todo: 1 }] constructor() { makeObservable(this, { - arr: observable.shallow + arr: observableShallow }) } } @@ -1067,13 +1067,13 @@ test("@observable.shallow (Babel)", () => { expect(a.arr[1] === todo2).toBeTruthy() }) -test("@observable.deep (Babel)", () => { +test("@observableDeep (Babel)", () => { class A { arr = [{ todo: 1 }] constructor() { makeObservable(this, { - arr: observable.deep + arr: observableDeep }) } } @@ -1097,7 +1097,7 @@ test("action.bound binds (Babel)", () => { constructor() { makeObservable(this, { x: observable, - inc: action.bound + inc: actionBound }) } @@ -1298,8 +1298,8 @@ test.skip("actions are reassignable", () => { makeObservable(this, { m1: action, m2: action, - m3: action.bound, - m4: action.bound + m3: actionBound, + m4: actionBound }) } diff --git a/packages/mobx/__tests__/base/become-observed.ts b/packages/mobx/__tests__/base/become-observed.ts index 8222cdbd06..a8fd6d6741 100644 --- a/packages/mobx/__tests__/base/become-observed.ts +++ b/packages/mobx/__tests__/base/become-observed.ts @@ -2,6 +2,7 @@ import { autorun, onBecomeObserved, observable, + observableRef, computed, action, makeObservable, @@ -26,7 +27,7 @@ test("#2309 don't trigger oBO for computeds that aren't subscribed to", () => { const events: string[] = [] class Asd { - @observable prop = 42 + @observable accessor prop = 42 @computed get computed() { @@ -42,10 +43,6 @@ test("#2309 don't trigger oBO for computeds that aren't subscribed to", () => { actionComputed() { const bar = this.computed } - - constructor() { - makeObservable(this) - } } const asd = new Asd() @@ -308,7 +305,7 @@ describe("nested computes don't trigger hooks #2686", () => { constructor() { makeObservable(this, { upperValue$: computed, - lower$: observable.ref + lower$: observableRef }) } @@ -434,14 +431,11 @@ test("#2686 - 3", () => { test("#2667", () => { const events: any[] = [] class LazyInitializedList { - @observable - public items: string[] | undefined + @observable accessor items: string[] | undefined = undefined - @observable - public listName + @observable accessor listName: string - public constructor(listName: string, lazyItems: string[]) { - makeObservable(this) + constructor(listName: string, lazyItems: string[]) { this.listName = listName onBecomeObserved( this, @@ -463,26 +457,24 @@ test("#2667", () => { } class ItemsStore { - @observable - private list: LazyInitializedList + @observable accessor list: LazyInitializedList - public constructor() { + constructor() { this.list = new LazyInitializedList("initial", ["a, b, c"]) - makeObservable(this) } @action - public changeList = () => { + changeList = () => { this.list = new LazyInitializedList("new", ["b, c, a"]) } @computed - public get items(): string[] | undefined { + get items(): string[] | undefined { return this.list.items } @computed - public get activeListName(): string { + get activeListName(): string { return this.list.listName } } diff --git a/packages/mobx/__tests__/base/decorate.js b/packages/mobx/__tests__/base/decorate.js index 0197f11a03..1a46a3d0ce 100644 --- a/packages/mobx/__tests__/base/decorate.js +++ b/packages/mobx/__tests__/base/decorate.js @@ -3,6 +3,7 @@ import { observable, + observableRef, computed, autorun, action, @@ -58,7 +59,7 @@ test("decorate should work", function () { } constructor() { makeObservable(this, { - uninitialized: observable.ref, + uninitialized: observableRef, height: observable, sizes: observable, someFunc: observable, diff --git a/packages/mobx/__tests__/base/errorhandling.js b/packages/mobx/__tests__/base/errorhandling.js index 0d5a8c377c..68fd14e2b0 100644 --- a/packages/mobx/__tests__/base/errorhandling.js +++ b/packages/mobx/__tests__/base/errorhandling.js @@ -785,69 +785,3 @@ test("error in effect of when is properly cleaned up", () => { checkGlobalState() }) - -describe("es5 compat warnings", () => { - beforeEach(() => { - mobx.configure({ - useProxies: "ifavailable" - }) - }) - - test("adding / deleting property", () => { - const x = observable({ - z: 0 - }) - - expect(() => { - x.y = 2 - }).toThrowErrorMatchingInlineSnapshot( - `"[MobX] MobX is currently configured to be able to run in ES5 mode, but in ES5 MobX won't be able to add a new observable property through direct assignment. Use 'set' from 'mobx' instead."` - ) - - expect(() => { - delete x.z - }).toThrowErrorMatchingInlineSnapshot( - `"[MobX] MobX is currently configured to be able to run in ES5 mode, but in ES5 MobX won't be able to delete properties from an observable object. Use 'remove' from 'mobx' instead."` - ) - }) - - test("iterating props", () => { - const x = observable({ - z: 0 - }) - - expect(() => { - "z" in x - }).not.toThrow() - - let e - autorun(() => { - try { - "z" in x - } catch (err) { - e = err - } - }) - expect(e).toMatchInlineSnapshot( - `[Error: [MobX] MobX is currently configured to be able to run in ES5 mode, but in ES5 MobX won't be able to detect new properties using the 'in' operator. Use 'has' from 'mobx' instead.]` - ) - - e = undefined - - expect(() => { - Object.getOwnPropertyNames(x) - }).not.toThrow() - autorun(() => { - try { - Object.getOwnPropertyNames(x) - } catch (err) { - e = err - } - }) - expect(e).toMatchInlineSnapshot( - `[Error: [MobX] MobX is currently configured to be able to run in ES5 mode, but in ES5 MobX won't be able to iterate keys to detect added / removed properties. Use 'keys' from 'mobx' instead.]` - ) - }) -}) - -test("should throw when adding properties in ES5 compat mode", () => {}) diff --git a/packages/mobx/__tests__/base/extras.js b/packages/mobx/__tests__/base/extras.js index ec22d55a20..528c1b87f9 100644 --- a/packages/mobx/__tests__/base/extras.js +++ b/packages/mobx/__tests__/base/extras.js @@ -482,9 +482,9 @@ test("deepEquals should yield correct results for complex objects #1118 - 1", () expect(d2016jan1).toEqual(d2016jan1_2) expect(d2016jan1).not.toEqual(d2017jan1) - expect(mobx.comparer.structural(d2016jan1, d2016jan1)).toBe(true) - expect(mobx.comparer.structural(d2016jan1, d2017jan1)).toBe(false) - expect(mobx.comparer.structural(d2016jan1, d2016jan1_2)).toBe(true) + expect(mobx.compareStructural(d2016jan1, d2016jan1)).toBe(true) + expect(mobx.compareStructural(d2016jan1, d2017jan1)).toBe(false) + expect(mobx.compareStructural(d2016jan1, d2016jan1_2)).toBe(true) }) test("deepEquals should yield correct results for complex objects #1118 - 2", () => { @@ -505,14 +505,14 @@ test("deepEquals should yield correct results for complex objects #1118 - 2", () expect(a1).toEqual(a2) expect(a1).not.toEqual(a3) - expect(mobx.comparer.structural(a1, a1)).toBe(true) - expect(mobx.comparer.structural(a1, a3)).toBe(false) - expect(mobx.comparer.structural(a1, a2)).toBe(true) - expect(mobx.comparer.structural(a1, a4)).toBe(false) + expect(mobx.compareStructural(a1, a1)).toBe(true) + expect(mobx.compareStructural(a1, a3)).toBe(false) + expect(mobx.compareStructural(a1, a2)).toBe(true) + expect(mobx.compareStructural(a1, a4)).toBe(false) }) -test("comparer.shallow should require types to be equal", () => { - const sh = mobx.comparer.shallow +test("compareShallow should require types to be equal", () => { + const sh = mobx.compareShallow const obs = mobx.observable expect(sh({}, {})).toBe(true) @@ -587,8 +587,8 @@ test("comparer.shallow should require types to be equal", () => { expect(sh(obs(new Map()), obs(new Set()))).toBe(false) expect(sh(obs(new Map()), obs(new Map()))).toBe(true) }) -test("comparer.shallow should work", () => { - const sh = mobx.comparer.shallow +test("compareShallow should work", () => { + const sh = mobx.compareShallow const obs = mobx.observable expect(sh(1, 1)).toBe(true) @@ -894,9 +894,6 @@ test("Default debug names - development", () => { /ObservableObject@\d+.x/.test(mobx.getDebugName(mobx.observable({ get x() {} }), "x")) ).toBe(true) expect(/ObservableArray@\d+/.test(mobx.getDebugName(mobx.observable([])))).toBe(true) - expect( - /ObservableArray@\d+/.test(mobx.getDebugName(mobx.observable([], { proxy: false }))) - ).toBe(true) expect(/ObservableMap@\d+/.test(mobx.getDebugName(mobx.observable(new Map())))).toBe(true) expect(/ObservableSet@\d+/.test(mobx.getDebugName(mobx.observable(new Set())))).toBe(true) expect(/ObservableValue@\d+/.test(mobx.getDebugName(mobx.observable("x")))).toBe(true) @@ -924,7 +921,6 @@ test("Default debug names - production", () => { expect(mobx.getDebugName(mobx.observable({ x: "x" }), "x")).toBe("ObservableObject.key") expect(mobx.getDebugName(mobx.observable({ get x() {} }), "x")).toBe("ObservableObject.key") expect(mobx.getDebugName(mobx.observable([]))).toBe("ObservableArray") - expect(mobx.getDebugName(mobx.observable([], { proxy: false }))).toBe("ObservableArray") expect(mobx.getDebugName(mobx.observable(new Map()))).toBe("ObservableMap") expect(mobx.getDebugName(mobx.observable(new Set()))).toBe("ObservableSet") expect(mobx.getDebugName(mobx.observable("x"))).toBe("ObservableValue") @@ -951,7 +947,6 @@ test("User provided debug names are always respected", () => { expect(mobx.getDebugName(mobx.computed(() => {}, { name }))).toBe(name) expect(mobx.getDebugName(mobx.observable({}, {}, { name }))).toBe(name) expect(mobx.getDebugName(mobx.observable([], { name }))).toBe(name) - expect(mobx.getDebugName(mobx.observable([], { name, proxy: false }))).toBe(name) expect(mobx.getDebugName(mobx.observable(new Map(), { name }))).toBe(name) expect(mobx.getDebugName(mobx.observable(new Set(), { name }))).toBe(name) expect(mobx.getDebugName(mobx.observable("x", { name }))).toBe(name) diff --git a/packages/mobx/__tests__/base/make-observable.ts b/packages/mobx/__tests__/base/make-observable.ts index db04237bd2..cd861e832c 100644 --- a/packages/mobx/__tests__/base/make-observable.ts +++ b/packages/mobx/__tests__/base/make-observable.ts @@ -3,8 +3,11 @@ import { deepEnhancer } from "../../src/internal" import { makeObservable, action, + actionBound, computed, observable, + observableRef, + observableShallow, isObservable, isObservableObject, isObservableProp, @@ -17,46 +20,12 @@ import { _getAdministration, configure, flow, + flowBound, override, ObservableSet, ObservableMap } from "../../src/mobx" -test("makeObservable picks up decorators", () => { - class Test { - @observable x = 3 - y = 3 - - @computed - get double() { - return this.x * 2 - } - - @action - unbound() { - return this - } - - @action.bound bound() { - return this - } - - constructor() { - makeObservable(this) - } - } - - const t = new Test() - expect(isObservableObject(t)).toBe(true) - expect(isObservableProp(t, "x")).toBe(true) - expect(isObservableProp(t, "y")).toBe(false) - expect(isComputedProp(t, "double")).toBe(true) - expect(isAction(t.unbound)).toBe(true) - expect(isAction(t.bound)).toBe(true) - expect(t.unbound.call(undefined)).toBe(undefined) - expect(t.bound.call(undefined)).toBe(t) -}) - test("makeObservable picks up annotations", () => { class Test { x = 3 @@ -79,7 +48,7 @@ test("makeObservable picks up annotations", () => { x: observable, double: computed, unbound: action, - bound: action.bound + bound: actionBound }) } } @@ -118,7 +87,7 @@ test("makeObservable supports private fields", () => { x: observable, double: computed, unbound: action, - bound: action.bound + bound: actionBound }) if (3 - 1 === 4) { makeObservable(this, { @@ -169,7 +138,7 @@ test("makeObservable has sane defaults", () => { y: false, double: true, unbound: true, - bound: action.bound + bound: actionBound }) if (3 - 1 === 4) { makeObservable(this, { @@ -353,7 +322,7 @@ test("makeAutoObservable allows overrides", () => { constructor() { makeAutoObservable(this, { unbound: true, - bound: action.bound, + bound: actionBound, y: false }) if (3 - 1 === 4) { @@ -563,21 +532,23 @@ test("makeObservable doesn't trigger in always mode'", () => { test("#2457", () => { class BaseClass { - @observable - value1?: number + value1: number | undefined = undefined constructor() { - makeObservable(this) + makeObservable(this, { + value1: observable + }) } } class SubClass extends BaseClass { constructor() { super() - makeObservable(this) + makeObservable(this, { + value1Computed: computed + }) } - @computed get value1Computed() { return this.value1 } @@ -617,7 +588,7 @@ test("makeObservable respects options.name #2614'", () => { expect(getDebugName(instance)).toBe(name) expect(getDebugName(plain)).toBe(name) }) -// "makeObservable + @action + arrow function + subclass override #2614" +// "makeObservable + action + arrow function + subclass override #2614" test("class - annotations", async () => { class Foo { @@ -628,13 +599,13 @@ test("class - annotations", async () => { constructor() { makeObservable(this, { observable: observable, - "observable.ref": observable.ref, - "observable.shallow": observable.shallow, + "observable.ref": observableRef, + "observable.shallow": observableShallow, computed: computed, action: action, - "action.bound": action.bound, + "action.bound": actionBound, flow: flow, - "flow.bound": flow.bound + "flow.bound": flowBound }) } @@ -695,81 +666,6 @@ test("class - annotations", async () => { expect(await foo["flow.bound"].call(null)).toBe(foo) }) -test("class - decorators", async () => { - class Foo { - @observable - ["observable"] = { nested: {} }; - @observable.ref - ["observable.ref"] = { nested: {} }; - @observable.shallow - ["observable.shallow"] = { nested: {} } - - constructor() { - makeObservable(this) - } - - @computed - get computed() { - return this - } - - @action - ["action"]() { - return this - } - - @action.bound - ["action.bound"]() { - return this - } - - @flow - *["flow"]() { - return this - } - - @flow.bound - *["flow.bound"]() { - return this - } - } - - const foo = new Foo() - expect(isObservableObject(foo)).toBe(true) - - expect(isObservableProp(foo, "observable")).toBe(true) - expect(isObservableObject(foo["observable"])).toBe(true) - - expect(isObservableProp(foo, "observable.ref")).toBe(true) - expect(isObservableObject(foo["observable.ref"])).toBe(false) - expect(isObservableObject(foo["observable.ref"].nested)).toBe(false) - - expect(isObservableProp(foo, "observable.shallow")).toBe(true) - expect(isObservableObject(foo["observable.shallow"])).toBe(true) - expect(isObservableObject(foo["observable.shallow"].nested)).toBe(false) - - expect(isComputedProp(foo, "computed")).toBe(true) - - expect(isAction(foo["action"])).toBe(true) - expect(Object.getPrototypeOf(foo).hasOwnProperty("action")).toBe(true) - expect(foo.hasOwnProperty("action")).toBe(false) - expect(foo["action"].call(null)).toBe(null) - - expect(isAction(foo["action.bound"])).toBe(true) - expect(Object.getPrototypeOf(foo).hasOwnProperty("action.bound")).toBe(true) - expect(foo.hasOwnProperty("action.bound")).toBe(true) - expect(foo["action.bound"].call(null)).toBe(foo) - - expect(isFlow(foo["flow"])).toBe(true) - expect(Object.getPrototypeOf(foo).hasOwnProperty("flow")).toBe(true) - expect(foo.hasOwnProperty("flow")).toBe(false) - - expect(isFlow(foo["flow.bound"])).toBe(true) - expect(Object.getPrototypeOf(foo).hasOwnProperty("flow.bound")).toBe(true) - expect(foo.hasOwnProperty("flow.bound")).toBe(true) - expect(await foo["flow.bound"].call(null)).toBe(foo) -}) - test("subclass - annotation", () => { class Parent { ["observable"] = { nested: {} }; @@ -779,11 +675,11 @@ test("subclass - annotation", () => { constructor() { makeObservable(this, { observable: observable, - "observable.ref": observable.ref, - "observable.shallow": observable.shallow, + "observable.ref": observableRef, + "observable.shallow": observableShallow, computed: computed, action: action, - "action.bound": action.bound, + "action.bound": actionBound, flow: flow }) } @@ -814,11 +710,11 @@ test("subclass - annotation", () => { super() makeObservable(this, { observable2: observable, - "observable.ref2": observable.ref, - "observable.shallow2": observable.shallow, + "observable.ref2": observableRef, + "observable.shallow2": observableShallow, computed2: computed, action2: action, - "action.bound2": action.bound, + "action.bound2": actionBound, flow2: flow }) } @@ -898,141 +794,15 @@ test("subclass - annotation", () => { expect(child.hasOwnProperty("flow2")).toBe(false) }) -test("subclass - decorator", () => { - class Parent { - @observable - ["observable"] = { nested: {} }; - @observable.ref - ["observable.ref"] = { nested: {} }; - @observable.shallow - ["observable.shallow"] = { nested: {} } - - constructor() { - makeObservable(this) - } - - @computed - get computed() { - return this - } - - @action - ["action"]() { - return this - } - - @action.bound - ["action.bound"]() { - return this - } - - @flow - *["flow"]() { - return this - } - } - - class Child extends Parent { - @observable - ["observable2"] = { nested: {} }; - @observable.ref - ["observable.ref2"] = { nested: {} }; - @observable.shallow - ["observable.shallow2"] = { nested: {} } - - constructor() { - super() - makeObservable(this) - } - - @computed - get computed2() { - return this - } - - @action - ["action2"]() { - return this - } - - @action.bound - ["action.bound2"]() { - return this - } - - @flow - *["flow2"]() { - return this - } - } - - const child = new Child() - expect(isObservableObject(child)).toBe(true) - - expect(isObservableProp(child, "observable")).toBe(true) - expect(isObservableObject(child["observable"])).toBe(true) - - expect(isObservableProp(child, "observable.ref")).toBe(true) - expect(isObservableObject(child["observable.ref"])).toBe(false) - expect(isObservableObject(child["observable.ref"].nested)).toBe(false) - - expect(isObservableProp(child, "observable.shallow")).toBe(true) - expect(isObservableObject(child["observable.shallow"])).toBe(true) - expect(isObservableObject(child["observable.shallow"].nested)).toBe(false) - - expect(isComputedProp(child, "computed")).toBe(true) - - expect(isAction(child["action"])).toBe(true) - expect(Object.getPrototypeOf(child).hasOwnProperty("action")).toBe(false) - expect(child.hasOwnProperty("action")).toBe(false) - expect(child["action"].call(null)).toBe(null) - - expect(isAction(child["action.bound"])).toBe(true) - expect(Object.getPrototypeOf(child).hasOwnProperty("action.bound")).toBe(false) - expect(child.hasOwnProperty("action.bound")).toBe(true) - expect(child["action.bound"].call(null)).toBe(child) - - expect(isFlow(child["flow"])).toBe(true) - expect(Object.getPrototypeOf(child).hasOwnProperty("flow")).toBe(false) - expect(child.hasOwnProperty("flow")).toBe(false) - - expect(isObservableProp(child, "observable2")).toBe(true) - expect(isObservableObject(child["observable2"])).toBe(true) - - expect(isObservableProp(child, "observable.ref2")).toBe(true) - expect(isObservableObject(child["observable.ref2"])).toBe(false) - expect(isObservableObject(child["observable.ref2"].nested)).toBe(false) - - expect(isObservableProp(child, "observable.shallow2")).toBe(true) - expect(isObservableObject(child["observable.shallow2"])).toBe(true) - expect(isObservableObject(child["observable.shallow2"].nested)).toBe(false) - - expect(isComputedProp(child, "computed2")).toBe(true) - - expect(isAction(child["action2"])).toBe(true) - expect(Object.getPrototypeOf(child).hasOwnProperty("action2")).toBe(true) - expect(child.hasOwnProperty("action2")).toBe(false) - expect(child["action2"].call(null)).toBe(null) - - expect(isAction(child["action.bound2"])).toBe(true) - expect(Object.getPrototypeOf(child).hasOwnProperty("action.bound2")).toBe(true) - expect(child.hasOwnProperty("action.bound2")).toBe(true) - expect(child["action.bound2"].call(null)).toBe(child) - - expect(isFlow(child["flow2"])).toBe(true) - expect(Object.getPrototypeOf(child).hasOwnProperty("flow2")).toBe(true) - expect(child.hasOwnProperty("flow2")).toBe(false) -}) - test("subclass - annotation - override", async () => { class Parent { constructor() { makeObservable(this, { action: action, - ["action.bound"]: action.bound, + ["action.bound"]: actionBound, computed: computed, flow: flow, - ["flow.bound"]: flow.bound + ["flow.bound"]: flowBound }) } action() { @@ -1122,103 +892,6 @@ test("subclass - annotation - override", async () => { expect(await child["flow.bound"]()).toBe("child of parent") }) -test("subclass - decorator - override", async () => { - class Parent { - constructor() { - makeObservable(this) - } - @action - action() { - return "parent" - } - @action.bound - ["action.bound"]() { - return "parent" - } - @flow - *flow() { - return "parent" - } - @flow.bound - *["flow.bound"]() { - return "parent" - } - @computed - get computed() { - return "parent" - } - } - - class Child extends Parent { - action() { - return "child of " + super.action() - } - ["action.bound"]() { - return "child of " + super["action.bound"]() - } - get computed() { - return "child" - } - *flow(): any { - const parent = yield super.flow() - return "child of " + parent - } - *["flow.bound"](): any { - const parent = yield super["flow.bound"]() - return "child of " + parent - } - } - const child = new Child() - - // Action - expect(isAction(Parent.prototype.action)).toBe(true) - expect(isAction(Child.prototype.action)).toBe(true) - expect(isAction(child.action)).toBe(true) - - expect(child.hasOwnProperty("action")).toBe(false) - - expect(Parent.prototype.action()).toBe("parent") - expect(Child.prototype.action()).toBe("child of parent") - expect(child.action()).toBe("child of parent") - - // Action bound - expect(isAction(Parent.prototype["action.bound"])).toBe(false) - expect(isAction(Child.prototype["action.bound"])).toBe(false) - expect(isAction(child["action.bound"])).toBe(true) - - expect(child.hasOwnProperty("action.bound")).toBe(true) - - expect(Parent.prototype["action.bound"]()).toBe("parent") - expect(Child.prototype["action.bound"]()).toBe("child of parent") - expect(child["action.bound"]()).toBe("child of parent") - - // Computed - expect(isComputedProp(child, "computed")).toBe(true) - expect(child.computed).toBe("child") - - // Flow - expect(isFlow(Parent.prototype.flow)).toBe(true) - expect(isFlow(Child.prototype.flow)).toBe(true) - expect(isFlow(child.flow)).toBe(true) - - expect(child.hasOwnProperty("flow")).toBe(false) - - expect(await Parent.prototype.flow()).toBe("parent") - expect(await Child.prototype.flow()).toBe("child of parent") - expect(await child.flow()).toBe("child of parent") - - // Flow bound - expect(isFlow(Parent.prototype["flow.bound"])).toBe(true) - expect(isFlow(Child.prototype["flow.bound"])).toBe(true) - expect(isFlow(child["flow.bound"])).toBe(true) - - expect(child.hasOwnProperty("flow.bound")).toBe(true) - - expect(await Parent.prototype["flow.bound"]()).toBe("parent") - expect(await Child.prototype["flow.bound"]()).toBe("child of parent") - expect(await child["flow.bound"]()).toBe("child of parent") -}) - test("subclass - cannot re-annotate", () => { class Parent { observable = 1 @@ -1226,9 +899,9 @@ test("subclass - cannot re-annotate", () => { makeObservable(this, { action: action, observable: observable, - actionBound: action.bound, + actionBound: actionBound, flow: flow, - flowBound: flow.bound, + flowBound: flowBound, computed: computed }) } @@ -1255,7 +928,7 @@ test("subclass - cannot re-annotate", () => { constructor() { super() makeObservable(this, { - actionBound: action.bound + actionBound: actionBound }) } actionBound() {} @@ -1275,7 +948,7 @@ test("subclass - cannot re-annotate", () => { constructor() { super() makeObservable(this, { - flowBound: flow.bound + flowBound: flowBound }) } *flowBound() {} @@ -1311,96 +984,6 @@ test("subclass - cannot re-annotate", () => { expect(() => new ChildComputed()).toThrow(/^\[MobX\] Cannot apply/) }) -test("subclass - cannot re-decorate", () => { - class Parent { - @observable - observable = 1 - constructor() { - makeObservable(this) - } - @action - action() {} - @action.bound - actionBound() {} - @flow - *flow() {} - @flow.bound - *flowBound() {} - @computed - get computed() { - return this - } - } - - expect(() => { - class ChildAction extends Parent { - constructor() { - super() - makeObservable(this) - } - @action - action() {} - } - }).toThrow(/^\[MobX\] Cannot apply/) - - expect(() => { - class ChildActionBound extends Parent { - constructor() { - super() - makeObservable(this) - } - @action.bound - actionBound() {} - } - }).toThrow(/^\[MobX\] Cannot apply/) - - expect(() => { - class ChildFlow extends Parent { - constructor() { - super() - makeObservable(this) - } - @flow - *flow() {} - } - }).toThrow(/^\[MobX\] Cannot apply/) - - expect(() => { - class ChildFlowBound extends Parent { - constructor() { - super() - makeObservable(this) - } - @flow.bound - *flowBound() {} - } - }).toThrow(/^\[MobX\] Cannot apply/) - - expect(() => { - class ChildObservable extends Parent { - @observable - observable = 1 - constructor() { - super() - makeObservable(this) - } - } - }).toThrow(/^\[MobX\] Cannot apply/) - - expect(() => { - class ChildComputed extends Parent { - constructor() { - super() - makeObservable(this) - } - @computed - get computed() { - return this - } - } - }).toThrow(/^\[MobX\] Cannot apply/) -}) - test("subclass - cannot redefine property", () => { class Parent { observable = 1 @@ -1435,33 +1018,6 @@ test("subclass - cannot redefine property", () => { expect(() => new ChildComputed()).toThrow(/^Cannot redefine property/) }) -test("@override", () => { - class Parent { - constructor() { - makeObservable(this) - } - - @action - action() { - return "parent" - } - } - - class Child extends Parent { - @override - action() { - return `child of ${super.action()}` - } - } - - const child = new Child() - expect(isAction(Parent.prototype.action)) - expect(Parent.prototype.action()).toBe("parent") - expect(isAction(Child.prototype.action)) - expect(isAction(child.action)).toBe(true) - expect(child.action()).toBe("child of parent") -}) - test("override", () => { class Parent { constructor() { @@ -1519,29 +1075,6 @@ test("override must override", () => { ) }) -test("@override must override", () => { - class Parent { - action() { - return "parent" - } - } - - expect(() => { - class Child extends Parent { - constructor() { - super() - makeObservable(this) - } - @override - action() { - return `child of ${super.action()}` - } - } - }).toThrow( - /^\[MobX\] 'Child\.prototype\.action' is decorated with 'override', but no such decorated member was found on prototype\./ - ) -}) - test("makeAutoObservable + production build #2751", () => { const mobx = require(`../../dist/mobx.cjs.production.min.js`) class Foo { @@ -1613,7 +1146,7 @@ test("makeAutoObservable + override + annotation cache #2832", () => { override = [] constructor() { makeAutoObservable(this, { - override: observable.ref + override: observableRef }) } } @@ -1630,7 +1163,7 @@ test("flow.bound #2941", async () => { class Clazz { constructor() { makeObservable(this, { - flowBound: flow.bound + flowBound: flowBound }) } *flowBound() { @@ -1643,20 +1176,6 @@ test("flow.bound #2941", async () => { expect(await Clazz.prototype.flowBound.call("ctx")).toBe("ctx") }) -test("makeObservable throws when mixing @decorators with annotations", () => { - class Test { - @observable x = 3 - - constructor() { - makeObservable(this, {}) - } - } - - expect(() => new Test()).toThrow( - /makeObservable second arg must be nullish when using decorators/ - ) -}) - test("makeAutoObservable + Object.create #3197", () => { const proto = { action() {}, @@ -1677,7 +1196,7 @@ test("makeAutoObservable + Object.create #3197", () => { test("flow.bound #3271", async () => { class Test { constructor() { - makeObservable(this, { flowBound: flow.bound }) + makeObservable(this, { flowBound: flowBound }) } *flowBound() { return this diff --git a/packages/mobx/__tests__/base/makereactive.js b/packages/mobx/__tests__/base/makereactive.js index 917427f099..e81f9052ea 100644 --- a/packages/mobx/__tests__/base/makereactive.js +++ b/packages/mobx/__tests__/base/makereactive.js @@ -99,7 +99,7 @@ test("observable1", function () { } }, { - a: m.observable.ref + a: m.observableRef } ) @@ -256,7 +256,7 @@ test("flat array", function () { } ] }, - { x: m.observable.shallow } + { x: m.observableShallow } ) let result @@ -324,7 +324,7 @@ test("as structure", function () { x: null }, { - x: m.observable.struct + x: m.observableStruct } ) @@ -429,7 +429,7 @@ test("as structure view", function () { } }, { - c: m.computed({ compareStructural: true }) + c: m.computed({ equals: m.compareStructural }) } ) @@ -543,7 +543,7 @@ test("761 - deeply nested modifiers work", () => { someNestedKey: [] }, { - someNestedKey: mobx.observable.ref + someNestedKey: mobx.observableRef } ) }) @@ -570,7 +570,7 @@ test("compare structurally, ref", () => { x: undefined }, { - x: mobx.observable.struct + x: mobx.observableStruct } ) @@ -608,7 +608,7 @@ test("double declare property", () => { o, { a: 2 }, { - a: mobx.observable.ref + a: mobx.observableRef } ) }).toThrow(/The field is already annotated/) @@ -620,7 +620,7 @@ test("structural collections", () => { x: [1, 2, 3] }, { - x: mobx.observable.struct + x: mobx.observableStruct } ) diff --git a/packages/mobx/__tests__/base/map.js b/packages/mobx/__tests__/base/map.js index be93a55716..dc4c294d2f 100644 --- a/packages/mobx/__tests__/base/map.js +++ b/packages/mobx/__tests__/base/map.js @@ -507,16 +507,16 @@ test("deepEqual map", () => { x2.set("x", 3) x2.set("y", { z: 3 }) - expect(mobx.comparer.structural(x, x2)).toBe(false) + expect(mobx.compareStructural(x, x2)).toBe(false) x2.get("y").z = 2 - expect(mobx.comparer.structural(x, x2)).toBe(true) + expect(mobx.compareStructural(x, x2)).toBe(true) x2.set("z", 1) - expect(mobx.comparer.structural(x, x2)).toBe(false) + expect(mobx.compareStructural(x, x2)).toBe(false) x2.delete("z") - expect(mobx.comparer.structural(x, x2)).toBe(true) + expect(mobx.compareStructural(x, x2)).toBe(true) x2.delete("y") - expect(mobx.comparer.structural(x, x2)).toBe(false) + expect(mobx.compareStructural(x, x2)).toBe(false) }) test("798, cannot return observable map from computed prop", () => { @@ -1360,12 +1360,14 @@ test("2346 - subscribe to not yet existing map keys", async () => { class Compute { values = observable.map() - @computed get get42() { + get get42() { return this.get(42) } constructor() { - makeObservable(this) + makeObservable(this, { + get42: computed + }) } get(k) { diff --git a/packages/mobx/__tests__/base/observables.js b/packages/mobx/__tests__/base/observables.js index efaf71fead..b30ba4f64e 100644 --- a/packages/mobx/__tests__/base/observables.js +++ b/packages/mobx/__tests__/base/observables.js @@ -6,6 +6,7 @@ const { $mobx, observable, computed, + compareStructural, transaction, autorun, extendObservable, @@ -85,7 +86,7 @@ test("computed with asStructure modifier", function () { sum: x1.get() + x2.get() } }, - { compareStructural: true } + { equals: compareStructural } ) const b = buffer() m.observe(y, b, true) @@ -1071,7 +1072,7 @@ test("computed values believe deep NaN === deep NaN when using compareStructural function () { return a.b }, - { compareStructural: true } + { equals: compareStructural } ) const buf = new buffer() @@ -2390,10 +2391,8 @@ test('Observables initialization does not violate `enforceActions: "always"`', ( check(() => mobx.extendObservable({}, { x: 0 })) check(() => mobx.observable(new Set([0]))) check(() => mobx.observable(new Map([[0, 0]]))) - check(() => mobx.observable({ x: 0 }, { proxy: false })) - check(() => mobx.observable({ x: 0 }, { proxy: true })) - check(() => mobx.observable([0], { proxy: false })) - check(() => mobx.observable([0], { proxy: true })) + check(() => mobx.observable({ x: 0 })) + check(() => mobx.observable([0])) check(() => mobx.computed(() => 0)) } finally { consoleWarnSpy.mockRestore() @@ -2459,10 +2458,8 @@ test("state version does not update on observable creation", () => { check(() => mobx.extendObservable({}, { x: 0 })) check(() => mobx.observable(new Set([0]))) check(() => mobx.observable(new Map([[0, 0]]))) - check(() => mobx.observable({ x: 0 }, { proxy: false })) - check(() => mobx.observable({ x: 0 }, { proxy: true })) - check(() => mobx.observable([0], { proxy: false })) - check(() => mobx.observable([0], { proxy: true })) + check(() => mobx.observable({ x: 0 })) + check(() => mobx.observable([0])) check(() => mobx.computed(() => 0)) }) diff --git a/packages/mobx/__tests__/base/proxies.js b/packages/mobx/__tests__/base/proxies.js index c5a8a1e93a..f548e567f5 100644 --- a/packages/mobx/__tests__/base/proxies.js +++ b/packages/mobx/__tests__/base/proxies.js @@ -6,6 +6,7 @@ import { autorun, observable, action, + actionBound, reaction, extendObservable, keys, @@ -212,13 +213,6 @@ test("adding a different key doesn't trigger a pending key", () => { d() }) -test("proxy false reverts to original behavior", () => { - const x = observable({ x: 3 }, {}, { proxy: false }) - x.y = 3 - expect(isObservableProp(x, "x")).toBe(true) - expect(isObservableProp(x, "y")).toBe(false) -}) - test("ownKeys invariant not broken - 1", () => { const a = observable({ x: 3, get y() {} }) expect(() => { @@ -233,33 +227,6 @@ test("ownKeys invariant not broken - 2", () => { }).toThrow("cannot be frozen") }) -test("non-proxied object", () => { - const a = observable({ x: 3 }, {}, { proxy: false }) - a.b = 4 - extendObservable( - a, - { - double() { - this.x = this.x * 2 - }, - get y() { - return this.x * 2 - } - }, - { - double: action - } - ) - - expect(a.y).toBe(6) - a.double() - expect(a.y).toBe(12) - expect(isComputedProp(a, "y")).toBe(true) - expect(isAction(a.double)).toBe(true) - expect(stripAdminFromDescriptors(Object.getOwnPropertyDescriptors(a))).toMatchSnapshot() - expect(Object.keys(a)).toEqual(["x", "b"]) -}) - test("extend proxies", () => { const a = observable({ x: 3 }) a.b = 4 @@ -332,7 +299,7 @@ test("predictable 'this' - 1", () => { }, { a1: action, - a2: action.bound + a2: actionBound } ) @@ -347,7 +314,7 @@ test("predictable 'this' - 2", () => { constructor() { makeObservable(this, { a1: action, - a2: action.bound, + a2: actionBound, computed: computed }) } diff --git a/packages/mobx/__tests__/base/reaction.js b/packages/mobx/__tests__/base/reaction.js index 8d49399572..792609f359 100644 --- a/packages/mobx/__tests__/base/reaction.js +++ b/packages/mobx/__tests__/base/reaction.js @@ -353,7 +353,7 @@ test("#278 do not rerun if expr output doesn't change structurally", () => { }, { fireImmediately: true, - compareStructural: true + equals: mobx.compareStructural } ) @@ -389,7 +389,7 @@ test("do not rerun if prev & next expr output is NaN", () => { newValue => { valuesS.push(String(newValue)) }, - { fireImmediately: true, compareStructural: true } + { fireImmediately: true, equals: mobx.compareStructural } ) v.set(NaN) diff --git a/packages/mobx/__tests__/base/set.js b/packages/mobx/__tests__/base/set.js index 45d893547b..408116f0b8 100644 --- a/packages/mobx/__tests__/base/set.js +++ b/packages/mobx/__tests__/base/set.js @@ -248,9 +248,9 @@ test("deepEqual set", () => { x2.add(1) x2.add({ z: 2 }) - expect(mobx.comparer.structural(x, x2)).toBe(false) + expect(mobx.compareStructural(x, x2)).toBe(false) x2.replace([1, { z: 1 }]) - expect(mobx.comparer.structural(x, x2)).toBe(true) + expect(mobx.compareStructural(x, x2)).toBe(true) }) test("set.clear should not be tracked", () => { diff --git a/packages/mobx/__tests__/base/spy.js b/packages/mobx/__tests__/base/spy.js index 908fd4ed41..d837dc7c96 100644 --- a/packages/mobx/__tests__/base/spy.js +++ b/packages/mobx/__tests__/base/spy.js @@ -114,7 +114,7 @@ test("bound actions report correct object (discussions/3140)", () => { mobx.makeAutoObservable( this, { - actionBound: mobx.action.bound + actionBound: mobx.actionBound }, { autoBind: true } ) diff --git a/packages/mobx/__tests__/decorators_20223/stage3-decorators-inheritance.ts b/packages/mobx/__tests__/base/stage3-decorators-inheritance.ts similarity index 98% rename from packages/mobx/__tests__/decorators_20223/stage3-decorators-inheritance.ts rename to packages/mobx/__tests__/base/stage3-decorators-inheritance.ts index bbe58b32b5..1e426d1d22 100644 --- a/packages/mobx/__tests__/decorators_20223/stage3-decorators-inheritance.ts +++ b/packages/mobx/__tests__/base/stage3-decorators-inheritance.ts @@ -1,17 +1,14 @@ import { - action, autorun, - computed, - flow, flowResult, isAction, isComputedProp, isFlow, isObservableProp, - observable, observe, runInAction } from "../../src/mobx" +import { action, actionBound, computed, flow, observable } from "../../src/mobx" test("inherited observable accessor remains reactive in subclass", () => { class Parent { @@ -193,7 +190,7 @@ test("subclass can add new observable, computed, and action members", () => { this.extra += 1 } - @action.bound + @actionBound incrementCount() { this.count += 1 } @@ -248,14 +245,14 @@ test("action.bound override can be called after extraction", () => { class Parent { @observable accessor count = 0 - @action.bound + @actionBound increment(value: number) { this.count += value } } class Child extends Parent { - @action.bound + @actionBound override increment(value: number) { super.increment(value) this.count += 1 diff --git a/packages/mobx/__tests__/decorators_20223/stage3-decorators.ts b/packages/mobx/__tests__/base/stage3-decorators.ts similarity index 97% rename from packages/mobx/__tests__/decorators_20223/stage3-decorators.ts rename to packages/mobx/__tests__/base/stage3-decorators.ts index f24cb07e12..7cd5b3d10f 100644 --- a/packages/mobx/__tests__/decorators_20223/stage3-decorators.ts +++ b/packages/mobx/__tests__/base/stage3-decorators.ts @@ -2,11 +2,8 @@ import { observe, - computed, - observable, autorun, extendObservable, - action, IObservableArray, IArrayWillChange, IArrayWillSplice, @@ -22,9 +19,19 @@ import { IAtom, createAtom, runInAction, - makeObservable, isComputedProp } from "../../src/mobx" +import { + action, + actionBound, + computed, + computedStruct, + flow, + observable, + observableDeep, + observableRef, + observableShallow +} from "../../src/mobx" import { $mobx, type ObservableArrayAdministration } from "../../src/internal" import * as mobx from "../../src/mobx" @@ -239,7 +246,7 @@ test("typescript: parameterized computed decorator", () => { class TestClass { @observable accessor x = 3 @observable accessor y = 3 - @computed.struct + @computedStruct get boxedSum() { return { sum: Math.round(this.x) + Math.round(this.y) } } @@ -272,9 +279,7 @@ test("issue 165", () => { } class Card { - constructor(public game: Game, public id: number) { - makeObservable(this) - } + constructor(public game: Game, public id: number) {} @computed get isWrong() { @@ -602,10 +607,6 @@ test("inheritance", () => { get c() { return this.a + this.b } - constructor() { - super() - makeObservable(this) - } } const b1 = new B() const b2 = new B() @@ -775,7 +776,6 @@ test("373 - fix isObservable for unused computed", () => { return 3 } constructor() { - makeObservable(this) t.equal(isObservableProp(this, "computedVal"), true) this.computedVal t.equal(isObservableProp(this, "computedVal"), true) @@ -836,9 +836,9 @@ test("705 - setter undoing caching (2022.3)", () => { d2() }) -test("@observable.ref (2022.3)", () => { +test("@observableRef (2022.3)", () => { class A { - @observable.ref accessor ref = { a: 3 } + @observableRef accessor ref = { a: 3 } } const a = new A() @@ -847,9 +847,9 @@ test("@observable.ref (2022.3)", () => { t.equal(mobx.isObservableProp(a, "ref"), true) }) -test("@observable.shallow (2022.3)", () => { +test("@observableShallow (2022.3)", () => { class A { - @observable.shallow accessor arr = [{ todo: 1 }] + @observableShallow accessor arr = [{ todo: 1 }] } const a = new A() @@ -862,9 +862,9 @@ test("@observable.shallow (2022.3)", () => { t.equal(a.arr[1] === todo2, true) }) -test("@observable.shallow - 2 (2022.3)", () => { +test("@observableShallow - 2 (2022.3)", () => { class A { - @observable.shallow accessor arr: Record = { x: { todo: 1 } } + @observableShallow accessor arr: Record = { x: { todo: 1 } } } const a = new A() @@ -877,9 +877,9 @@ test("@observable.shallow - 2 (2022.3)", () => { t.equal(a.arr.y === todo2, true) }) -test("@observable.deep (2022.3)", () => { +test("@observableDeep (2022.3)", () => { class A { - @observable.deep accessor arr = [{ todo: 1 }] + @observableDeep accessor arr = [{ todo: 1 }] } const a = new A() @@ -897,7 +897,7 @@ test("@observable.deep (2022.3)", () => { test("action.bound binds (2022.3)", () => { class A { @observable accessor x = 0 - @action.bound + @actionBound inc(value: number) { this.x += value } @@ -913,7 +913,7 @@ test("action.bound binds (2022.3)", () => { test("action.bound binds property function (2022.3)", () => { class A { @observable accessor x = 0 - @action.bound + @actionBound inc = function (value: number) { this.x += value } @@ -930,7 +930,6 @@ test("@computed.equals (2022.3)", () => { const sameTime = (from: Time, to: Time) => from.hour === to.hour && from.minute === to.minute class Time { constructor(hour: number, minute: number) { - makeObservable(this) this.hour = hour this.minute = minute } @@ -1023,11 +1022,6 @@ test("multiple inheritance should work", () => { class B extends A { @observable accessor y = 1 - - constructor() { - super() - makeObservable(this) - } } const adm = mobx._getAdministration(new B()) as any @@ -1046,7 +1040,7 @@ test.skip("actions are reassignable", () => { class A { @action m1() {} - @action.bound + @actionBound m3() {} } @@ -1080,14 +1074,14 @@ test("it should support asyncAction as decorator (2022.3)", async () => { test("it should support flow as decorator (2022.3)", async () => { class DecoratorInferred { - @mobx.flow + @flow *f() { return true } } class DecoratorExplicit { - @mobx.flow + @flow *f(): Generator { return true } diff --git a/packages/mobx/__tests__/base/tojs.js b/packages/mobx/__tests__/base/tojs.js index c8a01a698b..e4f857433a 100644 --- a/packages/mobx/__tests__/base/tojs.js +++ b/packages/mobx/__tests__/base/tojs.js @@ -384,15 +384,6 @@ describe("recurseEverything set to true", function () { }) }) -test("Does not throw on object with configure({ useProxies: 'ifavailable'}) #2871", () => { - mobx.configure({ useProxies: "ifavailable" }) - expect(() => { - const o = mobx.observable({ key: "value" }) - const dispose = mobx.autorun(() => mobx.toJS(o)) - dispose() - }).not.toThrow() -}) - test("Correctly converts observable objects with computed values", () => { const a = observable({ key: "value" }) const c = observable({ computedValue: mobx.computed(() => a.key) }) diff --git a/packages/mobx/__tests__/base/trace.ts b/packages/mobx/__tests__/base/trace.ts deleted file mode 100644 index bbc8b37c8c..0000000000 --- a/packages/mobx/__tests__/base/trace.ts +++ /dev/null @@ -1,148 +0,0 @@ -"use strict" - -import * as mobx from "../../src/mobx" - -describe("trace", () => { - let consoleLogSpy - beforeEach(() => { - mobx._resetGlobalState() - consoleLogSpy = jest.spyOn(console, "log").mockImplementation() - }) - afterEach(() => { - consoleLogSpy.mockRestore() - }) - - test("simple", () => { - const expectedLogCalls: Array = [] - const x = mobx.observable( - { - firstname: "Michel", - lastname: "Weststrate", - get fullname() { - /* test multi line comment - (run this unit test from VS code, and pass 'true' as third argument to trace below to verify) - */ - const res = this.firstname + " " + this.lastname - mobx.trace(this, "fullname") - return res - } - }, - {}, - { name: "x" } - ) - x.fullname - expectedLogCalls.push(["[mobx.trace] 'x.fullname' tracing enabled"]) - - x.fullname - expectedLogCalls.push([ - "[mobx.trace] Computed value 'x.fullname' is being read outside a reactive context. Doing a full recompute." - ]) - - const dispose = mobx.autorun( - () => { - x.fullname - mobx.trace() - }, - { name: "autorun" } - ) - expectedLogCalls.push(["[mobx.trace] 'autorun' tracing enabled"]) - - mobx.transaction(() => { - x.firstname = "John" - expectedLogCalls.push([ - "[mobx.trace] 'x.fullname' is invalidated due to a change in: 'x.firstname'" - ]) - x.lastname = "Doe" - }) - - expectedLogCalls.push([ - "[mobx.trace] 'autorun' is invalidated due to a change in: 'x.fullname'" - ]) - - dispose() - - expectedLogCalls.push([ - "[mobx.trace] Computed value 'x.fullname' was suspended and it will recompute on the next access." - ]) - - expect(expectedLogCalls).toEqual(consoleLogSpy.mock.calls) - }) - - test("Log only if derivation is actually about to re-run #2859", () => { - const expectedLogCalls: Array = [] - const x = mobx.observable( - { - foo: 0, - get fooIsGreaterThan5() { - return this.foo > 5 - } - }, - {}, - { name: "x" } - ) - mobx.trace(x, "fooIsGreaterThan5") - expectedLogCalls.push(["[mobx.trace] 'x.fooIsGreaterThan5' tracing enabled"]) - - x.fooIsGreaterThan5 - expectedLogCalls.push([ - "[mobx.trace] Computed value 'x.fooIsGreaterThan5' is being read outside a reactive context. Doing a full recompute." - ]) - - const dispose = mobx.autorun( - () => { - mobx.trace(true) - x.fooIsGreaterThan5 - }, - { name: "autorun" } - ) - expectedLogCalls.push(["[mobx.trace] 'autorun' tracing enabled"]) - - mobx.transaction(() => { - x.foo = 1 - expectedLogCalls.push([ - "[mobx.trace] 'x.fooIsGreaterThan5' is invalidated due to a change in: 'x.foo'" - ]) - }) - - mobx.transaction(() => { - x.foo = 6 - expectedLogCalls.push([ - "[mobx.trace] 'x.fooIsGreaterThan5' is invalidated due to a change in: 'x.foo'" - ]) - }) - - expectedLogCalls.push([ - "[mobx.trace] 'autorun' is invalidated due to a change in: 'x.fooIsGreaterThan5'" - ]) - - dispose() - - expectedLogCalls.push([ - "[mobx.trace] Computed value 'x.fooIsGreaterThan5' was suspended and it will recompute on the next access." - ]) - - expect(expectedLogCalls).toEqual(consoleLogSpy.mock.calls) - }) - - test("1850", () => { - const x = mobx.observable({ - firstname: "Michel", - lastname: "Weststrate", - get fullname() { - /* test multi line comment - (run this unit test from VS code, to manually verify serialization) - */ - const res = this.firstname + " " + this.lastname - mobx.trace(this, "fullname", true) - return res - } - }) - - mobx.autorun(() => { - x.fullname - }) - expect(() => { - x.firstname += "!" - }).not.toThrow("Unexpected identifier") - }) -}) diff --git a/packages/mobx/__tests__/base/typescript-decorators.ts b/packages/mobx/__tests__/base/typescript-decorators.ts deleted file mode 100644 index 13b507df1c..0000000000 --- a/packages/mobx/__tests__/base/typescript-decorators.ts +++ /dev/null @@ -1,1273 +0,0 @@ -"use strict" - -import { - observe, - computed, - observable, - autorun, - extendObservable, - action, - IObservableArray, - IArrayWillChange, - IArrayWillSplice, - IObservableValue, - isObservable, - isObservableProp, - isObservableObject, - transaction, - IObjectDidChange, - spy, - configure, - isAction, - IAtom, - createAtom, - runInAction, - makeObservable, - flow, - flowResult -} from "../../src/mobx" -import * as mobx from "../../src/mobx" - -const testFunction = function (a: any) {} - -// lazy wrapper around yest - -const t = { - equal(a: any, b: any) { - expect(a).toBe(b) - }, - deepEqual(a: any, b: any) { - expect(a).toEqual(b) - }, - notEqual(a: any, b: any) { - expect(a).not.toEqual(b) - }, - - throws(a: any, b: any) { - expect(a).toThrow(b) - } -} - -test("decorators", () => { - class Order { - @observable price: number = 3 - @observable amount: number = 2 - @observable orders: string[] = [] - @observable aFunction = testFunction - - @computed - get total() { - return this.amount * this.price * (1 + this.orders.length) - } - - constructor() { - makeObservable(this) - } - - @flow - *testDouble(n: number): Generator { - yield 3 - return n * 2 - } - - async run() { - const x: number = await flowResult(this.testDouble(2)) - } - } - - const o = new Order() - t.equal(isObservableObject(o), true) - t.equal(isObservableProp(o, "amount"), true) - t.equal(isObservableProp(o, "total"), true) - - const events: any[] = [] - const d1 = observe(o, (ev: IObjectDidChange) => events.push(ev.name, (ev as any).oldValue)) - const d2 = observe(o, "price", ev => events.push(ev.newValue, ev.oldValue)) - const d3 = observe(o, "total", ev => events.push(ev.newValue, ev.oldValue)) - - o.price = 4 - - d1() - d2() - d3() - - o.price = 5 - - t.deepEqual(events, [ - 8, // new total - 6, // old total - 4, // new price - 3, // old price - "price", // event name - 3 // event oldValue - ]) -}) - -test("annotations", () => { - const fn0 = () => 0 - class Order { - @observable price: number = 3 - @observable amount: number = 2 - @observable orders: string[] = [] - @observable aFunction = fn0 - - @computed - get total() { - return this.amount * this.price * (1 + this.orders.length) - } - - constructor() { - makeObservable(this) - } - } - - const order1totals: number[] = [] - const order1 = new Order() - const order2 = new Order() - - const disposer = autorun(() => { - order1totals.push(order1.total) - }) - - order2.price = 4 - order1.amount = 1 - - t.equal(order1.price, 3) - t.equal(order1.total, 3) - t.equal(order2.total, 8) - order2.orders.push("bla") - t.equal(order2.total, 16) - - order1.orders.splice(0, 0, "boe", "hoi") - t.deepEqual(order1totals, [6, 3, 9]) - - disposer() - order1.orders.pop() - t.equal(order1.total, 6) - t.deepEqual(order1totals, [6, 3, 9]) - expect(isAction(order1.aFunction)).toBe(true) - expect(order1.aFunction()).toBe(0) - order1.aFunction = () => 1 - expect(isAction(order1.aFunction)).toBe(true) - expect(order1.aFunction()).toBe(1) -}) - -test("box", () => { - class Box { - @observable uninitialized: any - @observable height = 20 - @observable sizes = [2] - @observable - someFunc = function () { - return 2 - } - @computed - get width() { - return this.height * this.sizes.length * this.someFunc() * (this.uninitialized ? 2 : 1) - } - @action("test") - addSize() { - this.sizes.push(3) - this.sizes.push(4) - } - - constructor() { - makeObservable(this) - } - } - - const box = new Box() - - const ar: number[] = [] - - autorun(() => { - ar.push(box.width) - }) - - t.deepEqual(ar.slice(), [40]) - box.height = 10 - t.deepEqual(ar.slice(), [40, 20]) - box.sizes.push(3, 4) - t.deepEqual(ar.slice(), [40, 20, 60]) - box.someFunc = () => 7 - t.deepEqual(ar.slice(), [40, 20, 60, 210]) - box.uninitialized = true - t.deepEqual(ar.slice(), [40, 20, 60, 210, 420]) - box.addSize() - expect(ar.slice()).toEqual([40, 20, 60, 210, 420, 700]) -}) - -test("computed setter should succeed", () => { - class Bla { - @observable a = 3 - @computed - get propX() { - return this.a * 2 - } - set propX(v) { - this.a = v - } - - constructor() { - makeObservable(this) - } - } - - const b = new Bla() - t.equal(b.propX, 6) - b.propX = 4 - t.equal(b.propX, 8) -}) - -test("typescript: parameterized computed decorator", () => { - class TestClass { - @observable x = 3 - @observable y = 3 - @computed.struct - get boxedSum() { - return { sum: Math.round(this.x) + Math.round(this.y) } - } - constructor() { - makeObservable(this) - } - } - - const t1 = new TestClass() - const changes: { sum: number }[] = [] - const d = autorun(() => changes.push(t1.boxedSum)) - - t1.y = 4 // change - t.equal(changes.length, 2) - t1.y = 4.2 // no change - t.equal(changes.length, 2) - transaction(() => { - t1.y = 3 - t1.x = 4 - }) // no change - t.equal(changes.length, 2) - t1.x = 6 // change - t.equal(changes.length, 3) - d() - - t.deepEqual(changes, [{ sum: 6 }, { sum: 7 }, { sum: 9 }]) -}) - -test("issue 165", () => { - function report(msg: string, value: T) { - // console.log(msg, ":", value) - return value - } - - class Card { - constructor(public game: Game, public id: number) { - makeObservable(this) - } - - @computed - get isWrong() { - return report( - "Computing isWrong for card " + this.id, - this.isSelected && this.game.isMatchWrong - ) - } - - @computed - get isSelected() { - return report( - "Computing isSelected for card" + this.id, - this.game.firstCardSelected === this || this.game.secondCardSelected === this - ) - } - } - - class Game { - @observable firstCardSelected: Card | null = null - @observable secondCardSelected: Card | null = null - - @computed - get isMatchWrong() { - return report( - "Computing isMatchWrong", - this.secondCardSelected !== null && - this.firstCardSelected!.id !== this.secondCardSelected.id - ) - } - - constructor() { - makeObservable(this) - } - } - - let game = new Game() - let card1 = new Card(game, 1), - card2 = new Card(game, 2) - - autorun(() => { - card1.isWrong - card2.isWrong - // console.log("card1.isWrong =", card1.isWrong) - // console.log("card2.isWrong =", card2.isWrong) - // console.log("------------------------------") - }) - - // console.log("Selecting first card") - game.firstCardSelected = card1 - // console.log("Selecting second card") - game.secondCardSelected = card2 - - t.equal(card1.isWrong, true) - t.equal(card2.isWrong, true) -}) - -test("issue 191 - shared initializers (ts)", () => { - class Test { - @observable obj = { a: 1 } - @observable array = [2] - constructor() { - makeObservable(this) - } - } - - const t1 = new Test() - t1.obj.a = 2 - t1.array.push(3) - - const t2 = new Test() - t2.obj.a = 3 - t2.array.push(4) - - t.notEqual(t1.obj, t2.obj) - t.notEqual(t1.array, t2.array) - t.equal(t1.obj.a, 2) - t.equal(t2.obj.a, 3) - - t.deepEqual(t1.array.slice(), [2, 3]) - t.deepEqual(t2.array.slice(), [2, 4]) -}) - -function normalizeSpyEvents(events: any[]) { - events.forEach(ev => { - delete ev.fn - delete ev.time - }) - return events -} - -test("action decorator (typescript)", () => { - class Store { - constructor(private multiplier: number) { - makeObservable(this) - } - - @action - add(a: number, b: number): number { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(3) - const events: any[] = [] - const d = spy(events.push.bind(events)) - t.equal(store1.add(3, 4), 14) - t.equal(store2.add(2, 2), 12) - t.equal(store1.add(1, 1), 4) - - t.deepEqual(normalizeSpyEvents(events), [ - { arguments: [3, 4], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [2, 2], name: "add", spyReportStart: true, object: store2, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [1, 1], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("custom action decorator (typescript)", () => { - class Store { - constructor(private multiplier: number) { - makeObservable(this) - } - - @action("zoem zoem") - add(a: number, b: number): number { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(3) - const events: any[] = [] - const d = spy(events.push.bind(events)) - t.equal(store1.add(3, 4), 14) - t.equal(store2.add(2, 2), 12) - t.equal(store1.add(1, 1), 4) - - t.deepEqual(normalizeSpyEvents(events), [ - { - arguments: [3, 4], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [2, 2], - name: "zoem zoem", - spyReportStart: true, - object: store2, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [1, 1], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("action decorator on field (typescript)", () => { - class Store { - constructor(private multiplier: number) { - makeObservable(this) - } - - @action - add = (a: number, b: number) => { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(7) - expect(store1.add).not.toEqual(store2.add) - - const events: any[] = [] - const d = spy(events.push.bind(events)) - t.equal(store1.add(3, 4), 14) - t.equal(store2.add(4, 5), 63) - t.equal(store1.add(2, 2), 8) - - t.deepEqual(normalizeSpyEvents(events), [ - { arguments: [3, 4], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [4, 5], name: "add", spyReportStart: true, object: store2, type: "action" }, - { type: "report-end", spyReportEnd: true }, - { arguments: [2, 2], name: "add", spyReportStart: true, object: store1, type: "action" }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("custom action decorator on field (typescript)", () => { - class Store { - constructor(private multiplier: number) { - makeObservable(this) - } - - @action("zoem zoem") - add = (a: number, b: number) => { - return (a + b) * this.multiplier - } - } - - const store1 = new Store(2) - const store2 = new Store(7) - - const events: any[] = [] - const d = spy(events.push.bind(events)) - t.equal(store1.add(3, 4), 14) - t.equal(store2.add(4, 5), 63) - t.equal(store1.add(2, 2), 8) - - t.deepEqual(normalizeSpyEvents(events), [ - { - arguments: [3, 4], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [4, 5], - name: "zoem zoem", - spyReportStart: true, - object: store2, - type: "action" - }, - { type: "report-end", spyReportEnd: true }, - { - arguments: [2, 2], - name: "zoem zoem", - spyReportStart: true, - object: store1, - type: "action" - }, - { type: "report-end", spyReportEnd: true } - ]) - - d() -}) - -test("267 (typescript) should be possible to declare properties observable outside strict mode", () => { - configure({ enforceActions: "observed" }) - - class Store { - @observable timer: number | null = null - - constructor() { - makeObservable(this) - } - } -}) - -test("288 atom not detected for object property", () => { - class Store { - @observable foo = "" - - constructor() { - makeObservable(this) - } - } - - const store = new Store() - - mobx.observe( - store, - "foo", - () => { - // console.log("Change observed") - }, - true - ) -}) - -test.skip("observable performance - ts - decorators", () => { - const AMOUNT = 100000 - - class A { - @observable a = 1 - @observable b = 2 - @observable c = 3 - @computed - get d() { - return this.a + this.b + this.c - } - constructor() { - makeObservable(this) - } - } - - const objs: any[] = [] - const start = Date.now() - - for (let i = 0; i < AMOUNT; i++) objs.push(new A()) - - console.log("created in ", Date.now() - start) - - for (let j = 0; j < 4; j++) { - for (let i = 0; i < AMOUNT; i++) { - const obj = objs[i] - obj.a += 3 - obj.b *= 4 - obj.c = obj.b - obj.a - obj.d - } - } - - console.log("changed in ", Date.now() - start) -}) - -test("unbound methods", () => { - class A { - // shared across all instances - @action - m1() {} - - // per instance - @action m2 = () => {} - constructor() { - makeObservable(this) - } - } - - const a1 = new A() - const a2 = new A() - - t.equal(a1.m1, a2.m1) - t.notEqual(a1.m2, a2.m2) - t.equal(Object.hasOwnProperty.call(a1, "m1"), false) - t.equal(Object.hasOwnProperty.call(a1, "m2"), true) - t.equal(Object.hasOwnProperty.call(a2, "m1"), false) - t.equal(Object.hasOwnProperty.call(a2, "m2"), true) -}) - -test("inheritance", () => { - class A { - @observable a = 2 - constructor() { - makeObservable(this) - } - } - - class B extends A { - @observable b = 3 - @computed - get c() { - return this.a + this.b - } - constructor() { - super() - makeObservable(this) - } - } - const b1 = new B() - const b2 = new B() - const values: any[] = [] - mobx.autorun(() => values.push(b1.c + b2.c)) - - b1.a = 3 - b1.b = 4 - b2.b = 5 - b2.a = 6 - - t.deepEqual(values, [10, 11, 12, 14, 18]) -}) - -test("inheritance overrides observable", () => { - class A { - @observable a = 2 - constructor() { - makeObservable(this) - } - } - - class B { - @observable a = 5 - @observable b = 3 - @computed - get c() { - return this.a + this.b - } - constructor() { - makeObservable(this) - } - } - - const b1 = new B() - const b2 = new B() - const values: any[] = [] - mobx.autorun(() => values.push(b1.c + b2.c)) - - b1.a = 3 - b1.b = 4 - b2.b = 5 - b2.a = 6 - - t.deepEqual(values, [16, 14, 15, 17, 18]) -}) - -test("reusing initializers", () => { - class A { - @observable a = 3 - @observable b = this.a + 2 - @computed - get c() { - return this.a + this.b - } - @computed - get d() { - return this.c + 1 - } - constructor() { - makeObservable(this) - } - } - - const a = new A() - const values: any[] = [] - mobx.autorun(() => values.push(a.d)) - - a.a = 4 - t.deepEqual(values, [9, 10]) -}) - -test("enumerability", () => { - class A { - @observable a = 1 // enumerable, on proto - @computed - get b() { - return this.a - } // non-enumerable, (and, ideally, on proto) - @action - m() {} // non-enumerable, on proto - @action m2 = () => {} // non-enumerable, on self - constructor() { - makeObservable(this) - } - } - - const a = new A() - - // not initialized yet - let ownProps = Object.keys(a) - let props: string[] = [] - for (const key in a) props.push(key) - - t.deepEqual(ownProps, [ - "a" // yeej! - ]) - - t.deepEqual(props, [ - // also 'a' would be ok - "a" - ]) - - t.equal("a" in a, true) - // eslint-disable-next-line - t.equal(a.hasOwnProperty("a"), true) - // eslint-disable-next-line - t.equal(a.hasOwnProperty("b"), true) // false would be slightly better, true also ok-ish, and, see #1777 - // eslint-disable-next-line - t.equal(a.hasOwnProperty("m"), false) - // eslint-disable-next-line - t.equal(a.hasOwnProperty("m2"), true) - - t.equal(mobx.isAction(a.m), true) - t.equal(mobx.isAction(a.m2), true) - - // after initialization - a.a - a.b - a.m - a.m2 - - ownProps = Object.keys(a) - props = [] - for (const key in a) props.push(key) - - t.deepEqual(ownProps, ["a"]) - - t.deepEqual(props, ["a"]) - - t.equal("a" in a, true) - // eslint-disable-next-line - t.equal(a.hasOwnProperty("a"), true) - // eslint-disable-next-line - t.equal(a.hasOwnProperty("b"), true) // false would be slightly better, true also ok-ish, and, see #1777 - // eslint-disable-next-line - t.equal(a.hasOwnProperty("m"), false) - // eslint-disable-next-line - t.equal(a.hasOwnProperty("m2"), true) -}) - -test("issue 285 (typescript)", () => { - const { observable, toJS } = mobx - - class Todo { - id = 1 - @observable title: string - @observable finished = false - @observable childThings = [1, 2, 3] - constructor(title: string) { - makeObservable(this) - this.title = title - } - } - - const todo = new Todo("Something to do") - - t.deepEqual(toJS(todo), { - id: 1, - title: "Something to do", - finished: false, - childThings: [1, 2, 3] - }) -}) - -test("verify object assign (typescript)", () => { - class Todo { - @observable title = "test" - @computed - get upperCase() { - return this.title.toUpperCase() - } - constructor() { - makeObservable(this) - } - } - - t.deepEqual((Object as any).assign({}, new Todo()), { - title: "test" - }) -}) - -test("379, inheritable actions (typescript)", () => { - class A { - @action - method() { - return 42 - } - constructor() { - makeObservable(this) - } - } - - class B extends A { - method() { - return super.method() * 2 - } - constructor() { - super() - makeObservable(this) - } - } - - class C extends B { - method() { - return super.method() + 3 - } - constructor() { - super() - makeObservable(this) - } - } - - const b = new B() - t.equal(b.method(), 84) - t.equal(isAction(b.method), true) - - const a = new A() - t.equal(a.method(), 42) - t.equal(isAction(a.method), true) - - const c = new C() - t.equal(c.method(), 87) - t.equal(isAction(c.method), true) -}) - -test("373 - fix isObservable for unused computed", () => { - class Bla { - @computed - get computedVal() { - return 3 - } - constructor() { - makeObservable(this) - t.equal(isObservableProp(this, "computedVal"), true) - this.computedVal - t.equal(isObservableProp(this, "computedVal"), true) - } - } - - new Bla() -}) - -test("505, don't throw when accessing subclass fields in super constructor (typescript)", () => { - const values: any = {} - class A { - @observable a = 1 - constructor() { - makeObservable(this) - values.b = (this as any)["b"] - values.a = this.a - } - } - - class B extends A { - @observable b = 2 - - constructor() { - super() - makeObservable(this) - } - } - - new B() - t.deepEqual(values, { a: 1, b: undefined }) // undefined, as A constructor runs before B constructor -}) - -test("705 - setter undoing caching (typescript)", () => { - let recomputes = 0 - let autoruns = 0 - - class Person { - @observable name: string = "" - @observable title: string = "" - - // Typescript bug: if fullName is before the getter, the property is defined twice / incorrectly, see #705 - // set fullName(val) { - // // Noop - // } - @computed - get fullName() { - recomputes++ - return this.title + " " + this.name - } - // Should also be possible to define the setter _before_ the fullname - set fullName(val) { - // Noop - } - constructor() { - makeObservable(this) - } - } - - let p1 = new Person() - p1.name = "Tom Tank" - p1.title = "Mr." - - t.equal(recomputes, 0) - t.equal(autoruns, 0) - - const d1 = autorun(() => { - autoruns++ - p1.fullName - }) - - const d2 = autorun(() => { - autoruns++ - p1.fullName - }) - - t.equal(recomputes, 1) - t.equal(autoruns, 2) - - p1.title = "Master" - t.equal(recomputes, 2) - t.equal(autoruns, 4) - - d1() - d2() -}) - -test("@observable.ref (TS)", () => { - class A { - @observable.ref ref = { a: 3 } - constructor() { - makeObservable(this) - } - } - - const a = new A() - t.equal(a.ref.a, 3) - t.equal(mobx.isObservable(a.ref), false) - t.equal(mobx.isObservableProp(a, "ref"), true) -}) - -test("@observable.shallow (TS)", () => { - class A { - @observable.shallow arr = [{ todo: 1 }] - constructor() { - makeObservable(this) - } - } - - const a = new A() - const todo2 = { todo: 2 } - a.arr.push(todo2) - t.equal(mobx.isObservable(a.arr), true) - t.equal(mobx.isObservableProp(a, "arr"), true) - t.equal(mobx.isObservable(a.arr[0]), false) - t.equal(mobx.isObservable(a.arr[1]), false) - t.equal(a.arr[1] === todo2, true) -}) - -test("@observable.shallow - 2 (TS)", () => { - class A { - @observable.shallow arr: Record = { x: { todo: 1 } } - constructor() { - makeObservable(this) - } - } - - const a = new A() - const todo2 = { todo: 2 } - a.arr.y = todo2 - t.equal(mobx.isObservable(a.arr), true) - t.equal(mobx.isObservableProp(a, "arr"), true) - t.equal(mobx.isObservable(a.arr.x), false) - t.equal(mobx.isObservable(a.arr.y), false) - t.equal(a.arr.y === todo2, true) -}) - -test("@observable.deep (TS)", () => { - class A { - @observable.deep arr = [{ todo: 1 }] - constructor() { - makeObservable(this) - } - } - - const a = new A() - const todo2 = { todo: 2 } - a.arr.push(todo2) - - t.equal(mobx.isObservable(a.arr), true) - t.equal(mobx.isObservableProp(a, "arr"), true) - t.equal(mobx.isObservable(a.arr[0]), true) - t.equal(mobx.isObservable(a.arr[1]), true) - t.equal(a.arr[1] !== todo2, true) - t.equal(isObservable(todo2), false) -}) - -test("action.bound binds (TS)", () => { - class A { - @observable x = 0 - @action.bound - inc(value: number) { - this.x += value - } - constructor() { - makeObservable(this) - } - } - - const a = new A() - const runner = a.inc - runner(2) - - t.equal(a.x, 2) -}) - -test("@computed.equals (TS)", () => { - const sameTime = (from: Time, to: Time) => from.hour === to.hour && from.minute === to.minute - class Time { - constructor(hour: number, minute: number) { - makeObservable(this) - this.hour = hour - this.minute = minute - } - - @observable public hour: number - @observable public minute: number - - @computed({ equals: sameTime }) - public get time() { - return { hour: this.hour, minute: this.minute } - } - } - const time = new Time(9, 0) - - const changes: Array<{ hour: number; minute: number }> = [] - const disposeAutorun = autorun(() => changes.push(time.time)) - - t.deepEqual(changes, [{ hour: 9, minute: 0 }]) - time.hour = 9 - t.deepEqual(changes, [{ hour: 9, minute: 0 }]) - time.minute = 0 - t.deepEqual(changes, [{ hour: 9, minute: 0 }]) - time.hour = 10 - t.deepEqual(changes, [ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 } - ]) - time.minute = 30 - t.deepEqual(changes, [ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 }, - { hour: 10, minute: 30 } - ]) - - disposeAutorun() -}) - -test("1072 - @observable without initial value and observe before first access", () => { - class User { - @observable loginCount?: number - - constructor() { - makeObservable(this) - } - } - - const user = new User() - observe(user, "loginCount", () => {}) -}) - -test("unobserved computed reads should warn with requiresReaction enabled", () => { - const consoleWarn = console.warn - const warnings: string[] = [] - console.warn = function (...args) { - warnings.push(...args) - } - try { - const expectedWarnings: string[] = [] - - class A { - @observable x = 0 - - @computed({ requiresReaction: true }) - get y() { - return this.x * 2 - } - constructor() { - makeObservable(this, undefined, { name: "a" }) - } - } - - const a = new A() - - a.y - expectedWarnings.push( - `[mobx] Computed value 'a.y' is being read outside a reactive context. Doing a full recompute.` - ) - - const d = mobx.reaction( - () => a.y, - () => {} - ) - - a.y - - d() - - a.y - expectedWarnings.push( - `[mobx] Computed value 'a.y' is being read outside a reactive context. Doing a full recompute.` - ) - - expect(warnings).toEqual(expectedWarnings) - } finally { - console.warn = consoleWarn - } -}) - -test("multiple inheritance should work", () => { - class A { - @observable x = 1 - - constructor() { - makeObservable(this) - } - } - - class B extends A { - @observable y = 1 - - constructor() { - super() - makeObservable(this) - } - } - - expect(mobx.keys(new B())).toEqual(["x", "y"]) -}) - -// 19.12.2020 @urugator: -// All annotated non-observable fields are not writable. -// All annotated fields of non-plain objects are non-configurable. -// https://github.com/mobxjs/mobx/pull/2641 -test.skip("actions are reassignable", () => { - // See #1398 and #1545, make actions reassignable to support stubbing - class A { - @action - m1() {} - @action m2 = () => {} - @action.bound - m3() {} - @action.bound m4 = () => {} - - constructor() { - makeObservable(this) - } - } - - const a = new A() - expect(isAction(a.m1)).toBe(true) - expect(isAction(a.m2)).toBe(true) - expect(isAction(a.m3)).toBe(true) - expect(isAction(a.m4)).toBe(true) - a.m1 = () => {} - expect(isAction(a.m1)).toBe(false) - a.m2 = () => {} - expect(isAction(a.m2)).toBe(false) - a.m3 = () => {} - expect(isAction(a.m3)).toBe(false) - a.m4 = () => {} - expect(isAction(a.m4)).toBe(false) -}) - -test("it should support asyncAction as decorator (ts)", async () => { - mobx.configure({ enforceActions: "observed" }) - - class X { - @observable a = 1 - - f = mobx.flow(function* f(this: X, initial: number) { - this.a = initial // this runs in action - this.a += yield Promise.resolve(5) as any - this.a = this.a * 2 - return this.a - }) - - constructor() { - makeObservable(this) - } - } - - const x = new X() - - expect(await x.f(3)).toBe(16) -}) - -test("toJS bug #1413 (TS)", () => { - class X { - @observable - test = { - test1: 1 - } - - constructor() { - makeObservable(this) - } - } - - const x = new X() - const res = mobx.toJS(x.test) as any - expect(res).toEqual({ test1: 1 }) - expect(res.__mobxDidRunLazyInitializers).toBe(undefined) -}) - -test("#2159 - computed property keys", () => { - const testSymbol = Symbol("test symbol") - const testString = "testString" - - class TestClass { - @observable [testSymbol] = "original symbol value"; - @observable [testString] = "original string value" - - constructor() { - makeObservable(this) - } - } - - const o = new TestClass() - - const events: any[] = [] - observe(o, testSymbol, ev => events.push(ev.newValue, ev.oldValue)) - observe(o, testString, ev => events.push(ev.newValue, ev.oldValue)) - - runInAction(() => { - o[testSymbol] = "new symbol value" - o[testString] = "new string value" - }) - - t.deepEqual(events, [ - "new symbol value", // new symbol - "original symbol value", // original symbol - "new string value", // new string - "original string value" // original string - ]) -}) diff --git a/packages/mobx/__tests__/base/typescript-tests.ts b/packages/mobx/__tests__/base/typescript-tests.ts index e4f55ad3f5..245fb27a92 100644 --- a/packages/mobx/__tests__/base/typescript-tests.ts +++ b/packages/mobx/__tests__/base/typescript-tests.ts @@ -3,10 +3,15 @@ import { observe, computed, + computedStruct, observable, + observableRef, + observableShallow, + observableDeep, autorun, extendObservable, action, + actionBound, IArrayDidChange, IArrayWillChange, IArrayWillSplice, @@ -370,7 +375,7 @@ test("typescript: parameterized computed decorator", () => { makeObservable(this, { x: observable, y: observable, - boxedSum: computed.struct + boxedSum: computedStruct }) } @@ -758,20 +763,14 @@ test.skip("observable performance - ts", () => { console.log("changed in ", Date.now() - start) }) +// Do not remove: slow manual stage-3 decorator benchmark test.skip("observable performance - ts - decorators", () => { const AMOUNT = 100000 class A { - @observable - a = 1 - @observable - b = 2 - @observable - c = 3 - - constructor() { - makeObservable(this) - } + @observable accessor a = 1 + @observable accessor b = 2 + @observable accessor c = 3 @computed get d() { @@ -1244,13 +1243,13 @@ test("705 - setter undoing caching (typescript)", () => { d2() }) -test("@observable.ref (TS)", () => { +test("@observableRef (TS)", () => { class A { ref = { a: 3 } constructor() { makeObservable(this, { - ref: observable.ref + ref: observableRef }) } } @@ -1261,13 +1260,13 @@ test("@observable.ref (TS)", () => { t.equal(mobx.isObservableProp(a, "ref"), true) }) -test("@observable.shallow (TS)", () => { +test("@observableShallow (TS)", () => { class A { arr = [{ todo: 1 }] constructor() { makeObservable(this, { - arr: observable.shallow + arr: observableShallow }) } } @@ -1282,13 +1281,13 @@ test("@observable.shallow (TS)", () => { t.equal(a.arr[1] === todo2, true) }) -test("@observable.deep (TS)", () => { +test("@observableDeep (TS)", () => { class A { arr = [{ todo: 1 }] constructor() { makeObservable(this, { - arr: observable.deep + arr: observableDeep }) } } @@ -1312,7 +1311,7 @@ test("action.bound binds (TS)", () => { constructor() { makeObservable(this, { x: observable, - inc: action.bound + inc: actionBound }) } @@ -1328,7 +1327,7 @@ test("action.bound binds (TS)", () => { t.equal(a.x, 2) }) -test("803 - action.bound and action preserve type info", () => { +test("803 - actionBound and action preserve type info", () => { function thingThatAcceptsCallback(cb: (elem: { x: boolean }) => void) {} thingThatAcceptsCallback(elem => { @@ -1741,8 +1740,8 @@ test.skip("actions are reassignable", () => { makeObservable(this, { m1: action, m2: action, - m3: action.bound, - m4: action.bound + m3: actionBound, + m4: actionBound }) } @@ -2380,20 +2379,21 @@ test("TS - it should support flow as annotation", done => { }, 10) }) -test("TS - it should support flow as decorator", done => { +test("TS - it should support flow as annotation", done => { const values: number[] = [] mobx.configure({ enforceActions: "observed" }) class X { - @observable a = 1 constructor() { - makeObservable(this) + makeObservable(this, { + a: observable, + f: flow + }) } - @flow *f(initial) { this.a = initial // this runs in action try { diff --git a/packages/mobx/__tests__/decorators_20223/tsconfig.json b/packages/mobx/__tests__/decorators_20223/tsconfig.json deleted file mode 100644 index 31758eb645..0000000000 --- a/packages/mobx/__tests__/decorators_20223/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../tsconfig.json", "../../../../tsconfig.test.json"], - "compilerOptions": { - "target": "ES6", - "experimentalDecorators": false, - "useDefineForClassFields": true, - - "rootDir": "../../" - }, - "exclude": ["__tests__"], - "include": ["./", "../../src"] // ["../../src", "./"] -} diff --git a/packages/mobx/__tests__/perf/index.js b/packages/mobx/__tests__/perf/index.js index 5ce81a7fa1..60e694e254 100644 --- a/packages/mobx/__tests__/perf/index.js +++ b/packages/mobx/__tests__/perf/index.js @@ -1,15 +1,10 @@ const start = Date.now() const mkdirp = require("mkdirp") -const ver = process.argv[2] -if (!ver || !ver.match(/legacy|proxy/)) { - throw new Error("specify version to perf test as (legacy|proxy)") -} - if (process.env.PERSIST) { const fs = require("fs") const path = require("path") - const logFile = path.resolve(`${__dirname}/../../perf_report/${ver}.txt`) + const logFile = path.resolve(`${__dirname}/../../perf_report/perf.txt`) mkdirp.sync(path.dirname(logFile)) // clear previous results if (fs.existsSync(logFile)) fs.unlinkSync(logFile) @@ -25,7 +20,7 @@ if (process.env.PERSIST) { } const perf = require("./perf.js") -perf(ver) +perf() // This test runs last.. require("tape")(t => { diff --git a/packages/mobx/__tests__/perf/lazy-computed-decorator.ts b/packages/mobx/__tests__/perf/lazy-computed-decorator.ts index 4ca6cfb8a6..64dcdd0a39 100644 --- a/packages/mobx/__tests__/perf/lazy-computed-decorator.ts +++ b/packages/mobx/__tests__/perf/lazy-computed-decorator.ts @@ -10,10 +10,9 @@ import * as path from "path" const distPath = path.resolve(__dirname, "..", "..", "..", "dist", "mobx.cjs.development.js") const mobx = require(distPath) as { computed: any - makeObservable: any observable: any } -const { computed, makeObservable, observable } = mobx +const { computed, observable } = mobx const INSTANCES = 50_000 const GETTERS_PER_INSTANCE = 10 @@ -53,10 +52,6 @@ class Wide { @computed get c9() { return this.v + 9 } - - constructor() { - makeObservable(this) - } } const GETTERS = ["c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9"] as const diff --git a/packages/mobx/__tests__/perf/perf.js b/packages/mobx/__tests__/perf/perf.js index 419a961f6d..08447ab48a 100644 --- a/packages/mobx/__tests__/perf/perf.js +++ b/packages/mobx/__tests__/perf/perf.js @@ -9,7 +9,7 @@ function voidObserver() { // nothing, nada, noppes. } -module.exports = function runForVersion(version) { +module.exports = function runPerfSuite() { /* results of this test: 300/40000 mseconds on netbook (AMD c60 processor, same test is on Intel i7 3770 ~10 times faster) @@ -19,13 +19,10 @@ results of this test: */ const mobx = require(`../../dist/mobx.cjs.production.min.js`) - if (version === "legacy") { - mobx.configure({ useProxies: false }) - } const observable = mobx.observable const computed = mobx.computed - test(`${version} - one observes ten thousand that observe one`, function (t) { + test("one observes ten thousand that observe one", function (t) { gc() const a = observable.box(2) @@ -71,7 +68,7 @@ results of this test: t.end() }) - test(`${version} - five hundrend properties that observe their sibling`, function (t) { + test("five hundrend properties that observe their sibling", function (t) { gc() const observables = [observable.box(1)] for (let i = 0; i < 500; i++) { @@ -105,7 +102,7 @@ results of this test: t.end() }) - test(`${version} - late dependency change`, function (t) { + test("late dependency change", function (t) { gc() const values = [] for (let i = 0; i < 100; i++) values.push(observable.box(0)) @@ -127,7 +124,7 @@ results of this test: t.end() }) - test(`${version} - lots of unused computables`, function (t) { + test("lots of unused computables", function (t) { gc() const a = observable.box(1) @@ -175,7 +172,7 @@ results of this test: t.end() }) - test(`${version} - many unreferenced observables`, function (t) { + test("many unreferenced observables", function (t) { gc() const a = observable.box(3) const b = observable.box(6) @@ -196,7 +193,7 @@ results of this test: t.end() }) - test(`${version} - array.es2023 findLastIndex methods`, function (t) { + test("array.es2023 findLastIndex methods", function (t) { gc() let aCalc = 0 let bCalc = 0 @@ -229,7 +226,7 @@ results of this test: t.end() }) - test(`${version} - array reduce`, function (t) { + test("array reduce", function (t) { gc() let aCalc = 0 const ar = observable([]) @@ -271,7 +268,7 @@ results of this test: t.end() }) - test(`${version} - array classic loop`, function (t) { + test("array classic loop", function (t) { gc() const ar = observable([]) let aCalc = 0 @@ -403,23 +400,23 @@ results of this test: t.end() } - test(`${version} - order system observed`, function (t) { + test("order system observed", function (t) { order_system_helper(t, false, true) }) - test(`${version} - order system batched observed`, function (t) { + test("order system batched observed", function (t) { order_system_helper(t, true, true) }) - test(`${version} - order system lazy`, function (t) { + test("order system lazy", function (t) { order_system_helper(t, false, false) }) - test(`${version} - order system batched lazy`, function (t) { + test("order system batched lazy", function (t) { order_system_helper(t, true, false) }) - test(`${version} - create array`, function (t) { + test("create array", function (t) { gc() const a = [] for (let i = 0; i < 1000; i++) a.push(i) @@ -429,7 +426,7 @@ results of this test: t.end() }) - test(`${version} - create array (fast)`, function (t) { + test("create array (fast)", function (t) { gc() const a = [] for (let i = 0; i < 1000; i++) a.push(i) @@ -439,7 +436,7 @@ results of this test: t.end() }) - test(`${version} - observe and dispose`, t => { + test("observe and dispose", t => { gc() const start = now() @@ -460,7 +457,7 @@ results of this test: t.end() }) - test(`${version} - sort`, t => { + test("sort", t => { gc() function Item(a, b, c) { @@ -534,7 +531,7 @@ results of this test: t.end() }) - test(`${version} - computed temporary memoization`, t => { + test("computed temporary memoization", t => { "use strict" gc() const computeds = [] @@ -550,7 +547,7 @@ results of this test: t.end() }) - test(`${version} - Set: initializing`, function (t) { + test("Set: initializing", function (t) { gc() const iterationsCount = 100000 let i @@ -564,7 +561,7 @@ results of this test: t.end() }) - test(`${version} - Set: setting and deleting properties`, function (t) { + test("Set: setting and deleting properties", function (t) { gc() const iterationsCount = 1000 const propertiesCount = 10000 @@ -595,7 +592,7 @@ results of this test: t.end() }) - test(`${version} - Set: looking up properties`, function (t) { + test("Set: looking up properties", function (t) { gc() const iterationsCount = 1000 const propertiesCount = 10000 @@ -627,7 +624,7 @@ results of this test: t.end() }) - test(`${version} - Set: iterator helpers`, function (t) { + test("Set: iterator helpers", function (t) { gc() const iterationsCount = 1000 const propertiesCount = 10000 @@ -657,7 +654,7 @@ results of this test: t.end() }) - test(`${version} - Set: conversion to array`, function (t) { + test("Set: conversion to array", function (t) { gc() const iterationsCount = 1000 const propertiesCount = 10000 @@ -690,7 +687,7 @@ results of this test: t.end() }) - test(`${version} - Map: initializing`, function (t) { + test("Map: initializing", function (t) { gc() const iterationsCount = 100000 let i @@ -704,7 +701,7 @@ results of this test: t.end() }) - test(`${version} - Map: looking up properties`, function (t) { + test("Map: looking up properties", function (t) { gc() const iterationsCount = 1000 const propertiesCount = 100 @@ -736,7 +733,7 @@ results of this test: t.end() }) - test(`${version} - Map: setting and deleting properties`, function (t) { + test("Map: setting and deleting properties", function (t) { gc() const iterationsCount = 1000 const propertiesCount = 100 diff --git a/packages/mobx/jest.config-decorators.js b/packages/mobx/jest.config-decorators.js deleted file mode 100644 index 751c550a4f..0000000000 --- a/packages/mobx/jest.config-decorators.js +++ /dev/null @@ -1,11 +0,0 @@ -const path = require("path") -const buildConfig = require("../../jest.base.config") - -module.exports = buildConfig( - __dirname, - { - testRegex: "__tests__/decorators_20223/.*\\.(t|j)sx?$", - setupFilesAfterEnv: [`/jest.setup.ts`] - }, - path.resolve(__dirname, "./__tests__/decorators_20223/tsconfig.json") -) diff --git a/packages/mobx/jest.projects.js b/packages/mobx/jest.projects.js index 6ded2bb72a..72394c69cd 100644 --- a/packages/mobx/jest.projects.js +++ b/packages/mobx/jest.projects.js @@ -2,5 +2,5 @@ module.exports = { collectCoverageFrom: ["/src/**/*.{ts,tsx}", "!**/node_modules/**"], coverageDirectory: "/coverage/", coverageReporters: ["lcov", "text"], - projects: ["/jest.config.js", "/jest.config-decorators.js"] + projects: ["/jest.config.js"] } diff --git a/packages/mobx/jest.setup.ts b/packages/mobx/jest.setup.ts index 38da0a0ab9..2c852eb852 100644 --- a/packages/mobx/jest.setup.ts +++ b/packages/mobx/jest.setup.ts @@ -6,7 +6,6 @@ function resetMobxTestState() { _resetGlobalState() _getGlobalState().spyListeners = [] configure({ - useProxies: "always", enforceActions: "never", computedRequiresReaction: false, reactionRequiresObservable: false, diff --git a/packages/mobx/package.json b/packages/mobx/package.json index e4db9abfce..bd49219ce3 100644 --- a/packages/mobx/package.json +++ b/packages/mobx/package.json @@ -3,6 +3,7 @@ "version": "6.16.1", "description": "Simple, scalable state management.", "source": "src/mobx.ts", + "type": "commonjs", "main": "dist/index.js", "umd:main": "dist/mobx.umd.production.min.js", "unpkg": "dist/mobx.umd.production.min.js", @@ -12,6 +13,25 @@ "react-native": "dist/mobx.esm.js", "types": "dist/mobx.d.ts", "typings": "dist/mobx.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "react-native": { + "types": "./dist/mobx.d.ts", + "default": "./dist/mobx.esm.js" + }, + "import": { + "types": "./dist/mobx.d.ts", + "default": "./dist/mobx.mjs" + }, + "default": { + "types": "./dist/mobx.d.ts", + "default": "./dist/index.js" + } + }, + "./dist/*": "./dist/*", + "./src/*": "./src/*" + }, "files": [ "src", "dist", @@ -36,13 +56,6 @@ "homepage": "https://mobx.js.org/", "dependencies": {}, "devDependencies": { - "@babel/core": "^7.9.0", - "@babel/plugin-proposal-class-properties": "^7.8.3", - "@babel/plugin-proposal-decorators": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.9.0", - "@babel/preset-env": "^7.9.5", - "@babel/preset-typescript": "^7.9.0", - "@babel/runtime": "^7.9.2", "conditional-type-checks": "^1.0.5" }, "keywords": [ @@ -62,18 +75,15 @@ "scripts": { "test": "jest --config jest.projects.js", "lint": "eslint src/**/*", - "build": "node ../../scripts/build.js mobx", - "build:test": "npm run build -- --target test", + "build": "rollup --config rollup.config.mjs", + "build:test": "npm run build -- --environment TARGET:test", "perf": "scripts/perf.sh", - "perf-legacy": "node --expose-gc ./__tests__/perf/index.js legacy", - "perf-proxy": "node --expose-gc ./__tests__/perf/index.js proxy", "perf-decorator": "tsc -p ./__tests__/perf/tsconfig.decorator.json && node --expose-gc ./__tests__/perf/compiled/lazy-computed-decorator.js && node --expose-gc ./__tests__/perf/compiled/lazy-observable-decorator.js", - "test:performance": "npm run perf -- proxy && npm run perf -- legacy && npm run perf-decorator", + "test:performance": "npm run perf && npm run perf-decorator", "test:mixed-versions": "npm test -- --testRegex mixed-versions", "test:types": "tsc --noEmit", "test:coverage": "npm test -- -i --coverage", - "test:size": "import-size --report . observable computed autorun action", "test:check": "npm run test:types", - "prepublishOnly": "node ./scripts/prepublish.js && npm run build -- --target publish" + "prepublishOnly": "node ./scripts/prepublish.js && npm run build -- --environment TARGET:publish" } } diff --git a/packages/mobx/rollup.config.mjs b/packages/mobx/rollup.config.mjs new file mode 100644 index 0000000000..030770c1f6 --- /dev/null +++ b/packages/mobx/rollup.config.mjs @@ -0,0 +1,10 @@ +import createRollupConfig from "../../scripts/create-rollup-config.mjs" + +export default createRollupConfig({ + packageName: "mobx", + input: "src/mobx.ts", + globals: { + react: "React", + "react-native": "ReactNative" + } +}) diff --git a/packages/mobx/scripts/perf.sh b/packages/mobx/scripts/perf.sh index ff9b61484e..df2a4b6065 100755 --- a/packages/mobx/scripts/perf.sh +++ b/packages/mobx/scripts/perf.sh @@ -1,3 +1,3 @@ #!/bin/bash -time node --expose-gc ./__tests__/perf/index.js $1 \ No newline at end of file +time node --expose-gc ./__tests__/perf/index.js diff --git a/packages/mobx/src/api/action.ts b/packages/mobx/src/api/action.ts index 2fc2b519e7..4798757e6b 100644 --- a/packages/mobx/src/api/action.ts +++ b/packages/mobx/src/api/action.ts @@ -2,16 +2,15 @@ import { createAction, executeAction, Annotation, - storeAnnotation, die, isFunction, isStringish, - createDecoratorAnnotation, createActionAnnotation, - is20223Decorator + decorateAction20223_, + assign } from "../internal" - -import type { ClassFieldDecorator, ClassMethodDecorator } from "../types/decorator_fills" +import { createDecoratorAnnotation, type DecoratorAnnotation } from "./decoratorannotation" +import type { ClassMethodAndFieldDecorator } from "../types/decorator_fills" export const ACTION = "action" export const ACTION_BOUND = "action.bound" @@ -32,28 +31,32 @@ const autoActionBoundAnnotation = createActionAnnotation(AUTOACTION_BOUND, { bound: true }) -export interface IActionFactory - extends Annotation, - PropertyDecorator, - ClassMethodDecorator, - ClassFieldDecorator { +function createActionDecoratorAnnotation( + annotation: Annotation +): DecoratorAnnotation { + return createDecoratorAnnotation(annotation, decorateAction20223_) +} + +export interface IActionFactory extends Annotation, ClassMethodAndFieldDecorator { // nameless actions (fn: T): T // named actions (name: string, fn: T): T - // named decorator - (customName: string): PropertyDecorator & - Annotation & - ClassMethodDecorator & - ClassFieldDecorator - - // decorator (name no longer supported) - bound: Annotation & PropertyDecorator & ClassMethodDecorator & ClassFieldDecorator + // named annotation + (customName: string): DecoratorAnnotation } function createActionFactory(autoAction: boolean): IActionFactory { const res: IActionFactory = function action(arg1, arg2?): any { + if (arg2 && typeof arg2.kind === "string") { + return decorateAction20223_( + autoAction ? autoActionAnnotation : actionAnnotation, + arg1, + arg2 + ) + } + // action(fn() {}) if (isFunction(arg1)) { return createAction(arg1.name || DEFAULT_ACTION_NAME, arg1, autoAction) @@ -62,20 +65,9 @@ function createActionFactory(autoAction: boolean): IActionFactory { if (isFunction(arg2)) { return createAction(arg1, arg2, autoAction) } - // @action (2022.3 Decorators) - if (is20223Decorator(arg2)) { - return (autoAction ? autoActionAnnotation : actionAnnotation).decorate_20223_( - arg1, - arg2 - ) - } - // @action - if (isStringish(arg2)) { - return storeAnnotation(arg1, arg2, autoAction ? autoActionAnnotation : actionAnnotation) - } - // action("name") & @action("name") + // action("name") annotation if (isStringish(arg1)) { - return createDecoratorAnnotation( + return createActionDecoratorAnnotation( createActionAnnotation(autoAction ? AUTOACTION : ACTION, { name: arg1, autoAction @@ -91,12 +83,12 @@ function createActionFactory(autoAction: boolean): IActionFactory { } export const action: IActionFactory = createActionFactory(false) -Object.assign(action, actionAnnotation) +assign(action, actionAnnotation) export const autoAction: IActionFactory = createActionFactory(true) -Object.assign(autoAction, autoActionAnnotation) +assign(autoAction, autoActionAnnotation) -action.bound = createDecoratorAnnotation(actionBoundAnnotation) -autoAction.bound = createDecoratorAnnotation(autoActionBoundAnnotation) +export const actionBound = createActionDecoratorAnnotation(actionBoundAnnotation) +export const autoActionBound = createActionDecoratorAnnotation(autoActionBoundAnnotation) export function runInAction(fn: () => T): T { return executeAction(fn.name || DEFAULT_ACTION_NAME, false, fn, this, undefined) diff --git a/packages/mobx/src/api/annotation.ts b/packages/mobx/src/api/annotation.ts index 719c727402..87a9ea607f 100644 --- a/packages/mobx/src/api/annotation.ts +++ b/packages/mobx/src/api/annotation.ts @@ -20,7 +20,6 @@ export type Annotation = { descriptor: PropertyDescriptor, proxyTrap: boolean ): boolean | null - decorate_20223_(value: any, context: DecoratorContext) options_?: any } diff --git a/packages/mobx/src/api/autorun.ts b/packages/mobx/src/api/autorun.ts index 63454ab169..5317b52ed2 100644 --- a/packages/mobx/src/api/autorun.ts +++ b/packages/mobx/src/api/autorun.ts @@ -6,7 +6,7 @@ import { Lambda, Reaction, action, - comparer, + compareDefault, getNextId, isAction, isFunction, @@ -140,9 +140,7 @@ export function reaction( let isScheduled = false let value: T - const equals: IEqualsComparer = (opts as any).compareStructural - ? comparer.structural - : opts.equals || comparer.default + const equals: IEqualsComparer = opts.equals || compareDefault const r = new Reaction( name, diff --git a/packages/mobx/src/api/computed.ts b/packages/mobx/src/api/computed.ts index 6ac31e8e04..1f077de236 100644 --- a/packages/mobx/src/api/computed.ts +++ b/packages/mobx/src/api/computed.ts @@ -2,53 +2,48 @@ import { ComputedValue, IComputedValueOptions, Annotation, - storeAnnotation, - createDecoratorAnnotation, - isStringish, isPlainObject, isFunction, die, IComputedValue, createComputedAnnotation, - comparer, - is20223Decorator + compareStructural, + decorateComputed20223_, + assign } from "../internal" - +import { createDecoratorAnnotation, type DecoratorAnnotation } from "./decoratorannotation" import type { ClassGetterDecorator } from "../types/decorator_fills" export const COMPUTED = "computed" export const COMPUTED_STRUCT = "computed.struct" -export interface IComputedFactory extends Annotation, PropertyDecorator, ClassGetterDecorator { - // @computed(opts) - (options: IComputedValueOptions): Annotation & PropertyDecorator & ClassGetterDecorator +function createComputedDecoratorAnnotation( + annotation: Annotation +): DecoratorAnnotation { + return createDecoratorAnnotation(annotation, decorateComputed20223_) +} + +export interface IComputedFactory extends Annotation, ClassGetterDecorator { + // computed annotation with options + (options: IComputedValueOptions): DecoratorAnnotation // computed(fn, opts) (func: () => T, options?: IComputedValueOptions): IComputedValue - - struct: Annotation & PropertyDecorator & ClassGetterDecorator } const computedAnnotation = createComputedAnnotation(COMPUTED) const computedStructAnnotation = createComputedAnnotation(COMPUTED_STRUCT, { - equals: comparer.structural + equals: compareStructural }) +export const computedStruct = createComputedDecoratorAnnotation(computedStructAnnotation) -/** - * Decorator for class properties: @computed get value() { return expr; }. - * For legacy purposes also invokable as ES5 observable created: `computed(() => expr)`; - */ export const computed: IComputedFactory = function computed(arg1, arg2) { - if (is20223Decorator(arg2)) { - // @computed (2022.3 Decorators) - return computedAnnotation.decorate_20223_(arg1, arg2) - } - if (isStringish(arg2)) { - // @computed - return storeAnnotation(arg1, arg2, computedAnnotation) + if (arg2 && typeof arg2.kind === "string") { + return decorateComputed20223_(computedAnnotation, arg1, arg2) } + if (isPlainObject(arg1)) { - // @computed({ options }) - return createDecoratorAnnotation(createComputedAnnotation(COMPUTED, arg1)) + // computed annotation with options + return createComputedDecoratorAnnotation(createComputedAnnotation(COMPUTED, arg1)) } // computed(expr, options?) @@ -69,6 +64,4 @@ export const computed: IComputedFactory = function computed(arg1, arg2) { return new ComputedValue(opts) } as any -Object.assign(computed, computedAnnotation) - -computed.struct = createDecoratorAnnotation(computedStructAnnotation) +assign(computed, computedAnnotation) diff --git a/packages/mobx/src/api/configure.ts b/packages/mobx/src/api/configure.ts index cfabf9f2d4..f1a6fa32c2 100644 --- a/packages/mobx/src/api/configure.ts +++ b/packages/mobx/src/api/configure.ts @@ -1,9 +1,7 @@ import { globalState, isolateGlobalState, setReactionScheduler } from "../internal" -const NEVER = "never" const ALWAYS = "always" const OBSERVED = "observed" -// const IF_AVAILABLE = "ifavailable" export function configure(options: { enforceActions?: "never" | "always" | "observed" @@ -20,23 +18,11 @@ export function configure(options: { disableErrorBoundaries?: boolean safeDescriptors?: boolean reactionScheduler?: (f: () => void) => void - useProxies?: "always" | "never" | "ifavailable" }): void { if (options.isolateGlobalState === true) { isolateGlobalState() } - const { useProxies, enforceActions } = options - if (useProxies !== undefined) { - globalState.useProxies = - useProxies === ALWAYS - ? true - : useProxies === NEVER - ? false - : typeof Proxy !== "undefined" - } - if (useProxies === "ifavailable") { - globalState.verifyProxies = true - } + const { enforceActions } = options if (enforceActions !== undefined) { const ea = enforceActions === ALWAYS ? ALWAYS : enforceActions === OBSERVED globalState.enforceActions = ea diff --git a/packages/mobx/src/api/decoratorannotation.ts b/packages/mobx/src/api/decoratorannotation.ts new file mode 100644 index 0000000000..504fa6453d --- /dev/null +++ b/packages/mobx/src/api/decoratorannotation.ts @@ -0,0 +1,20 @@ +import { die } from "../errors" +import { assign } from "../utils/utils" +import type { Annotation } from "./annotation" + +export type DecoratorAnnotation any> = Annotation & Decorator + +export function createDecoratorAnnotation any>( + annotation: Annotation, + decorate: (annotation: Annotation, value: any, context: any) => any +): DecoratorAnnotation { + return assign(function decoratorAnnotation(value, context) { + if (context && typeof context.kind === "string") { + return decorate(annotation, value, context) + } + if (__DEV__) { + die(`Invalid arguments for \`${annotation.annotationType_}\``) + } + return undefined + }, annotation) as any +} diff --git a/packages/mobx/src/api/decorators.ts b/packages/mobx/src/api/decorators.ts index 9ecde78c12..76ccb9dbf1 100644 --- a/packages/mobx/src/api/decorators.ts +++ b/packages/mobx/src/api/decorators.ts @@ -1,88 +1,4 @@ -import { Annotation, addHiddenProp, AnnotationsMap, hasProp, die, isOverride } from "../internal" - -import type { Decorator } from "../types/decorator_fills" - -export const storedAnnotationsSymbol = Symbol("mobx-stored-annotations") - -/** - * Creates a function that acts as - * - decorator - * - annotation object - */ -export function createDecoratorAnnotation( - annotation: Annotation -): PropertyDecorator & Annotation & D { - function decorator(target, property) { - if (is20223Decorator(property)) { - return annotation.decorate_20223_(target, property) - } else { - storeAnnotation(target, property, annotation) - } - } - return Object.assign(decorator, annotation) as any -} - -/** - * Stores annotation to prototype, - * so it can be inspected later by `makeObservable` called from constructor - */ -export function storeAnnotation(prototype: any, key: PropertyKey, annotation: Annotation) { - if (!hasProp(prototype, storedAnnotationsSymbol)) { - addHiddenProp(prototype, storedAnnotationsSymbol, { - // Inherit annotations - ...prototype[storedAnnotationsSymbol] - }) - } - // @override must override something - if (__DEV__ && isOverride(annotation) && !hasProp(prototype[storedAnnotationsSymbol], key)) { - const fieldName = `${prototype.constructor.name}.prototype.${key.toString()}` - die( - `'${fieldName}' is decorated with 'override', ` + - `but no such decorated member was found on prototype.` - ) - } - // Cannot re-decorate - assertNotDecorated(prototype, annotation, key) - - // Ignore override - if (!isOverride(annotation)) { - prototype[storedAnnotationsSymbol][key] = annotation - } -} - -function assertNotDecorated(prototype: object, annotation: Annotation, key: PropertyKey) { - if (__DEV__ && !isOverride(annotation) && hasProp(prototype[storedAnnotationsSymbol], key)) { - const fieldName = `${prototype.constructor.name}.prototype.${key.toString()}` - const currentAnnotationType = prototype[storedAnnotationsSymbol][key].annotationType_ - const requestedAnnotationType = annotation.annotationType_ - die( - `Cannot apply '@${requestedAnnotationType}' to '${fieldName}':` + - `\nThe field is already decorated with '@${currentAnnotationType}'.` + - `\nRe-decorating fields is not allowed.` + - `\nUse '@override' decorator for methods overridden by subclass.` - ) - } -} - -/** - * Collects annotations from prototypes and stores them on target (instance) - */ -export function collectStoredAnnotations(target): AnnotationsMap { - if (!hasProp(target, storedAnnotationsSymbol)) { - // if (__DEV__ && !target[storedAnnotationsSymbol]) { - // die( - // `No annotations were passed to makeObservable, but no decorated members have been found either` - // ) - // } - // We need a copy as we will remove annotation from the list once it's applied. - addHiddenProp(target, storedAnnotationsSymbol, { ...target[storedAnnotationsSymbol] }) - } - return target[storedAnnotationsSymbol] -} - -export function is20223Decorator(context): context is DecoratorContext { - return typeof context == "object" && typeof context["kind"] == "string" -} +import { die } from "../internal" export function assert20223DecoratorType( context: DecoratorContext, diff --git a/packages/mobx/src/api/extras.ts b/packages/mobx/src/api/extras.ts index 5a7bd40fee..0b76c1bfd0 100644 --- a/packages/mobx/src/api/extras.ts +++ b/packages/mobx/src/api/extras.ts @@ -33,7 +33,7 @@ function nodeToObserverTree(node: IDepTreeNode): IObserverTree { name: node.name_ } if (hasObservers(node as any)) { - result.observers = Array.from(getObservers(node as any)).map(nodeToObserverTree) + result.observers = Array.from(getObservers(node as any), nodeToObserverTree) } return result } diff --git a/packages/mobx/src/api/flow.ts b/packages/mobx/src/api/flow.ts index 9a635858db..15694bbd79 100644 --- a/packages/mobx/src/api/flow.ts +++ b/packages/mobx/src/api/flow.ts @@ -4,13 +4,11 @@ import { die, isFunction, Annotation, - isStringish, - storeAnnotation, createFlowAnnotation, - createDecoratorAnnotation, - is20223Decorator + decorateFlow20223_, + assign } from "../internal" - +import { createDecoratorAnnotation, type DecoratorAnnotation } from "./decoratorannotation" import type { ClassMethodDecorator } from "../types/decorator_fills" export const FLOW = "flow" @@ -35,46 +33,44 @@ export function isFlowCancellationError(error: Error) { export type CancellablePromise = Promise & { cancel(): void } -type FlowGenerator = (...args: any[]) => Generator | AsyncGenerator +function createFlowDecoratorAnnotation( + annotation: Annotation +): DecoratorAnnotation { + return createDecoratorAnnotation(annotation, decorateFlow20223_) +} -// PropertyDecorator is only for legacy decorators -interface Flow extends Annotation, PropertyDecorator { - ( - value: Value, - context: ClassMethodDecoratorContext - ): Value | void +interface Flow extends Annotation, ClassMethodDecorator { ( - generator: (...args: Args) => Generator | AsyncGenerator + generator: (...args: Args) => Generator | AsyncGenerator, + context?: never ): (...args: Args) => CancellablePromise - bound: Annotation & PropertyDecorator & ClassMethodDecorator } const flowAnnotation = createFlowAnnotation("flow") const flowBoundAnnotation = createFlowAnnotation("flow.bound", { bound: true }) -export const flow: Flow = Object.assign( +export const flow: Flow = assign( function flow(arg1, arg2?) { - // @flow (2022.3 Decorators) - if (is20223Decorator(arg2)) { - return flowAnnotation.decorate_20223_(arg1, arg2) - } - // @flow - if (isStringish(arg2)) { - return storeAnnotation(arg1, arg2, flowAnnotation) + if (arg2 && typeof arg2.kind === "string") { + return decorateFlow20223_(flowAnnotation, arg1, arg2) } + // flow(fn) if (__DEV__ && arguments.length !== 1) { die(`Flow expects single argument with generator function`) } const generator = arg1 - const name = generator.name || "" + const name = generator.name || (__DEV__ ? "" : "flow") // Implementation based on https://github.com/tj/co/blob/master/index.js const res = function () { const ctx = this const args = arguments - const runId = ++generatorId - const gen = action(`${name} - runid: ${runId} - init`, generator).apply(ctx, args) + const runId = __DEV__ ? ++generatorId : 0 + const gen = action( + __DEV__ ? `${name} - runid: ${runId} - init` : name, + generator + ).apply(ctx, args) let rejector: (error: any) => void let pendingPromise: CancellablePromise | undefined = undefined @@ -87,7 +83,7 @@ export const flow: Flow = Object.assign( let ret try { ret = action( - `${name} - runid: ${runId} - yield ${stepId++}`, + __DEV__ ? `${name} - runid: ${runId} - yield ${stepId++}` : name, gen.next ).call(gen, res) } catch (e) { @@ -102,7 +98,7 @@ export const flow: Flow = Object.assign( let ret try { ret = action( - `${name} - runid: ${runId} - yield ${stepId++}`, + __DEV__ ? `${name} - runid: ${runId} - yield ${stepId++}` : name, gen.throw! ).call(gen, err) } catch (e) { @@ -127,7 +123,8 @@ export const flow: Flow = Object.assign( onFulfilled(undefined) // kick off the process }) as any - promise.cancel = action(`${name} - runid: ${runId} - cancel`, function () { + const cancelActionName = __DEV__ ? `${name} - runid: ${runId} - cancel` : name + promise.cancel = action(cancelActionName, function () { try { if (pendingPromise) { cancelPromise(pendingPromise) @@ -152,7 +149,7 @@ export const flow: Flow = Object.assign( flowAnnotation ) -flow.bound = createDecoratorAnnotation(flowBoundAnnotation) +export const flowBound = createFlowDecoratorAnnotation(flowBoundAnnotation) function cancelPromise(promise) { if (isFunction(promise.cancel)) { diff --git a/packages/mobx/src/api/intercept.ts b/packages/mobx/src/api/intercept.ts index 9ccb544e14..ee25998e9e 100644 --- a/packages/mobx/src/api/intercept.ts +++ b/packages/mobx/src/api/intercept.ts @@ -12,7 +12,8 @@ import { getAdministration, ObservableSet, ISetWillChange, - isFunction + isFunction, + registerInterceptor } from "../internal" export function intercept( @@ -51,9 +52,9 @@ export function intercept(thing, propOrHandler?, handler?): Lambda { } function interceptInterceptable(thing, handler) { - return getAdministration(thing).intercept_(handler) + return registerInterceptor(getAdministration(thing), handler) } function interceptProperty(thing, property, handler) { - return getAdministration(thing, property).intercept_(handler) + return registerInterceptor(getAdministration(thing, property), handler) } diff --git a/packages/mobx/src/api/makeObservable.ts b/packages/mobx/src/api/makeObservable.ts index 156161d241..81a430b2a8 100644 --- a/packages/mobx/src/api/makeObservable.ts +++ b/packages/mobx/src/api/makeObservable.ts @@ -4,15 +4,19 @@ import { AnnotationsMap, CreateObservableOptions, ObservableObjectAdministration, - collectStoredAnnotations, isPlainObject, isObservableObject, die, ownKeys, extendObservable, addHiddenProp, - storedAnnotationsSymbol, - initObservable + initObservable, + Annotation, + assertAnnotable, + getDescriptor, + MakeResult, + objectPrototype, + recordAnnotationApplied } from "../internal" // Hack based on https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-322267089 @@ -22,25 +26,16 @@ import { // Fixes: https://github.com/mobxjs/mobx/issues/2325#issuecomment-691070022 type NoInfer = [T][T extends any ? 0 : never] -type MakeObservableOptions = Omit - export function makeObservable( target: T, - annotations?: AnnotationsMap>, - options?: MakeObservableOptions + annotations: AnnotationsMap>, + options?: CreateObservableOptions ): T { initObservable(() => { const adm: ObservableObjectAdministration = asObservableObject(target, options)[$mobx] - if (__DEV__ && annotations && target[storedAnnotationsSymbol]) { - die( - `makeObservable second arg must be nullish when using decorators. Mixing @decorator syntax with annotations is not supported.` - ) - } - // Default to decorators - annotations ??= collectStoredAnnotations(target) // Annotate - ownKeys(annotations).forEach(key => adm.make_(key, annotations![key])) + ownKeys(annotations).forEach(key => make_(adm, key, annotations[key])) }) return target } @@ -51,7 +46,7 @@ const keysSymbol = Symbol("mobx-keys") export function makeAutoObservable( target: T, overrides?: AnnotationsMap>, - options?: MakeObservableOptions + options?: CreateObservableOptions ): T { if (__DEV__) { if (!isPlainObject(target) && !isPlainObject(Object.getPrototypeOf(target))) { @@ -82,7 +77,8 @@ export function makeAutoObservable - adm.make_( + make_( + adm, key, // must pass "undefined" for { key: undefined } !overrides ? true : key in overrides ? overrides[key] : true @@ -92,3 +88,35 @@ export function makeAutoObservable= obj.length) { diff --git a/packages/mobx/src/api/observable.ts b/packages/mobx/src/api/observable.ts index 738410ea60..eaa68e1bd8 100644 --- a/packages/mobx/src/api/observable.ts +++ b/packages/mobx/src/api/observable.ts @@ -24,20 +24,14 @@ import { shallowEnhancer, refStructEnhancer, AnnotationsMap, - asObservableObject, - storeAnnotation, - createDecoratorAnnotation, - createLegacyArray, - globalState, assign, - isStringish, createObservableAnnotation, createAutoAnnotation, - is20223Decorator, - initObservable + initObservable, + decorateObservable20223_ } from "../internal" - -import type { ClassAccessorDecorator, ClassFieldDecorator } from "../types/decorator_fills" +import { createDecoratorAnnotation, type DecoratorAnnotation } from "./decoratorannotation" +import type { ClassAccessorAndFieldDecorator } from "../types/decorator_fills" export const OBSERVABLE = "observable" export const OBSERVABLE_REF = "observable.ref" @@ -49,7 +43,6 @@ export type CreateObservableOptions = { equals?: IEqualsComparer deep?: boolean defaultDecorator?: Annotation - proxy?: boolean autoBind?: boolean } @@ -58,8 +51,7 @@ export type CreateObservableOptions = { export const defaultCreateObservableOptions: CreateObservableOptions = { deep: true, name: undefined, - defaultDecorator: undefined, - proxy: true + defaultDecorator: undefined } Object.freeze(defaultCreateObservableOptions) @@ -69,16 +61,20 @@ export function asCreateObservableOptions(thing: any): CreateObservableOptions { const observableAnnotation = createObservableAnnotation(OBSERVABLE) const observableRefAnnotation = createObservableAnnotation(OBSERVABLE_REF, { - enhancer: referenceEnhancer + enhancer_: referenceEnhancer }) const observableShallowAnnotation = createObservableAnnotation(OBSERVABLE_SHALLOW, { - enhancer: shallowEnhancer + enhancer_: shallowEnhancer }) const observableStructAnnotation = createObservableAnnotation(OBSERVABLE_STRUCT, { - enhancer: refStructEnhancer + enhancer_: refStructEnhancer }) -const observableDecoratorAnnotation = - createDecoratorAnnotation(observableAnnotation) + +function createObservableDecoratorAnnotation( + annotation: Annotation +): DecoratorAnnotation { + return createDecoratorAnnotation(annotation, decorateObservable20223_) +} export function getEnhancerFromOptions(options: CreateObservableOptions): IEnhancer { return options.deep === true @@ -95,7 +91,7 @@ export function getAnnotationFromOptions( } export function getEnhancerFromAnnotation(annotation?: Annotation): IEnhancer { - return !annotation ? deepEnhancer : annotation.options_?.enhancer ?? deepEnhancer + return !annotation ? deepEnhancer : annotation.options_?.enhancer_ ?? deepEnhancer } /** @@ -103,15 +99,8 @@ export function getEnhancerFromAnnotation(annotation?: Annotation): IEnhancer(value: T, options?: CreateObservableOptions): IObservableValue @@ -172,18 +160,13 @@ export interface IObservableMapFactory { > } -export interface IObservableFactory - extends Annotation, - PropertyDecorator, - ClassAccessorDecorator, - ClassFieldDecorator { - // TODO: remove ClassFieldDecorator, this is only temporarily support for legacy decorators +export interface IObservableFactory extends Annotation, ClassAccessorAndFieldDecorator { (value: T[], options?: CreateObservableOptions): IObservableArray (value: Set, options?: CreateObservableOptions): ObservableSet (value: Map, options?: CreateObservableOptions): ObservableMap ( value: T, - decorators?: AnnotationsMap, + annotations?: AnnotationsMap, options?: CreateObservableOptions ): T @@ -196,20 +179,9 @@ export interface IObservableFactory map: IObservableMapFactory object: ( props: T, - decorators?: AnnotationsMap, + annotations?: AnnotationsMap, options?: CreateObservableOptions ) => T - - /** - * Decorator that creates an observable that only observes the references, but doesn't try to turn the assigned value into an observable.ts. - */ - ref: Annotation & PropertyDecorator & ClassAccessorDecorator & ClassFieldDecorator - /** - * Decorator that creates an observable converts its value (objects, maps or arrays) into a shallow observable structure - */ - shallow: Annotation & PropertyDecorator & ClassAccessorDecorator & ClassFieldDecorator - deep: Annotation & PropertyDecorator & ClassAccessorDecorator & ClassFieldDecorator - struct: Annotation & PropertyDecorator & ClassAccessorDecorator & ClassFieldDecorator } const observableFactories: IObservableFactory = { @@ -219,11 +191,7 @@ const observableFactories: IObservableFactory = { }, array(initialValues?: T[], options?: CreateObservableOptions): IObservableArray { const o = asCreateObservableOptions(options) - return ( - globalState.useProxies === false || o.proxy === false - ? createLegacyArray - : createObservableArray - )(initialValues, getEnhancerFromOptions(o), o.name) + return createObservableArray(initialValues, getEnhancerFromOptions(o), o.name) }, map( initialValues?: IObservableMapInitialValues, @@ -241,24 +209,23 @@ const observableFactories: IObservableFactory = { }, object( props: T, - decorators?: AnnotationsMap, + annotations?: AnnotationsMap, options?: CreateObservableOptions ): T { return initObservable(() => - extendObservable( - globalState.useProxies === false || options?.proxy === false - ? asObservableObject({}, options) - : asDynamicObservableObject({}, options), - props, - decorators - ) + extendObservable(asDynamicObservableObject({}, options), props, annotations) ) - }, - ref: createDecoratorAnnotation(observableRefAnnotation), - shallow: createDecoratorAnnotation(observableShallowAnnotation), - deep: observableDecoratorAnnotation, - struct: createDecoratorAnnotation(observableStructAnnotation) + } } as any +export const observableRef = createObservableDecoratorAnnotation(observableRefAnnotation) +export const observableShallow = createObservableDecoratorAnnotation(observableShallowAnnotation) +export const observableDeep = createObservableDecoratorAnnotation(observableAnnotation) +export const observableStruct = createObservableDecoratorAnnotation(observableStructAnnotation) + // eslint-disable-next-line -export var observable: IObservableFactory = assign(createObservable, observableFactories) +export var observable: IObservableFactory = assign( + createObservable, + observableAnnotation, + observableFactories +) diff --git a/packages/mobx/src/api/observe.ts b/packages/mobx/src/api/observe.ts index f3c4c7c5bb..f602406874 100644 --- a/packages/mobx/src/api/observe.ts +++ b/packages/mobx/src/api/observe.ts @@ -11,7 +11,18 @@ import { getAdministration, ObservableSet, ISetDidChange, - isFunction + isFunction, + isComputedValue, + isObservableArray, + isObservableMap, + isObservableObject, + isObservableSet, + autorun, + registerListener, + untrackedEnd, + untrackedStart, + UPDATE, + die } from "../internal" export function observe( @@ -61,9 +72,77 @@ export function observe(thing, propOrCb?, cbOrFire?, fireImmediately?): Lambda { } function observeObservable(thing, listener, fireImmediately: boolean) { - return getAdministration(thing).observe_(listener, fireImmediately) + const adm = getAdministration(thing) + + if (isObservableArray(thing)) { + if (fireImmediately) { + listener({ + observableKind: "array", + object: adm.proxy_, + debugObjectName: adm.atom_.name_, + type: "splice", + index: 0, + added: adm.values_.slice(), + addedCount: adm.values_.length, + removed: [], + removedCount: 0 + }) + } + } else if (isObservableMap(thing)) { + if (__DEV__ && fireImmediately === true) { + die("`observe` doesn't support fireImmediately=true in combination with maps.") + } + } else if (isObservableSet(thing)) { + if (__DEV__ && fireImmediately === true) { + die("`observe` doesn't support fireImmediately=true in combination with sets.") + } + } else if (isObservableObject(thing)) { + if (__DEV__ && fireImmediately === true) { + die("`observe` doesn't support the fire immediately property for observable objects.") + } + } else { + return observeValue(adm, listener, fireImmediately) + } + + return registerListener(adm, listener) } function observeObservableProperty(thing, property, listener, fireImmediately: boolean) { - return getAdministration(thing, property).observe_(listener, fireImmediately) + return observeValue(getAdministration(thing, property), listener, fireImmediately) +} + +function observeValue(adm, listener, fireImmediately: boolean) { + if (isComputedValue(adm)) { + let firstTime = true + let prevValue: any = undefined + return autorun(() => { + const newValue = adm.get() + if (!firstTime || fireImmediately) { + const prevU = untrackedStart() + listener({ + observableKind: "computed", + debugObjectName: adm.name_, + type: UPDATE, + object: adm, + newValue, + oldValue: prevValue + }) + untrackedEnd(prevU) + } + firstTime = false + prevValue = newValue + }) + } + + if (fireImmediately) { + listener({ + observableKind: "value", + debugObjectName: adm.name_, + object: adm, + type: UPDATE, + newValue: adm.value_, + oldValue: undefined + }) + } + return registerListener(adm, listener) } diff --git a/packages/mobx/src/api/trace.ts b/packages/mobx/src/api/trace.ts deleted file mode 100644 index c304329b3d..0000000000 --- a/packages/mobx/src/api/trace.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TraceMode, die, getAtom, globalState } from "../internal" - -export function trace(thing?: any, prop?: string, enterBreakPoint?: boolean): void -export function trace(thing?: any, enterBreakPoint?: boolean): void -export function trace(enterBreakPoint?: boolean): void -export function trace(...args: any[]): void { - if (!__DEV__) { - return - } - let enterBreakPoint = false - if (typeof args[args.length - 1] === "boolean") { - enterBreakPoint = args.pop() - } - const derivation = getAtomFromArgs(args) - if (!derivation) { - return die( - `'trace(break?)' can only be used inside a tracked computed value or a Reaction. Consider passing in the computed value or reaction explicitly` - ) - } - if (derivation.isTracing_ === TraceMode.NONE) { - console.log(`[mobx.trace] '${derivation.name_}' tracing enabled`) - } - derivation.isTracing_ = enterBreakPoint ? TraceMode.BREAK : TraceMode.LOG -} - -function getAtomFromArgs(args): any { - switch (args.length) { - case 0: - return globalState.trackingDerivation - case 1: - return getAtom(args[0]) - case 2: - return getAtom(args[0], args[1]) - } -} diff --git a/packages/mobx/src/api/when.ts b/packages/mobx/src/api/when.ts index 66985a1af6..3c0f598aa1 100644 --- a/packages/mobx/src/api/when.ts +++ b/packages/mobx/src/api/when.ts @@ -7,7 +7,8 @@ import { getNextId, die, allowStateChanges, - GenericAbortSignal + GenericAbortSignal, + assign } from "../internal" export interface IWhenOptions { @@ -77,12 +78,12 @@ function whenPromise( return die(`the options 'onError' and 'promise' cannot be combined`) } if (opts?.signal?.aborted) { - return Object.assign(Promise.reject(new Error("WHEN_ABORTED")), { cancel: () => null }) + return assign(Promise.reject(new Error("WHEN_ABORTED")), { cancel: () => null }) } let cancel let abort const res = new Promise((resolve, reject) => { - let disposer = _when(predicate, resolve as Lambda, { ...opts, onError: reject }) + let disposer = _when(predicate, resolve as Lambda, assign({}, opts, { onError: reject })) cancel = () => { disposer() reject(new Error("WHEN_CANCELLED")) diff --git a/packages/mobx/src/core/action.ts b/packages/mobx/src/core/action.ts index ec8ab01e63..50dd708fa4 100644 --- a/packages/mobx/src/core/action.ts +++ b/packages/mobx/src/core/action.ts @@ -96,7 +96,7 @@ export function _startAction( ): IActionRunInfo { const notifySpy_ = __DEV__ && isSpyEnabled() && !!actionName let startTime_: number = 0 - if (__DEV__ && notifySpy_) { + if (notifySpy_) { startTime_ = Date.now() const flattenedArgs = args ? Array.from(args) : EMPTY_ARRAY spyReportStart({ @@ -112,9 +112,14 @@ export function _startAction( let prevAllowStateChanges_ = globalState.allowStateChanges // by default preserve previous allow if (runAsAction) { untrackedStart() - prevAllowStateChanges_ = allowStateChangesStart(true) + if (__DEV__) { + prevAllowStateChanges_ = allowStateChangesStart(true) + } + } + const prevAllowStateReads_ = globalState.allowStateReads + if (__DEV__) { + allowStateReadsStart(true) } - const prevAllowStateReads_ = allowStateReadsStart(true) const runInfo = { runAsAction_: runAsAction, prevDerivation_, @@ -138,8 +143,10 @@ export function _endAction(runInfo: IActionRunInfo) { if (runInfo.error_ !== undefined) { globalState.suppressReactionErrors = true } - allowStateChangesEnd(runInfo.prevAllowStateChanges_) - allowStateReadsEnd(runInfo.prevAllowStateReads_) + if (__DEV__) { + allowStateChangesEnd(runInfo.prevAllowStateChanges_) + allowStateReadsEnd(runInfo.prevAllowStateReads_) + } endBatch() if (runInfo.runAsAction_) { untrackedEnd(runInfo.prevDerivation_) diff --git a/packages/mobx/src/core/atom.ts b/packages/mobx/src/core/atom.ts index 2df57def5c..4000a91248 100644 --- a/packages/mobx/src/core/atom.ts +++ b/packages/mobx/src/core/atom.ts @@ -6,8 +6,6 @@ import { endBatch, getNextId, noop, - onBecomeObserved, - onBecomeUnobserved, propagateChanged, reportObserved, startBatch, @@ -23,10 +21,13 @@ export interface IAtom extends IObservable { reportChanged(): void } +const enum AtomFlags { + isBeingObserved = 0b001, + isPendingUnobservation = 0b010, + diffValue = 0b100 +} + export class Atom implements IAtom { - private static readonly isBeingObservedMask_ = 0b001 - private static readonly isPendingUnobservationMask_ = 0b010 - private static readonly diffValueMask_ = 0b100 private flags_ = 0b000 observers_ = new Set() @@ -41,24 +42,24 @@ export class Atom implements IAtom { // for effective unobserving. BaseAtom has true, for extra optimization, so its onBecomeUnobserved never gets called, because it's not needed get isBeingObserved(): boolean { - return getFlag(this.flags_, Atom.isBeingObservedMask_) + return getFlag(this.flags_, AtomFlags.isBeingObserved) } set isBeingObserved(newValue: boolean) { - this.flags_ = setFlag(this.flags_, Atom.isBeingObservedMask_, newValue) + this.flags_ = setFlag(this.flags_, AtomFlags.isBeingObserved, newValue) } get isPendingUnobservation(): boolean { - return getFlag(this.flags_, Atom.isPendingUnobservationMask_) + return getFlag(this.flags_, AtomFlags.isPendingUnobservation) } set isPendingUnobservation(newValue: boolean) { - this.flags_ = setFlag(this.flags_, Atom.isPendingUnobservationMask_, newValue) + this.flags_ = setFlag(this.flags_, AtomFlags.isPendingUnobservation, newValue) } get diffValue(): 0 | 1 { - return getFlag(this.flags_, Atom.diffValueMask_) ? 1 : 0 + return getFlag(this.flags_, AtomFlags.diffValue) ? 1 : 0 } set diffValue(newValue: 0 | 1) { - this.flags_ = setFlag(this.flags_, Atom.diffValueMask_, newValue === 1 ? true : false) + this.flags_ = setFlag(this.flags_, AtomFlags.diffValue, newValue === 1 ? true : false) } // onBecomeObservedListeners @@ -110,11 +111,11 @@ export function createAtom( const atom = new Atom(name) // default `noop` listener will not initialize the hook Set if (onBecomeObservedHandler !== noop) { - onBecomeObserved(atom, onBecomeObservedHandler) + atom.onBOL = new Set([onBecomeObservedHandler]) } if (onBecomeUnobservedHandler !== noop) { - onBecomeUnobserved(atom, onBecomeUnobservedHandler) + atom.onBUOL = new Set([onBecomeUnobservedHandler]) } return atom } diff --git a/packages/mobx/src/core/computedvalue.ts b/packages/mobx/src/core/computedvalue.ts index cec036d6c1..d570d79556 100644 --- a/packages/mobx/src/core/computedvalue.ts +++ b/packages/mobx/src/core/computedvalue.ts @@ -5,10 +5,8 @@ import { IEqualsComparer, IObservable, Lambda, - TraceMode, - autorun, clearObserving, - comparer, + compareDefault, createAction, createInstanceofPredicate, endBatch, @@ -24,9 +22,6 @@ import { startBatch, toPrimitive, trackDerivedFunction, - untrackedEnd, - untrackedStart, - UPDATE, die, allowStateChangesStart, allowStateChangesEnd @@ -77,6 +72,14 @@ export type IComputedDidChange = { * * If at any point it's outside batch and it isn't observed: reset everything and go to 1. */ +const enum ComputedValueFlags { + isComputing = 0b00001, + isRunningSetter = 0b00010, + isBeingObserved = 0b00100, + isPendingUnobservation = 0b01000, + diffValue = 0b10000 +} + export class ComputedValue implements IObservable, IComputedValue, IDerivation { dependenciesState_ = IDerivationState_.NOT_TRACKING_ observing_: IObservable[] = [] // nodes we are looking at. Our value depends on these nodes @@ -90,16 +93,10 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva name_: string triggeredBy_?: string - private static readonly isComputingMask_ = 0b00001 - private static readonly isRunningSetterMask_ = 0b00010 - private static readonly isBeingObservedMask_ = 0b00100 - private static readonly isPendingUnobservationMask_ = 0b01000 - private static readonly diffValueMask_ = 0b10000 private flags_ = 0b00000 derivation: () => T // N.B: unminified as it is used by MST setter_?: (value: T) => void - isTracing_: TraceMode = TraceMode.NONE scope_: Object | undefined private equals_: IEqualsComparer private requiresReaction_: boolean | undefined @@ -110,11 +107,9 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva * * The `name` property is for debug purposes only. * - * The `equals` property specifies the comparer function to use to determine if a newly produced - * value differs from the previous value. Two comparers are provided in the library; `defaultComparer` - * compares based on identity comparison (===), and `structuralComparer` deeply compares the structure. - * Structural comparison can be convenient if you always produce a new aggregated object and - * don't want to notify observers if it is structurally the same. + * The `equals` property specifies the comparer function used to determine if a newly produced + * value differs from the previous value. Structural comparison can be convenient if you always + * produce a new aggregated object and don't want to notify observers if it is structurally the same. * This is useful for working with vectors, mouse coordinates etc. */ constructor(options: IComputedValueOptions) { @@ -129,11 +124,7 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva options.set ) as any } - this.equals_ = - options.equals || - ((options as any).compareStructural || (options as any).struct - ? comparer.structural - : comparer.default) + this.equals_ = options.equals || compareDefault this.scope_ = options.context this.requiresReaction_ = options.requiresReaction this.keepAlive_ = !!options.keepAlive @@ -160,40 +151,40 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva // to check for cycles private get isComputing(): boolean { - return getFlag(this.flags_, ComputedValue.isComputingMask_) + return getFlag(this.flags_, ComputedValueFlags.isComputing) } private set isComputing(newValue: boolean) { - this.flags_ = setFlag(this.flags_, ComputedValue.isComputingMask_, newValue) + this.flags_ = setFlag(this.flags_, ComputedValueFlags.isComputing, newValue) } private get isRunningSetter(): boolean { - return getFlag(this.flags_, ComputedValue.isRunningSetterMask_) + return getFlag(this.flags_, ComputedValueFlags.isRunningSetter) } private set isRunningSetter(newValue: boolean) { - this.flags_ = setFlag(this.flags_, ComputedValue.isRunningSetterMask_, newValue) + this.flags_ = setFlag(this.flags_, ComputedValueFlags.isRunningSetter, newValue) } get isBeingObserved(): boolean { - return getFlag(this.flags_, ComputedValue.isBeingObservedMask_) + return getFlag(this.flags_, ComputedValueFlags.isBeingObserved) } set isBeingObserved(newValue: boolean) { - this.flags_ = setFlag(this.flags_, ComputedValue.isBeingObservedMask_, newValue) + this.flags_ = setFlag(this.flags_, ComputedValueFlags.isBeingObserved, newValue) } get isPendingUnobservation(): boolean { - return getFlag(this.flags_, ComputedValue.isPendingUnobservationMask_) + return getFlag(this.flags_, ComputedValueFlags.isPendingUnobservation) } set isPendingUnobservation(newValue: boolean) { - this.flags_ = setFlag(this.flags_, ComputedValue.isPendingUnobservationMask_, newValue) + this.flags_ = setFlag(this.flags_, ComputedValueFlags.isPendingUnobservation, newValue) } get diffValue(): 0 | 1 { - return getFlag(this.flags_, ComputedValue.diffValueMask_) ? 1 : 0 + return getFlag(this.flags_, ComputedValueFlags.diffValue) ? 1 : 0 } set diffValue(newValue: 0 | 1) { this.flags_ = setFlag( this.flags_, - ComputedValue.diffValueMask_, + ComputedValueFlags.diffValue, newValue === 1 ? true : false ) } @@ -289,7 +280,7 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva computeValue_(track: boolean) { this.isComputing = true // don't allow state changes during computation - const prev = allowStateChangesStart(false) + const prev = __DEV__ ? allowStateChangesStart(false) : false let res: T | CaughtException if (track) { res = trackDerivedFunction(this, this.derivation, this.scope_) @@ -304,7 +295,9 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva } } } - allowStateChangesEnd(prev) + if (__DEV__) { + allowStateChangesEnd(prev) + } this.isComputing = false return res } @@ -313,46 +306,13 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva if (!this.keepAlive_) { clearObserving(this) this.value_ = undefined // don't hold on to computed value! - if (__DEV__ && this.isTracing_ !== TraceMode.NONE) { - console.log( - `[mobx.trace] Computed value '${this.name_}' was suspended and it will recompute on the next access.` - ) - } } } - observe_(listener: (change: IComputedDidChange) => void, fireImmediately?: boolean): Lambda { - let firstTime = true - let prevValue: T | undefined = undefined - return autorun(() => { - // TODO: why is this in a different place than the spyReport() function? in all other observables it's called in the same place - let newValue = this.get() - if (!firstTime || fireImmediately) { - const prevU = untrackedStart() - listener({ - observableKind: "computed", - debugObjectName: this.name_, - type: UPDATE, - object: this, - newValue, - oldValue: prevValue - }) - untrackedEnd(prevU) - } - firstTime = false - prevValue = newValue - }) - } - warnAboutUntrackedRead_() { if (!__DEV__) { return } - if (this.isTracing_ !== TraceMode.NONE) { - console.log( - `[mobx.trace] Computed value '${this.name_}' is being read outside a reactive context. Doing a full recompute.` - ) - } if ( typeof this.requiresReaction_ === "boolean" ? this.requiresReaction_ diff --git a/packages/mobx/src/core/derivation.ts b/packages/mobx/src/core/derivation.ts index b1c2d25048..4ba8c6eb47 100644 --- a/packages/mobx/src/core/derivation.ts +++ b/packages/mobx/src/core/derivation.ts @@ -8,7 +8,7 @@ import { removeObserver } from "../internal" -export enum IDerivationState_ { +export const enum IDerivationState_ { // before being run or (outside batch and not being observed) // at this point derivation is not holding any data about dependency tree NOT_TRACKING_ = -1, @@ -28,12 +28,6 @@ export enum IDerivationState_ { STALE_ = 2 } -export enum TraceMode { - NONE, - LOG, - BREAK -} - /** * A derivation is everything that can be derived from the state (all the atoms) in a pure manner. * See https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.xvbh6qd74 @@ -52,7 +46,6 @@ export interface IDerivation extends IDepTreeNode { */ unboundDepsCount_: number onBecomeStale_(): void - isTracing_: TraceMode /** * warn if the derivation has no dependencies after creation/update @@ -90,7 +83,7 @@ export function shouldCompute(derivation: IDerivation): boolean { return true case IDerivationState_.POSSIBLY_STALE_: { // state propagation can occur outside of action/reactive context #2195 - const prevAllowStateReads = allowStateReadsStart(true) + const prevAllowStateReads = __DEV__ ? allowStateReadsStart(true) : true const prevUntracked = untrackedStart() // no need for those computeds to be reported, they will be picked up in trackDerivedFunction. const obs = derivation.observing_, l = obs.length @@ -105,7 +98,9 @@ export function shouldCompute(derivation: IDerivation): boolean { } catch (e) { // we are not interested in the value *or* exception at this moment, but if there is one, notify all untrackedEnd(prevUntracked) - allowStateReadsEnd(prevAllowStateReads) + if (__DEV__) { + allowStateReadsEnd(prevAllowStateReads) + } return true } } @@ -114,14 +109,18 @@ export function shouldCompute(derivation: IDerivation): boolean { // invariantShouldCompute(derivation) if ((derivation.dependenciesState_ as any) === IDerivationState_.STALE_) { untrackedEnd(prevUntracked) - allowStateReadsEnd(prevAllowStateReads) + if (__DEV__) { + allowStateReadsEnd(prevAllowStateReads) + } return true } } } changeDependenciesStateTo0(derivation) untrackedEnd(prevUntracked) - allowStateReadsEnd(prevAllowStateReads) + if (__DEV__) { + allowStateReadsEnd(prevAllowStateReads) + } return false } } @@ -165,7 +164,7 @@ export function checkIfStateReadsAreAllowed(observable: IObservable) { * as observer of any of the accessed observables. */ export function trackDerivedFunction(derivation: IDerivation, f: () => T, context: any) { - const prevAllowStateReads = allowStateReadsStart(true) + const prevAllowStateReads = __DEV__ ? allowStateReadsStart(true) : true changeDependenciesStateTo0(derivation) // Preallocate array; will be trimmed by bindDependencies. derivation.newObserving_ = new Array( @@ -193,7 +192,9 @@ export function trackDerivedFunction(derivation: IDerivation, f: () => T, con bindDependencies(derivation) warnAboutDerivationWithoutDependencies(derivation) - allowStateReadsEnd(prevAllowStateReads) + if (__DEV__) { + allowStateReadsEnd(prevAllowStateReads) + } return result } diff --git a/packages/mobx/src/core/globalstate.ts b/packages/mobx/src/core/globalstate.ts index 35de9dfea8..a1c4de6475 100644 --- a/packages/mobx/src/core/globalstate.ts +++ b/packages/mobx/src/core/globalstate.ts @@ -1,6 +1,8 @@ -import { IDerivation, IObservable, Reaction, die, getGlobal } from "../internal" +import { IDerivation, IObservable, Reaction, die } from "../internal" import { ComputedValue } from "./computedvalue" +const MOBX_GLOBALS_VERSION = 7 + /** * These values will persist if global state is reset */ @@ -14,8 +16,7 @@ const persistentKeys: (keyof MobXGlobals)[] = [ "allowStateReads", "disableErrorBoundaries", "runId", - "UNCHANGED", - "useProxies" + "UNCHANGED" ] export type IUNCHANGED = {} @@ -29,7 +30,7 @@ export class MobXGlobals { * N.B: this version is unrelated to the package version of MobX, and is only the version of the * internal state storage of MobX, and can be the same across many different package versions */ - version = 6 + version = MOBX_GLOBALS_VERSION /** * globally unique token to signal unchanged @@ -138,12 +139,6 @@ export class MobXGlobals { */ suppressReactionErrors = false - useProxies = true - /* - * print warnings about code that would fail if proxies weren't available - */ - verifyProxies = false - /** * False forces all object's descriptors to * writable: true @@ -156,11 +151,11 @@ let canMergeGlobalState = true let isolateCalled = false export let globalState: MobXGlobals = (function () { - let global = getGlobal() + let global = globalThis as any if (global.__mobxInstanceCount > 0 && !global.__mobxGlobals) { canMergeGlobalState = false } - if (global.__mobxGlobals && global.__mobxGlobals.version !== new MobXGlobals().version) { + if (global.__mobxGlobals && global.__mobxGlobals.version !== MOBX_GLOBALS_VERSION) { canMergeGlobalState = false } @@ -195,7 +190,7 @@ export function isolateGlobalState() { } isolateCalled = true if (canMergeGlobalState) { - let global = getGlobal() + let global = globalThis as any if (--global.__mobxInstanceCount === 0) { global.__mobxGlobals = undefined } diff --git a/packages/mobx/src/core/observable.ts b/packages/mobx/src/core/observable.ts index 5ddbc74c86..b79c639391 100644 --- a/packages/mobx/src/core/observable.ts +++ b/packages/mobx/src/core/observable.ts @@ -1,11 +1,8 @@ import { Lambda, ComputedValue, - IDependencyTree, IDerivation, IDerivationState_, - TraceMode, - getDependencyTree, globalState, runReactions, checkIfStateReadsAreAllowed @@ -192,9 +189,6 @@ export function propagateChanged(observable: IObservable) { // Ideally we use for..of here, but the downcompiled version is really slow... observable.observers_.forEach(d => { if (d.dependenciesState_ === IDerivationState_.UP_TO_DATE_) { - if (__DEV__ && d.isTracing_ !== TraceMode.NONE) { - logTraceInfo(d, observable) - } d.onBecomeStale_() } d.dependenciesState_ = IDerivationState_.STALE_ @@ -213,9 +207,6 @@ export function propagateChangeConfirmed(observable: IObservable) { observable.observers_.forEach(d => { if (d.dependenciesState_ === IDerivationState_.POSSIBLY_STALE_) { d.dependenciesState_ = IDerivationState_.STALE_ - if (__DEV__ && d.isTracing_ !== TraceMode.NONE) { - logTraceInfo(d, observable) - } } else if ( d.dependenciesState_ === IDerivationState_.UP_TO_DATE_ // this happens during computing of `d`, just keep lowestObserverState up to date. ) { @@ -241,42 +232,3 @@ export function propagateMaybeChanged(observable: IObservable) { }) // invariantLOS(observable, "maybe end"); } - -function logTraceInfo(derivation: IDerivation, observable: IObservable) { - console.log( - `[mobx.trace] '${derivation.name_}' is invalidated due to a change in: '${observable.name_}'` - ) - if (derivation.isTracing_ === TraceMode.BREAK) { - const lines = [] - printDepTree(getDependencyTree(derivation), lines, 1) - - // prettier-ignore - new Function( -`debugger; -/* -Tracing '${derivation.name_}' - -You are entering this break point because derivation '${derivation.name_}' is being traced and '${observable.name_}' is now forcing it to update. -Just follow the stacktrace you should now see in the devtools to see precisely what piece of your code is causing this update -The stackframe you are looking for is at least ~6-8 stack-frames up. - -${derivation instanceof ComputedValue ? derivation.derivation.toString().replace(/[*]\//g, "/") : ""} - -The dependencies for this derivation are: - -${lines.join("\n")} -*/ - `)() - } -} - -function printDepTree(tree: IDependencyTree, lines: string[], depth: number) { - if (lines.length >= 1000) { - lines.push("(and many more)") - return - } - lines.push(`${"\t".repeat(depth - 1)}${tree.name}`) - if (tree.dependencies) { - tree.dependencies.forEach(child => printDepTree(child, lines, depth + 1)) - } -} diff --git a/packages/mobx/src/core/reaction.ts b/packages/mobx/src/core/reaction.ts index d0c85b2d21..21382c85b6 100644 --- a/packages/mobx/src/core/reaction.ts +++ b/packages/mobx/src/core/reaction.ts @@ -4,7 +4,6 @@ import { IDerivationState_, IObservable, Lambda, - TraceMode, clearObserving, createInstanceofPredicate, endBatch, @@ -17,7 +16,6 @@ import { spyReportEnd, spyReportStart, startBatch, - trace, trackDerivedFunction, GenericAbortSignal } from "../internal" @@ -45,7 +43,6 @@ import { getFlag, setFlag } from "../utils/utils" export interface IReactionPublic { dispose(): void - trace(enterBreakPoint?: boolean): void } export interface IReactionDisposer { @@ -53,6 +50,14 @@ export interface IReactionDisposer { [$mobx]: Reaction } +const enum ReactionFlags { + isDisposed = 0b00001, + isScheduled = 0b00010, + isTrackPending = 0b00100, + isRunning = 0b01000, + diffValue = 0b10000 +} + export class Reaction implements IDerivation, IReactionPublic { observing_: IObservable[] = [] // nodes we are looking at. Our value depends on these nodes newObserving_: IObservable[] = [] @@ -60,15 +65,8 @@ export class Reaction implements IDerivation, IReactionPublic { runId_ = 0 unboundDepsCount_ = 0 - private static readonly isDisposedMask_ = 0b00001 - private static readonly isScheduledMask_ = 0b00010 - private static readonly isTrackPendingMask_ = 0b00100 - private static readonly isRunningMask_ = 0b01000 - private static readonly diffValueMask_ = 0b10000 private flags_ = 0b00000 - isTracing_: TraceMode = TraceMode.NONE - constructor( public name_: string = __DEV__ ? "Reaction@" + getNextId() : "Reaction", private onInvalidate_: () => void, @@ -77,38 +75,38 @@ export class Reaction implements IDerivation, IReactionPublic { ) {} get isDisposed() { - return getFlag(this.flags_, Reaction.isDisposedMask_) + return getFlag(this.flags_, ReactionFlags.isDisposed) } set isDisposed(newValue: boolean) { - this.flags_ = setFlag(this.flags_, Reaction.isDisposedMask_, newValue) + this.flags_ = setFlag(this.flags_, ReactionFlags.isDisposed, newValue) } get isScheduled() { - return getFlag(this.flags_, Reaction.isScheduledMask_) + return getFlag(this.flags_, ReactionFlags.isScheduled) } set isScheduled(newValue: boolean) { - this.flags_ = setFlag(this.flags_, Reaction.isScheduledMask_, newValue) + this.flags_ = setFlag(this.flags_, ReactionFlags.isScheduled, newValue) } get isTrackPending() { - return getFlag(this.flags_, Reaction.isTrackPendingMask_) + return getFlag(this.flags_, ReactionFlags.isTrackPending) } set isTrackPending(newValue: boolean) { - this.flags_ = setFlag(this.flags_, Reaction.isTrackPendingMask_, newValue) + this.flags_ = setFlag(this.flags_, ReactionFlags.isTrackPending, newValue) } get isRunning() { - return getFlag(this.flags_, Reaction.isRunningMask_) + return getFlag(this.flags_, ReactionFlags.isRunning) } set isRunning(newValue: boolean) { - this.flags_ = setFlag(this.flags_, Reaction.isRunningMask_, newValue) + this.flags_ = setFlag(this.flags_, ReactionFlags.isRunning, newValue) } get diffValue(): 0 | 1 { - return getFlag(this.flags_, Reaction.diffValueMask_) ? 1 : 0 + return getFlag(this.flags_, ReactionFlags.diffValue) ? 1 : 0 } set diffValue(newValue: 0 | 1) { - this.flags_ = setFlag(this.flags_, Reaction.diffValueMask_, newValue === 1 ? true : false) + this.flags_ = setFlag(this.flags_, ReactionFlags.diffValue, newValue === 1 ? true : false) } onBecomeStale_() { @@ -159,7 +157,7 @@ export class Reaction implements IDerivation, IReactionPublic { // console.warn("Reaction already disposed") // Note: Not a warning / error in mobx 4 either } startBatch() - const notify = isSpyEnabled() + const notify = __DEV__ && isSpyEnabled() let startTime if (__DEV__ && notify) { startTime = Date.now() @@ -250,10 +248,6 @@ export class Reaction implements IDerivation, IReactionPublic { toString() { return `Reaction[${this.name_}]` } - - trace(enterBreakPoint: boolean = false) { - trace(this, enterBreakPoint) - } } export function onReactionError(handler: (error: any, derivation: IDerivation) => void): Lambda { diff --git a/packages/mobx/src/core/spy.ts b/packages/mobx/src/core/spy.ts index 2267c5cf0e..7344ffe010 100644 --- a/packages/mobx/src/core/spy.ts +++ b/packages/mobx/src/core/spy.ts @@ -2,7 +2,7 @@ import { IComputedDidChange } from "./computedvalue" import { IValueDidChange, IBoxDidChange } from "./../types/observablevalue" import { IObjectDidChange } from "./../types/observableobject" import { IArrayDidChange } from "./../types/observablearray" -import { Lambda, globalState, once, ISetDidChange, IMapDidChange } from "../internal" +import { Lambda, globalState, once, ISetDidChange, IMapDidChange, assign } from "../internal" export function isSpyEnabled() { return __DEV__ && !!globalState.spyListeners.length @@ -41,7 +41,7 @@ export function spyReportStart(event: PureSpyEvent) { if (!__DEV__) { return } - const change = { ...event, spyReportStart: true as const } + const change = assign({}, event, { spyReportStart: true as const }) spyReport(change) } @@ -52,7 +52,7 @@ export function spyReportEnd(change?: { time?: number }) { return } if (change) { - spyReport({ ...change, type: "report-end", spyReportEnd: true }) + spyReport(assign({}, change, { type: "report-end" as const, spyReportEnd: true as const })) } else { spyReport(END_EVENT) } diff --git a/packages/mobx/src/errors.ts b/packages/mobx/src/errors.ts index 9ec23a018c..d642d6389e 100644 --- a/packages/mobx/src/errors.ts +++ b/packages/mobx/src/errors.ts @@ -26,10 +26,6 @@ export const niceErrors = { 14: "Intercept handlers should return nothing or a change object", 15: `Observable arrays cannot be frozen. If you're passing observables to 3rd party component/function that calls Object.freeze, pass copy instead: toJS(observable)`, 16: `Modification exception: the internal structure of an observable array was changed.`, - 17(index, length) { - return `[mobx.array] Index out of bounds, ${index} is larger than ${length}` - }, - 18: "mobx.map requires Map polyfill for the current browser. Check babel-polyfill or core-js/es6/map.js", 19(other) { return "Cannot initialize from classes that inherit from Map: " + other.constructor.name }, @@ -39,7 +35,6 @@ export const niceErrors = { 21(dataStructure) { return `Cannot convert to map from '${dataStructure}'` }, - 22: "mobx.set requires Set polyfill for the current browser. Check babel-polyfill or core-js/es6/set.js", 23: "It is not possible to get index atoms from arrays", 24(thing) { return "Cannot obtain administration from " + thing @@ -72,7 +67,25 @@ export const niceErrors = { return `[mobx] \`observableArray.${method}()\` mutates the array in-place, which is not allowed inside a derivation. Use \`array.slice().${method}()\` instead` }, 38: "'ownKeys()' can only be used on observable objects", - 39: "'defineProperty()' can only be used on observable objects" + 39: "'defineProperty()' can only be used on observable objects", + 40(length) { + return "Out of range: " + length + }, + 41(other) { + return "Cannot initialize set from " + other + }, + 42(key) { + return `Invalid index: '${key}'` + }, + 43(annotationType, name, kind) { + return ( + `Cannot apply '${annotationType}' to '${name}' (kind: ${kind}):` + + `\n'${annotationType}' can only be used on properties with a function value.` + ) + }, + 44(annotationType) { + return `'${annotationType}' can only be used with 'makeObservable'` + } } as const const errors: typeof niceErrors = __DEV__ ? niceErrors : ({} as any) @@ -84,10 +97,8 @@ export function die(error: string | keyof typeof errors, ...args: any[]): never throw new Error(`[MobX] ${e}`) } throw new Error( - typeof error === "number" - ? `[MobX] minified error nr: ${error}${ - args.length ? " " + args.map(String).join(",") : "" - }. See http://mobx.js.org/errors` - : `[MobX] ${error}` + `[MobX] minified error nr: ${error}${ + args.length ? " " + args.map(String).join(",") : "" + }. See mobx.js.org/errors` ) } diff --git a/packages/mobx/src/internal.ts b/packages/mobx/src/internal.ts index 48885069a4..0ab576def3 100644 --- a/packages/mobx/src/internal.ts +++ b/packages/mobx/src/internal.ts @@ -5,7 +5,6 @@ it will cause undefined errors (for example because super classes or local varia With this file that will still happen, but at least in this file we can magically reorder the imports with trial and error until the build succeeds again. */ -export * from "./utils/global" export { die } from "./errors" export * from "./utils/utils" export * from "./api/decorators" @@ -43,7 +42,6 @@ export * from "./api/isobservable" export * from "./api/object-api" export * from "./api/observe" export * from "./api/tojs" -export * from "./api/trace" export * from "./api/transaction" export * from "./api/when" export * from "./types/dynamicobject" @@ -54,7 +52,6 @@ export * from "./types/observablearray" export * from "./types/observablemap" export * from "./types/observableset" export * from "./types/observableobject" -export * from "./types/legacyobservablearray" export * from "./types/type-utils" export * from "./utils/eq" export * from "./utils/iterable" diff --git a/packages/mobx/src/mobx.ts b/packages/mobx/src/mobx.ts index cb366b52a9..d3cd161c5f 100644 --- a/packages/mobx/src/mobx.ts +++ b/packages/mobx/src/mobx.ts @@ -16,13 +16,14 @@ * */ import { die } from "./errors" -import { getGlobal } from "./utils/global" -;["Symbol", "Map", "Set"].forEach(m => { - let g = getGlobal() - if (typeof g[m] === "undefined") { - die(`MobX requires global '${m}' to be available or polyfilled`) - } -}) +if (__DEV__) { + const g = globalThis as any + ;["Symbol", "Map", "Set", "Proxy"].forEach(m => { + if (typeof g[m] === "undefined") { + die(`MobX requires global '${m}' to be available or polyfilled`) + } + }) +} import { spy, getDebugName, $mobx } from "./internal" @@ -38,7 +39,10 @@ export { spy, IComputedValue, IEqualsComparer, - comparer, + compareDefault, + compareIdentity, + compareStructural, + compareShallow, IEnhancer, IInterceptable, IInterceptor, @@ -72,9 +76,14 @@ export { IObservableSetInitialValues, transaction, observable, + observableRef, + observableShallow, + observableDeep, + observableStruct, IObservableFactory, CreateObservableOptions, computed, + computedStruct, IComputedFactory, isObservable, isObservableProp, @@ -90,6 +99,7 @@ export { when, IWhenOptions, action, + actionBound, isAction, runInAction, IActionFactory, @@ -106,13 +116,13 @@ export { onBecomeObserved, onBecomeUnobserved, flow, + flowBound, isFlow, flowResult, CancellablePromise, FlowCancellationError, isFlowCancellationError, toJS, - trace, IObserverTree, IDependencyTree, getDependencyTree, @@ -138,6 +148,7 @@ export { makeObservable, makeAutoObservable, autoAction as _autoAction, + autoActionBound as _autoActionBound, AnnotationsMap, AnnotationMapEntry, override @@ -145,7 +156,7 @@ export { // Devtools support declare const __MOBX_DEVTOOLS_GLOBAL_HOOK__: { injectMobx: (any) => void } -if (typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === "object") { +if (__DEV__ && typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === "object") { // See: https://github.com/andykog/mobx-devtools/ __MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobx({ spy, diff --git a/packages/mobx/src/types/actionannotation.ts b/packages/mobx/src/types/actionannotation.ts index ab9d354bb3..369cd1e8ed 100644 --- a/packages/mobx/src/types/actionannotation.ts +++ b/packages/mobx/src/types/actionannotation.ts @@ -16,8 +16,7 @@ export function createActionAnnotation(name: string, options?: object): Annotati annotationType_: name, options_: options, make_, - extend_, - decorate_20223_ + extend_ } } @@ -62,12 +61,12 @@ function extend_( return adm.defineProperty_(key, actionDescriptor, proxyTrap) } -function decorate_20223_(this: Annotation, mthd, context: DecoratorContext) { +export function decorateAction20223_(annotation: Annotation, mthd, context: DecoratorContext) { if (__DEV__) { assert20223DecoratorType(context, ["method", "field"]) } const { kind, name, addInitializer } = context - const ann = this + const ann = annotation const _createAction = m => createAction(ann.options_?.name ?? name!.toString(), m, ann.options_?.autoAction ?? false) @@ -91,7 +90,7 @@ function decorate_20223_(this: Annotation, mthd, context: DecoratorContext) { mthd = _createAction(mthd) } - if (this.options_?.bound) { + if (ann.options_?.bound) { addInitializer(function () { const self = this as any const bound = self[name].bind(self) @@ -103,10 +102,7 @@ function decorate_20223_(this: Annotation, mthd, context: DecoratorContext) { return mthd } - die( - `Cannot apply '${ann.annotationType_}' to '${String(name)}' (kind: ${kind}):` + - `\n'${ann.annotationType_}' can only be used on properties with a function value.` - ) + die(43, ann.annotationType_, String(name), kind) } function assertActionDescriptor( diff --git a/packages/mobx/src/types/autoannotation.ts b/packages/mobx/src/types/autoannotation.ts index 902e741213..27cd3df7f4 100644 --- a/packages/mobx/src/types/autoannotation.ts +++ b/packages/mobx/src/types/autoannotation.ts @@ -1,16 +1,18 @@ import { ObservableObjectAdministration, observable, + observableRef, Annotation, defineProperty, createAction, globalState, flow, + flowBound, computed, autoAction, + autoActionBound, isGenerator, MakeResult, - die, isAction } from "../internal" @@ -23,8 +25,7 @@ export function createAutoAnnotation(options?: object): Annotation { annotationType_: AUTO, options_: options, make_, - extend_, - decorate_20223_ + extend_ } } @@ -63,16 +64,16 @@ function make_( // function on proto -> autoAction/flow if (source !== adm.target_ && typeof descriptor.value === "function") { if (isGenerator(descriptor.value)) { - const flowAnnotation = this.options_?.autoBind ? flow.bound : flow + const flowAnnotation = this.options_?.autoBind ? flowBound : flow return flowAnnotation.make_(adm, key, descriptor, source) } - const actionAnnotation = this.options_?.autoBind ? autoAction.bound : autoAction + const actionAnnotation = this.options_?.autoBind ? autoActionBound : autoAction return actionAnnotation.make_(adm, key, descriptor, source) } // other -> observable // Copy props from proto as well, see test: // "decorate should work with Object.create" - let observableAnnotation = this.options_?.deep === false ? observable.ref : observable + let observableAnnotation = this.options_?.deep === false ? observableRef : observable // if function respect autoBind option if (typeof descriptor.value === "function" && this.options_?.autoBind) { descriptor.value = descriptor.value.bind(adm.proxy_ ?? adm.target_) @@ -107,10 +108,6 @@ function extend_( if (typeof descriptor.value === "function" && this.options_?.autoBind) { descriptor.value = descriptor.value.bind(adm.proxy_ ?? adm.target_) } - let observableAnnotation = this.options_?.deep === false ? observable.ref : observable + let observableAnnotation = this.options_?.deep === false ? observableRef : observable return observableAnnotation.extend_(adm, key, descriptor, proxyTrap) } - -function decorate_20223_(this: Annotation, desc, context: ClassGetterDecoratorContext) { - die(`'${this.annotationType_}' cannot be used as a decorator`) -} diff --git a/packages/mobx/src/types/computedannotation.ts b/packages/mobx/src/types/computedannotation.ts index 11dd4e90ab..03fb682dd3 100644 --- a/packages/mobx/src/types/computedannotation.ts +++ b/packages/mobx/src/types/computedannotation.ts @@ -6,7 +6,8 @@ import { assert20223DecoratorType, $mobx, asObservableObject, - ComputedValue + ComputedValue, + assign } from "../internal" export function createComputedAnnotation(name: string, options?: object): Annotation { @@ -14,8 +15,7 @@ export function createComputedAnnotation(name: string, options?: object): Annota annotationType_: name, options_: options, make_, - extend_, - decorate_20223_ + extend_ } } @@ -38,20 +38,23 @@ function extend_( assertComputedDescriptor(adm, this, key, descriptor) return adm.defineComputedProperty_( key, - { - ...this.options_, + assign({}, this.options_, { get: descriptor.get, set: descriptor.set - }, + }), proxyTrap ) } -function decorate_20223_(this: Annotation, get, context: ClassGetterDecoratorContext) { +export function decorateComputed20223_( + annotation: Annotation, + get, + context: ClassGetterDecoratorContext +) { if (__DEV__) { assert20223DecoratorType(context, ["getter"]) } - const ann = this + const ann = annotation const { name: key, addInitializer } = context let computedValues: WeakMap> | undefined @@ -59,11 +62,10 @@ function decorate_20223_(this: Annotation, get, context: ClassGetterDecoratorCon // ComputedValues for getters that are never read on a given instance. // The factory is materialised by ObservableObjectAdministration on demand. function createComputedValue(target: object, adm: ObservableObjectAdministration) { - const options = { - ...ann.options_, + const options = assign({}, ann.options_, { get, context: target - } + }) options.name ||= __DEV__ ? `${adm.name_}.${key.toString()}` : `ObservableObject.${key.toString()}` diff --git a/packages/mobx/src/types/decorator_fills.ts b/packages/mobx/src/types/decorator_fills.ts index 084f023376..ad3aeab617 100644 --- a/packages/mobx/src/types/decorator_fills.ts +++ b/packages/mobx/src/types/decorator_fills.ts @@ -25,6 +25,10 @@ export type ClassFieldDecorator any = context: ClassFieldDecoratorContext ) => Value | void +export type ClassMethodAndFieldDecorator = ClassMethodDecorator & ClassFieldDecorator + +export type ClassAccessorAndFieldDecorator = ClassAccessorDecorator & ClassFieldDecorator + export type Decorator = | ClassAccessorDecorator | ClassGetterDecorator diff --git a/packages/mobx/src/types/dynamicobject.ts b/packages/mobx/src/types/dynamicobject.ts index 71598879b4..afcc8bb903 100644 --- a/packages/mobx/src/types/dynamicobject.ts +++ b/packages/mobx/src/types/dynamicobject.ts @@ -2,11 +2,8 @@ import { $mobx, IIsObservableObject, ObservableObjectAdministration, - warnAboutProxyRequirement, - assertProxies, die, isStringish, - globalState, CreateObservableOptions, asObservableObject } from "../internal" @@ -19,11 +16,6 @@ function getAdm(target): ObservableObjectAdministration { // and skip either the internal values map, or the base object with its property descriptors! const objectProxyTraps: ProxyHandler = { has(target: IIsObservableObject, name: PropertyKey): boolean { - if (__DEV__ && globalState.trackingDerivation) { - warnAboutProxyRequirement( - "detect new properties using the 'in' operator. Use 'has' from 'mobx' instead." - ) - } return getAdm(target).has_(name) }, get(target: IIsObservableObject, name: PropertyKey): any { @@ -33,20 +25,10 @@ const objectProxyTraps: ProxyHandler = { if (!isStringish(name)) { return false } - if (__DEV__ && !getAdm(target).values_.has(name)) { - warnAboutProxyRequirement( - "add a new observable property through direct assignment. Use 'set' from 'mobx' instead." - ) - } // null (intercepted) -> true (success) return getAdm(target).set_(name, value, true) ?? true }, deleteProperty(target: IIsObservableObject, name: PropertyKey): boolean { - if (__DEV__) { - warnAboutProxyRequirement( - "delete properties from an observable object. Use 'remove' from 'mobx' instead." - ) - } if (!isStringish(name)) { return false } @@ -58,20 +40,10 @@ const objectProxyTraps: ProxyHandler = { name: PropertyKey, descriptor: PropertyDescriptor ): boolean { - if (__DEV__) { - warnAboutProxyRequirement( - "define property on an observable object. Use 'defineProperty' from 'mobx' instead." - ) - } // null (intercepted) -> true (success) return getAdm(target).defineProperty_(name, descriptor) ?? true }, ownKeys(target: IIsObservableObject): ArrayLike { - if (__DEV__ && globalState.trackingDerivation) { - warnAboutProxyRequirement( - "iterate keys to detect added / removed properties. Use 'keys' from 'mobx' instead." - ) - } return getAdm(target).ownKeys_() }, preventExtensions(target) { @@ -83,7 +55,6 @@ export function asDynamicObservableObject( target: any, options?: CreateObservableOptions ): IIsObservableObject { - assertProxies() target = asObservableObject(target, options) return (target[$mobx].proxy_ ??= new Proxy(target, objectProxyTraps)) } diff --git a/packages/mobx/src/types/flowannotation.ts b/packages/mobx/src/types/flowannotation.ts index 530b95942c..4e272703a3 100644 --- a/packages/mobx/src/types/flowannotation.ts +++ b/packages/mobx/src/types/flowannotation.ts @@ -17,8 +17,7 @@ export function createFlowAnnotation(name: string, options?: object): Annotation annotationType_: name, options_: options, make_, - extend_, - decorate_20223_ + extend_ } } @@ -63,7 +62,11 @@ function extend_( return adm.defineProperty_(key, flowDescriptor, proxyTrap) } -function decorate_20223_(this: Annotation, mthd, context: ClassMethodDecoratorContext) { +export function decorateFlow20223_( + annotation: Annotation, + mthd, + context: ClassMethodDecoratorContext +) { if (__DEV__) { assert20223DecoratorType(context, ["method"]) } @@ -73,7 +76,7 @@ function decorate_20223_(this: Annotation, mthd, context: ClassMethodDecoratorCo mthd = flow(mthd) } - if (this.options_?.bound) { + if (annotation.options_?.bound) { addInitializer(function () { const self = this as any const bound = self[name].bind(self) diff --git a/packages/mobx/src/types/legacyobservablearray.ts b/packages/mobx/src/types/legacyobservablearray.ts deleted file mode 100644 index 5ca88d07a1..0000000000 --- a/packages/mobx/src/types/legacyobservablearray.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { - getNextId, - addHiddenFinalProp, - makeIterable, - addHiddenProp, - ObservableArrayAdministration, - $mobx, - arrayExtensions, - IEnhancer, - isObservableArray, - IObservableArray, - defineProperty, - initObservable -} from "../internal" - -// Bug in safari 9.* (or iOS 9 safari mobile). See #364 -const ENTRY_0 = createArrayEntryDescriptor(0) - -const safariPrototypeSetterInheritanceBug = (() => { - let v = false - const p = {} - Object.defineProperty(p, "0", { - set: () => { - v = true - } - }) - Object.create(p)["0"] = 1 - return v === false -})() - -/** - * This array buffer contains two lists of properties, so that all arrays - * can recycle their property definitions, which significantly improves performance of creating - * properties on the fly. - */ -let OBSERVABLE_ARRAY_BUFFER_SIZE = 0 - -// Typescript workaround to make sure ObservableArray extends Array -class StubArray {} -function inherit(ctor, proto) { - if (Object.setPrototypeOf) { - Object.setPrototypeOf(ctor.prototype, proto) - } else if (ctor.prototype.__proto__ !== undefined) { - ctor.prototype.__proto__ = proto - } else { - ctor.prototype = proto - } -} -inherit(StubArray, Array.prototype) - -// Weex proto freeze protection was here, -// but it is unclear why the hack is need as MobX never changed the prototype -// anyway, so removed it in V6 - -export class LegacyObservableArray extends StubArray { - constructor( - initialValues: T[] | undefined, - enhancer: IEnhancer, - name = __DEV__ ? "ObservableArray@" + getNextId() : "ObservableArray", - owned = false - ) { - super() - initObservable(() => { - const adm = new ObservableArrayAdministration(name, enhancer, owned, true) - adm.proxy_ = this as any - addHiddenFinalProp(this, $mobx, adm) - - if (initialValues && initialValues.length) { - // @ts-ignore - this.spliceWithArray(0, 0, initialValues) - } - - if (safariPrototypeSetterInheritanceBug) { - // Seems that Safari won't use numeric prototype setter until any * numeric property is - // defined on the instance. After that it works fine, even if this property is deleted. - Object.defineProperty(this, "0", ENTRY_0) - } - }) - } - - concat(...arrays: T[][]): T[] { - ;(this[$mobx] as ObservableArrayAdministration).atom_.reportObserved() - return Array.prototype.concat.apply( - (this as any).slice(), - //@ts-ignore - arrays.map(a => (isObservableArray(a) ? a.slice() : a)) - ) - } - - get length(): number { - return (this[$mobx] as ObservableArrayAdministration).getArrayLength_() - } - - set length(newLength: number) { - ;(this[$mobx] as ObservableArrayAdministration).setArrayLength_(newLength) - } - - get [Symbol.toStringTag]() { - return "Array" - } - - [Symbol.iterator]() { - const self = this - let nextIndex = 0 - return makeIterable({ - next() { - return nextIndex < self.length - ? { value: self[nextIndex++], done: false } - : { done: true, value: undefined } - } - }) - } -} - -Object.entries(arrayExtensions).forEach(([prop, fn]) => { - if (prop !== "concat") { - addHiddenProp(LegacyObservableArray.prototype, prop, fn) - } -}) - -function createArrayEntryDescriptor(index: number) { - return { - enumerable: false, - configurable: true, - get: function () { - return this[$mobx].get_(index) - }, - set: function (value) { - this[$mobx].set_(index, value) - } - } -} - -function createArrayBufferItem(index: number) { - defineProperty(LegacyObservableArray.prototype, "" + index, createArrayEntryDescriptor(index)) -} - -export function reserveArrayBuffer(max: number) { - if (max > OBSERVABLE_ARRAY_BUFFER_SIZE) { - for (let index = OBSERVABLE_ARRAY_BUFFER_SIZE; index < max + 100; index++) { - createArrayBufferItem(index) - } - OBSERVABLE_ARRAY_BUFFER_SIZE = max - } -} - -reserveArrayBuffer(1000) - -export function createLegacyArray( - initialValues: T[] | undefined, - enhancer: IEnhancer, - name?: string -): IObservableArray { - return new LegacyObservableArray(initialValues, enhancer, name) as any -} diff --git a/packages/mobx/src/types/observableannotation.ts b/packages/mobx/src/types/observableannotation.ts index b7ec2c1bfb..4c688d5f38 100644 --- a/packages/mobx/src/types/observableannotation.ts +++ b/packages/mobx/src/types/observableannotation.ts @@ -15,8 +15,7 @@ export function createObservableAnnotation(name: string, options?: object): Anno annotationType_: name, options_: options, make_, - extend_, - decorate_20223_ + extend_ } } @@ -40,13 +39,13 @@ function extend_( return adm.defineObservableProperty_( key, descriptor.value, - this.options_?.enhancer ?? deepEnhancer, + this.options_?.enhancer_ ?? deepEnhancer, proxyTrap ) } -function decorate_20223_( - this: Annotation, +export function decorateObservable20223_( + annotation: Annotation, desc, context: ClassAccessorDecoratorContext | ClassFieldDecoratorContext ) { @@ -61,7 +60,7 @@ function decorate_20223_( assert20223DecoratorType(context, ["accessor"]) } - const ann = this + const ann = annotation const { kind, name } = context if (kind !== "accessor") { @@ -78,7 +77,7 @@ function decorate_20223_( () => new ObservableValue( value, - ann.options_?.enhancer ?? deepEnhancer, + ann.options_?.enhancer_ ?? deepEnhancer, __DEV__ ? `${adm.name_}.${name.toString()}` : `ObservableObject.${name.toString()}`, diff --git a/packages/mobx/src/types/observablearray.ts b/packages/mobx/src/types/observablearray.ts index 18f45a1f99..c8ba985b7f 100644 --- a/packages/mobx/src/types/observablearray.ts +++ b/packages/mobx/src/types/observablearray.ts @@ -5,9 +5,7 @@ import { IAtom, IEnhancer, IInterceptable, - IInterceptor, IListenable, - Lambda, addHiddenFinalProp, checkIfStateModificationsAreAllowed, createInstanceofPredicate, @@ -18,12 +16,8 @@ import { isObject, isSpyEnabled, notifyListeners, - registerInterceptor, - registerListener, spyReportEnd, spyReportStart, - assertProxies, - reserveArrayBuffer, hasProp, die, globalState, @@ -130,8 +124,7 @@ export class ObservableArrayAdministration constructor( name = __DEV__ ? "ObservableArray@" + getNextId() : "ObservableArray", enhancer: IEnhancer, - public owned_: boolean, - public legacyMode_: boolean + public owned_: boolean ) { this.atom_ = new Atom(name) this.enhancer_ = (newV, oldV) => @@ -152,30 +145,6 @@ export class ObservableArrayAdministration return values } - intercept_(handler: IInterceptor | IArrayWillSplice>): Lambda { - return registerInterceptor | IArrayWillSplice>(this, handler) - } - - observe_( - listener: (changeData: IArrayDidChange) => void, - fireImmediately = false - ): Lambda { - if (fireImmediately) { - listener(>{ - observableKind: "array", - object: this.proxy_ as any, - debugObjectName: this.atom_.name_, - type: "splice", - index: 0, - added: this.values_.slice(), - addedCount: this.values_.length, - removed: [], - removedCount: 0 - }) - } - return registerListener(this, listener) - } - getArrayLength_(): number { this.atom_.reportObserved() return this.values_.length @@ -183,16 +152,13 @@ export class ObservableArrayAdministration setArrayLength_(newLength: number) { if (typeof newLength !== "number" || isNaN(newLength) || newLength < 0) { - die("Out of range: " + newLength) + die(40, newLength) } let currentLength = this.values_.length if (newLength === currentLength) { return } else if (newLength > currentLength) { - const newItems = new Array(newLength - currentLength) - for (let i = 0; i < newLength - currentLength; i++) { - newItems[i] = undefined - } // No Array.fill everywhere... + const newItems = Array.from({ length: newLength - currentLength }) this.spliceWithArray_(currentLength, 0, newItems) } else { this.spliceWithArray_(newLength, currentLength - newLength) @@ -204,9 +170,6 @@ export class ObservableArrayAdministration die(16) } this.lastKnownLength_ += delta - if (this.legacyMode_ && delta > 0) { - reserveArrayBuffer(oldLength + delta + 1) - } } spliceWithArray_(index: number, deleteCount?: number, newItems?: any[]): any[] { @@ -250,7 +213,7 @@ export class ObservableArrayAdministration newItems = newItems.length === 0 ? newItems : newItems.map(v => this.enhancer_(v, undefined)) - if (this.legacyMode_ || __DEV__) { + if (__DEV__) { const lengthDelta = newItems.length - deleteCount this.updateArrayLength_(length, lengthDelta) // checks if internal array wasn't modified } @@ -283,7 +246,7 @@ export class ObservableArrayAdministration } notifyArrayChildUpdate_(index: number, newValue: any, oldValue: any) { - const notifySpy = !this.owned_ && isSpyEnabled() + const notifySpy = __DEV__ && !this.owned_ && isSpyEnabled() const notify = hasListeners(this) const change: IArrayDidChange | null = notify || notifySpy @@ -313,7 +276,7 @@ export class ObservableArrayAdministration } notifyArraySplice_(index: number, added: any[], removed: any[]) { - const notifySpy = !this.owned_ && isSpyEnabled() + const notifySpy = __DEV__ && !this.owned_ && isSpyEnabled() const notify = hasListeners(this) const change: IArraySplice | null = notify || notifySpy @@ -344,24 +307,12 @@ export class ObservableArrayAdministration } get_(index: number): any | undefined { - if (this.legacyMode_ && index >= this.values_.length) { - console.warn( - __DEV__ - ? `[mobx.array] Attempt to read an array index (${index}) that is out of bounds (${this.values_.length}). Please check length first. Out of bound indices will not be tracked by MobX` - : `[mobx] Out of bounds read: ${index}` - ) - return undefined - } this.atom_.reportObserved() return this.dehanceValue_(this.values_[index]) } set_(index: number, newValue: any) { const values = this.values_ - if (this.legacyMode_ && index > values.length) { - // out of bounds - die(17, index, values.length) - } if (index < values.length) { // update at index in range checkIfStateModificationsAreAllowed(this.atom_) @@ -388,10 +339,7 @@ export class ObservableArrayAdministration // For out of bound index, we don't create an actual sparse array, // but rather fill the holes with undefined (same as setArrayLength_). // This could be considered a bug. - const newItems = new Array(index + 1 - values.length) - for (let i = 0; i < newItems.length - 1; i++) { - newItems[i] = undefined - } // No Array.fill everywhere... + const newItems = Array.from({ length: index + 1 - values.length }) newItems[newItems.length - 1] = newValue this.spliceWithArray_(values.length, 0, newItems) } @@ -404,9 +352,8 @@ export function createObservableArray( name = __DEV__ ? "ObservableArray@" + getNextId() : "ObservableArray", owned = false ): IObservableArray { - assertProxies() return initObservable(() => { - const adm = new ObservableArrayAdministration(name, enhancer, owned, false) + const adm = new ObservableArrayAdministration(name, enhancer, owned) addHiddenFinalProp(adm.values_, $mobx, adm) const proxy = new Proxy(adm.values_, arrayTraps) as any adm.proxy_ = proxy diff --git a/packages/mobx/src/types/observablemap.ts b/packages/mobx/src/types/observablemap.ts index 15061103a1..068ced1003 100644 --- a/packages/mobx/src/types/observablemap.ts +++ b/packages/mobx/src/types/observablemap.ts @@ -2,9 +2,7 @@ import { $mobx, IEnhancer, IInterceptable, - IInterceptor, IListenable, - Lambda, ObservableValue, checkIfStateModificationsAreAllowed, createAtom, @@ -22,17 +20,13 @@ import { isSpyEnabled, notifyListeners, referenceEnhancer, - registerInterceptor, - registerListener, spyReportEnd, spyReportStart, stringifyKey, transaction, untracked, - onBecomeUnobserved, globalState, die, - isFunction, UPDATE, IAtom, PureSpyEvent, @@ -106,9 +100,6 @@ export class ObservableMap public enhancer_: IEnhancer = deepEnhancer, public name_ = __DEV__ ? "ObservableMap@" + getNextId() : "ObservableMap" ) { - if (!isFunction(Map)) { - die(18) - } initObservable(() => { this.keysAtom_ = createAtom(__DEV__ ? `${this.name_}.keys()` : "ObservableMap.keys()") this.data_ = new Map() @@ -137,7 +128,7 @@ export class ObservableMap false )) this.hasMap_.set(key, newEntry) - onBecomeUnobserved(newEntry, () => this.hasMap_.delete(key)) + newEntry.onBUOL = new Set([() => this.hasMap_.delete(key)]) } return entry.get() @@ -178,7 +169,7 @@ export class ObservableMap } } if (this.has_(key)) { - const notifySpy = isSpyEnabled() + const notifySpy = __DEV__ && isSpyEnabled() const notify = hasListeners(this) const change: IMapDidChange | null = notify || notifySpy @@ -217,7 +208,7 @@ export class ObservableMap const observable = this.data_.get(key)! newValue = (observable as any).prepareNewValue_(newValue) as V if (newValue !== globalState.UNCHANGED) { - const notifySpy = isSpyEnabled() + const notifySpy = __DEV__ && isSpyEnabled() const notify = hasListeners(this) const change: IMapDidChange | null = notify || notifySpy @@ -258,7 +249,7 @@ export class ObservableMap this.hasMap_.get(key)?.setNewValue_(true) this.keysAtom_.reportChanged() }) - const notifySpy = isSpyEnabled() + const notifySpy = __DEV__ && isSpyEnabled() const notify = hasListeners(this) const change: IMapDidChange | null = notify || notifySpy @@ -481,22 +472,6 @@ export class ObservableMap get [Symbol.toStringTag]() { return "Map" } - - /** - * Observes this object. Triggers for the events 'add', 'update' and 'delete'. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe - * for callback details - */ - observe_(listener: (changes: IMapDidChange) => void, fireImmediately?: boolean): Lambda { - if (__DEV__ && fireImmediately === true) { - die("`observe` doesn't support fireImmediately=true in combination with maps.") - } - return registerListener(this, listener) - } - - intercept_(handler: IInterceptor>): Lambda { - return registerInterceptor(this, handler) - } } // eslint-disable-next-line diff --git a/packages/mobx/src/types/observableobject.ts b/packages/mobx/src/types/observableobject.ts index 6e7e600279..455d360a1a 100644 --- a/packages/mobx/src/types/observableobject.ts +++ b/packages/mobx/src/types/observableobject.ts @@ -12,7 +12,6 @@ import { IEnhancer, IInterceptable, IListenable, - Lambda, ObservableValue, addHiddenProp, createInstanceofPredicate, @@ -26,8 +25,6 @@ import { isSpyEnabled, notifyListeners, referenceEnhancer, - registerInterceptor, - registerListener, spyReportEnd, spyReportStart, startBatch, @@ -38,16 +35,14 @@ import { die, hasProp, getDescriptor, - storedAnnotationsSymbol, ownKeys, isOverride, defineProperty, autoAnnotation, getAdministration, getDebugName, - objectPrototype, - MakeResult, - checkIfStateModificationsAreAllowed + checkIfStateModificationsAreAllowed, + assign } from "../internal" const descriptorCache = Object.create(null) @@ -274,47 +269,6 @@ export class ObservableObjectAdministration return entry.get() } - /** - * @param {PropertyKey} key - * @param {Annotation|boolean} annotation true - use default annotation, false - ignore prop - */ - make_(key: PropertyKey, annotation: Annotation | boolean): void { - if (annotation === true) { - annotation = this.defaultAnnotation_ - } - if (annotation === false) { - return - } - assertAnnotable(this, annotation, key) - if (!(key in this.target_)) { - // Throw on missing key, except for decorators: - // Decorator annotations are collected from whole prototype chain. - // When called from super() some props may not exist yet. - // However we don't have to worry about missing prop, - // because the decorator must have been applied to something. - if (this.target_[storedAnnotationsSymbol]?.[key]) { - return // will be annotated by subclass constructor - } else { - die(1, annotation.annotationType_, `${this.name_}.${key.toString()}`) - } - } - let source = this.target_ - while (source && source !== objectPrototype) { - const descriptor = getDescriptor(source, key) - if (descriptor) { - const outcome = annotation.make_(this, key, descriptor, source) - if (outcome === MakeResult.Cancel) { - return - } - if (outcome === MakeResult.Break) { - break - } - } - source = Object.getPrototypeOf(source) - } - recordAnnotationApplied(this, annotation, key) - } - /** * @param {PropertyKey} key * @param {PropertyDescriptor} descriptor @@ -377,10 +331,9 @@ export class ObservableObjectAdministration } const { newValue } = change as any if (descriptor.value !== newValue) { - descriptor = { - ...descriptor, + descriptor = assign({}, descriptor, { value: newValue - } + }) } } @@ -618,22 +571,6 @@ export class ObservableObjectAdministration return true } - /** - * Observes this object. Triggers for the events 'add', 'update' and 'delete'. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe - * for callback details - */ - observe_(callback: (changes: IObjectDidChange) => void, fireImmediately?: boolean): Lambda { - if (__DEV__ && fireImmediately === true) { - die("`observe` doesn't support the fire immediately property for observable objects.") - } - return registerListener(this, callback) - } - - intercept_(handler): Lambda { - return registerInterceptor(this, handler) - } - notifyPropertyAddition_(key: PropertyKey, value: any) { const notify = hasListeners(this) const notifySpy = __DEV__ && isSpyEnabled() @@ -765,11 +702,9 @@ export function recordAnnotationApplied( if (__DEV__) { adm.appliedAnnotations_![key] = annotation } - // Remove applied decorator annotation so we don't try to apply it again in subclass constructor - delete adm.target_[storedAnnotationsSymbol]?.[key] } -function assertAnnotable( +export function assertAnnotable( adm: ObservableObjectAdministration, annotation: Annotation, key: PropertyKey diff --git a/packages/mobx/src/types/observableset.ts b/packages/mobx/src/types/observableset.ts index ba9cdc2f43..ca088463b8 100644 --- a/packages/mobx/src/types/observableset.ts +++ b/packages/mobx/src/types/observableset.ts @@ -7,8 +7,6 @@ import { isSpyEnabled, hasListeners, IListenable, - registerListener, - Lambda, spyReportStart, notifyListeners, spyReportEnd, @@ -17,8 +15,6 @@ import { hasInterceptors, interceptChange, IInterceptable, - IInterceptor, - registerInterceptor, checkIfStateModificationsAreAllowed, untracked, transaction, @@ -27,7 +23,6 @@ import { DELETE, ADD, die, - isFunction, initObservable } from "../internal" @@ -55,16 +50,14 @@ export type ISetWillDeleteChange = { type: "delete" object: ObservableSet oldValue: T -}; +} export type ISetWillAddChange = { type: "add" object: ObservableSet newValue: T -}; +} -export type ISetWillChange = - | ISetWillDeleteChange - | ISetWillAddChange +export type ISetWillChange = ISetWillDeleteChange | ISetWillAddChange export class ObservableSet implements Set, IInterceptable, IListenable { [$mobx] = ObservableSetMarker @@ -80,9 +73,6 @@ export class ObservableSet implements Set, IInterceptable = deepEnhancer, public name_ = __DEV__ ? "ObservableSet@" + getNextId() : "ObservableSet" ) { - if (!isFunction(Set)) { - die(22) - } this.enhancer_ = (newV, oldV) => enhancer(newV, oldV, name_) initObservable(() => { this.atom_ = createAtom(this.name_) @@ -133,8 +123,7 @@ export class ObservableSet implements Set, IInterceptable { @@ -304,24 +293,12 @@ export class ObservableSet implements Set, IInterceptable this.add(value)) } else if (other !== null && other !== undefined) { - die("Cannot initialize set from " + other) + die(41, other) } }) return this } - observe_(listener: (changes: ISetDidChange) => void, fireImmediately?: boolean): Lambda { - // ... 'fireImmediately' could also be true? - if (__DEV__ && fireImmediately === true) { - die("`observe` doesn't support fireImmediately=true in combination with sets.") - } - return registerListener(this, listener) - } - - intercept_(handler: IInterceptor>): Lambda { - return registerInterceptor(this, handler) - } - toJSON(): T[] { return Array.from(this) } diff --git a/packages/mobx/src/types/observablevalue.ts b/packages/mobx/src/types/observablevalue.ts index b46f75b66d..edb274c976 100644 --- a/packages/mobx/src/types/observablevalue.ts +++ b/packages/mobx/src/types/observablevalue.ts @@ -3,11 +3,9 @@ import { IEnhancer, IInterceptable, IEqualsComparer, - IInterceptor, IListenable, - Lambda, checkIfStateModificationsAreAllowed, - comparer, + compareDefault, createInstanceofPredicate, getNextId, hasInterceptors, @@ -15,8 +13,6 @@ import { interceptChange, isSpyEnabled, notifyListeners, - registerInterceptor, - registerListener, spyReport, spyReportEnd, spyReportStart, @@ -69,13 +65,13 @@ export class ObservableValue constructor( value: T, - public enhancer: IEnhancer, + public enhancer_: IEnhancer, public name_ = __DEV__ ? "ObservableValue@" + getNextId() : "ObservableValue", notifySpy = true, - private equals: IEqualsComparer = comparer.default + private equals_: IEqualsComparer = compareDefault ) { super(name_) - this.value_ = enhancer(value, undefined, name_) + this.value_ = enhancer_(value, undefined, name_) if (__DEV__ && notifySpy && isSpyEnabled()) { // only notify spy if this is a stand-alone observable spyReport({ @@ -99,7 +95,7 @@ export class ObservableValue const oldValue = this.value_ newValue = this.prepareNewValue_(newValue) as any if (newValue !== globalState.UNCHANGED) { - const notifySpy = isSpyEnabled() + const notifySpy = __DEV__ && isSpyEnabled() if (__DEV__ && notifySpy) { spyReportStart({ type: UPDATE, @@ -131,8 +127,8 @@ export class ObservableValue newValue = change.newValue } // apply modifier - newValue = this.enhancer(newValue, this.value_, this.name_) - return this.equals(this.value_, newValue) ? globalState.UNCHANGED : newValue + newValue = this.enhancer_(newValue, this.value_, this.name_) + return this.equals_(this.value_, newValue) ? globalState.UNCHANGED : newValue } setNewValue_(newValue: T) { @@ -154,24 +150,6 @@ export class ObservableValue return this.dehanceValue(this.value_) } - intercept_(handler: IInterceptor>): Lambda { - return registerInterceptor(this, handler) - } - - observe_(listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda { - if (fireImmediately) { - listener({ - observableKind: "value", - debugObjectName: this.name_, - object: this, - type: UPDATE, - newValue: this.value_, - oldValue: undefined - }) - } - return registerListener(this, listener) - } - raw() { // used by MST ot get undehanced value return this.value_ diff --git a/packages/mobx/src/types/overrideannotation.ts b/packages/mobx/src/types/overrideannotation.ts index f688c99630..2eeee9d64d 100644 --- a/packages/mobx/src/types/overrideannotation.ts +++ b/packages/mobx/src/types/overrideannotation.ts @@ -1,23 +1,12 @@ -import { - die, - Annotation, - hasProp, - createDecoratorAnnotation, - ObservableObjectAdministration, - MakeResult -} from "../internal" - -import type { ClassMethodDecorator } from "./decorator_fills" +import { die, Annotation, hasProp, ObservableObjectAdministration, MakeResult } from "../internal" const OVERRIDE = "override" -export const override: Annotation & PropertyDecorator & ClassMethodDecorator = - createDecoratorAnnotation({ - annotationType_: OVERRIDE, - make_, - extend_, - decorate_20223_ - }) +export const override: Annotation = { + annotationType_: OVERRIDE, + make_, + extend_ +} export function isOverride(annotation: Annotation): boolean { return annotation.annotationType_ === OVERRIDE @@ -42,9 +31,5 @@ function make_(this: Annotation, adm: ObservableObjectAdministration, key): Make } function extend_(this: Annotation, adm, key, descriptor, proxyTrap): boolean { - die(`'${this.annotationType_}' can only be used with 'makeObservable'`) -} - -function decorate_20223_(this: Annotation, desc, context: DecoratorContext) { - console.warn(`'${this.annotationType_}' cannot be used with decorators - this is a no-op`) + die(44, this.annotationType_) } diff --git a/packages/mobx/src/types/type-utils.ts b/packages/mobx/src/types/type-utils.ts index c48ddf26d1..dc3c29de8c 100644 --- a/packages/mobx/src/types/type-utils.ts +++ b/packages/mobx/src/types/type-utils.ts @@ -111,13 +111,15 @@ export function getDebugName(thing: any, property?: string): string { */ export function initObservable(cb: () => T): T { const derivation = untrackedStart() - const allowStateChanges = allowStateChangesStart(true) + const allowStateChanges = __DEV__ ? allowStateChangesStart(true) : true startBatch() try { return cb() } finally { endBatch() - allowStateChangesEnd(allowStateChanges) + if (__DEV__) { + allowStateChangesEnd(allowStateChanges) + } untrackedEnd(derivation) } } diff --git a/packages/mobx/src/utils/comparer.ts b/packages/mobx/src/utils/comparer.ts index 7f560a650b..5a33a5b63f 100644 --- a/packages/mobx/src/utils/comparer.ts +++ b/packages/mobx/src/utils/comparer.ts @@ -4,29 +4,16 @@ export interface IEqualsComparer { (a: T, b: T): boolean } -function identityComparer(a: any, b: any): boolean { +export function compareIdentity(a: any, b: any): boolean { return a === b } -function structuralComparer(a: any, b: any): boolean { +export function compareStructural(a: any, b: any): boolean { return deepEqual(a, b) } -function shallowComparer(a: any, b: any): boolean { +export function compareShallow(a: any, b: any): boolean { return deepEqual(a, b, 1) } -function defaultComparer(a: any, b: any): boolean { - if (Object.is) { - return Object.is(a, b) - } - - return a === b ? a !== 0 || 1 / a === 1 / b : a !== a && b !== b -} - -export const comparer = { - identity: identityComparer, - structural: structuralComparer, - default: defaultComparer, - shallow: shallowComparer -} +export const compareDefault = Object.is diff --git a/packages/mobx/src/utils/global.ts b/packages/mobx/src/utils/global.ts deleted file mode 100644 index 86f57987ca..0000000000 --- a/packages/mobx/src/utils/global.ts +++ /dev/null @@ -1,20 +0,0 @@ -declare const window: any -declare const self: any - -const mockGlobal = {} - -export function getGlobal() { - if (typeof globalThis !== "undefined") { - return globalThis - } - if (typeof window !== "undefined") { - return window - } - if (typeof global !== "undefined") { - return global - } - if (typeof self !== "undefined") { - return self - } - return mockGlobal -} diff --git a/packages/mobx/src/utils/iterable.ts b/packages/mobx/src/utils/iterable.ts index 6e0f678341..42ad8e17fa 100644 --- a/packages/mobx/src/utils/iterable.ts +++ b/packages/mobx/src/utils/iterable.ts @@ -1,14 +1,13 @@ -import { getGlobal } from "../internal" +import { assign } from "./utils" // safely get iterator prototype if available -const global = getGlobal() -const maybeIteratorPrototype = global.Iterator?.prototype || {} +const maybeIteratorPrototype = (globalThis as any).Iterator?.prototype || {} export function makeIterable( iterator: Iterator ): IteratorObject { iterator[Symbol.iterator] = getSelf - return Object.assign(Object.create(maybeIteratorPrototype), iterator) + return assign(Object.create(maybeIteratorPrototype), iterator) } function getSelf() { diff --git a/packages/mobx/src/utils/utils.ts b/packages/mobx/src/utils/utils.ts index a00b927483..413636aefb 100644 --- a/packages/mobx/src/utils/utils.ts +++ b/packages/mobx/src/utils/utils.ts @@ -1,4 +1,4 @@ -import { globalState, die } from "../internal" +import { globalState } from "../internal" // We shorten anything used > 5 times export const assign = Object.assign @@ -17,28 +17,8 @@ export interface Lambda { name?: string } -const hasProxy = typeof Proxy !== "undefined" const plainObjectString = Object.toString() -export function assertProxies() { - if (!hasProxy) { - die( - __DEV__ - ? "`Proxy` objects are not available in the current environment. Please configure MobX to enable a fallback implementation.`" - : "Proxy not available" - ) - } -} - -export function warnAboutProxyRequirement(msg: string) { - if (__DEV__ && globalState.verifyProxies) { - die( - "MobX is currently configured to be able to run in ES5 mode, but in ES5 MobX won't be able to " + - msg - ) - } -} - export function getNextId() { return ++globalState.mobxGuid } @@ -90,7 +70,7 @@ export function isPlainObject(value: any) { if (proto == null) { return true } - const protoConstructor = Object.hasOwnProperty.call(proto, "constructor") && proto.constructor + const protoConstructor = hasProp(proto, "constructor") && proto.constructor return ( typeof protoConstructor === "function" && protoConstructor.toString() === plainObjectString ) @@ -164,17 +144,11 @@ export function isES6Set(thing: unknown): thing is Set { return thing != null && Object.prototype.toString.call(thing) === "[object Set]" } -const hasGetOwnPropertySymbols = typeof Object.getOwnPropertySymbols !== "undefined" - /** * Returns the following: own enumerable keys and symbols. */ export function getPlainObjectKeys(object: any) { const keys = Object.keys(object) - // Not supported in IE, so there are not going to be symbol props anyway... - if (!hasGetOwnPropertySymbols) { - return keys - } const symbols = Object.getOwnPropertySymbols(object) if (!symbols.length) { return keys @@ -184,12 +158,7 @@ export function getPlainObjectKeys(object: any) { // From Immer utils // Returns all own keys, including non-enumerable and symbolic -export const ownKeys: (target: any) => Array = - typeof Reflect !== "undefined" && Reflect.ownKeys - ? Reflect.ownKeys - : hasGetOwnPropertySymbols - ? obj => Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj) as any) - : /* istanbul ignore next */ Object.getOwnPropertyNames +export const ownKeys: (target: any) => Array = Reflect.ownKeys export function stringifyKey(key: any): string { if (typeof key === "string") { @@ -209,18 +178,7 @@ export function hasProp(target: Object, prop: PropertyKey): boolean { return objectPrototype.hasOwnProperty.call(target, prop) } -// From Immer utils -export const getOwnPropertyDescriptors = - Object.getOwnPropertyDescriptors || - function getOwnPropertyDescriptors(target: any) { - // Polyfill needed for Hermes and IE, see https://github.com/facebook/hermes/issues/274 - const res: any = {} - // Note: without polyfill for ownKeys, symbols won't be picked up - ownKeys(target).forEach(key => { - res[key] = getDescriptor(target, key) - }) - return res - } +export const getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors export function getFlag(flags: number, mask: number) { return !!(flags & mask) diff --git a/packages/mobx/tsdx.config.js b/packages/mobx/tsdx.config.js deleted file mode 100644 index 62b1b82aa3..0000000000 --- a/packages/mobx/tsdx.config.js +++ /dev/null @@ -1,35 +0,0 @@ -const { terser } = require("rollup-plugin-terser") - -module.exports = { - // This function will run for each entry/format/env combination - rollup(config, options) { - // MWE: disabled minifying esm builds as source maps aren't too reliable. - // It is a shame because mangle properties saves quite a bit (1 KB gzipped) - // For comparison: - // webpack + terser + unminified mobx: - // 13757 - // webpack + pre-minified mobx: - // 12949 - if (/*options.format === "esm" || */ options.env === "production") { - config.plugins.push( - terser({ - sourcemap: true, - module: true, - compress: { - hoist_funs: true, - passes: 2, - keep_fargs: false, - pure_getters: true, - unsafe: false - }, - mangle: { - properties: { - regex: /_$/ - } - } - }) - ) - } - return config - } -} diff --git a/scripts/build.js b/scripts/build.js deleted file mode 100644 index 8a8fd97b6a..0000000000 --- a/scripts/build.js +++ /dev/null @@ -1,67 +0,0 @@ -const fs = require("fs-extra") -const execa = require("execa") -const minimist = require("minimist") - -const stdio = ["ignore", "inherit", "pipe"] -const opts = { stdio } - -const { - _: [packageName], - target -} = minimist(process.argv.slice(2)) - -// build to publish needs to do more things so it's slower -// for the CI run and local testing this is not necessary -const isPublish = target === "publish" - -// for running tests in CI we need CJS only -const isTest = target === "test" - -const run = async () => { - // TSDX converts passed name argument to lowercase for file name - const pkgPrefix = `${packageName.toLowerCase()}.` - - const tempMove = name => fs.move(`dist/${pkgPrefix}${name}`, `temp/${pkgPrefix}${name}`) - const moveTemp = name => fs.move(`temp/${pkgPrefix}${name}`, `dist/${pkgPrefix}${name}`) - - const build = (format, env) => { - const args = ["build", "--name", packageName, "--format", format] - if (env) { - args.push("--env", env) - } - return execa("tsdx", args, opts) - } - - if (isPublish) { - await fs.emptyDir("temp") - // build dev/prod ESM bundles that can be consumed in browser without NODE_ENV annoyance - // and these builds cannot be run in parallel because tsdx doesn't allow to change target dir - await build("esm", "development") - // tsdx will purge dist folder, so it's necessary to move these - await tempMove(`esm.development.js`) - await tempMove(`esm.development.js.map`) - - // cannot build these concurrently - await build("esm", "production") - await tempMove(`esm.production.min.js`) - await tempMove(`esm.production.min.js.map`) - } - - await build(isTest ? "cjs" : "esm,cjs,umd").catch(err => { - throw new Error(`build failed: ${err.stderr}`) - }) - - if (isPublish) { - // move ESM bundles back to dist folder and remove temp - await moveTemp(`esm.development.js`) - await moveTemp(`esm.development.js.map`) - await moveTemp(`esm.production.min.js`) - await moveTemp(`esm.production.min.js.map`) - await fs.remove("temp") - } -} - -run().catch(err => { - console.error(err) - process.exit(1) -}) diff --git a/scripts/create-rollup-config.mjs b/scripts/create-rollup-config.mjs new file mode 100644 index 0000000000..5e31376413 --- /dev/null +++ b/scripts/create-rollup-config.mjs @@ -0,0 +1,267 @@ +import { DEFAULT_EXTENSIONS } from "@babel/core" +import { babel } from "@rollup/plugin-babel" +import commonjs from "@rollup/plugin-commonjs" +import json from "@rollup/plugin-json" +import { DEFAULTS as nodeResolveDefaults, nodeResolve } from "@rollup/plugin-node-resolve" +import replace from "@rollup/plugin-replace" +import terser from "@rollup/plugin-terser" +import fs from "fs-extra" +import path from "node:path" +import sourcemaps from "rollup-plugin-sourcemaps" +import typescript from "typescript" +import typescript2 from "rollup-plugin-typescript2" + +const dist = "dist" + +const tsconfigDefaults = { + exclude: [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "node_modules", + "bower_components", + "jspm_packages", + dist + ], + compilerOptions: { + sourceMap: true, + declaration: true, + jsx: "react" + } +} + +const defaultTerserOptions = format => ({ + output: { comments: false }, + compress: { + keep_infinity: true, + pure_getters: true, + passes: 10 + }, + ecma: 5, + toplevel: format === "cjs" +}) + +const privatePropertyTerserOptions = { + module: true, + compress: { + hoist_funs: true, + passes: 2, + keep_fargs: false, + pure_getters: true, + unsafe: false + }, + mangle: { + properties: { + regex: /_$/ + } + } +} + +const writeCjsEntry = packageBase => { + const contents = ` +'use strict' + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./${packageBase}.cjs.production.min.js') +} else { + module.exports = require('./${packageBase}.cjs.development.js') +} +` + fs.outputFileSync(path.join(dist, "index.js"), contents) +} + +const stripShebang = () => ({ + name: "strip-shebang", + transform(code) { + return { + code: code.replace(/^#!(.*)/, ""), + map: null + } + } +}) + +const babelPlugin = () => + babel({ + babelHelpers: "bundled", + exclude: "node_modules/**", + extensions: [...DEFAULT_EXTENSIONS, ".ts", ".tsx"], + babelrc: false, + configFile: false, + passPerPreset: true, + presets: [ + [ + "@babel/preset-env", + { + bugfixes: true, + ignoreBrowserslistConfig: true, + loose: true, + modules: false, + targets: { + esmodules: true + } + } + ] + ], + plugins: [ + "babel-plugin-annotate-pure-calls", + "babel-plugin-dev-expression", + ["@babel/plugin-proposal-class-properties", { loose: true }] + ].filter(Boolean) + }) + +const createConfig = ({ + format, + env, + declarations, + extraOutputs = [], + input, + packageBase, + packageName, + globals +}) => { + const shouldMinify = env === "production" + const outputName = [`${dist}/${packageBase}`, format, env, shouldMinify ? "min" : "", "js"] + .filter(Boolean) + .join(".") + + const output = { + file: outputName, + format, + freeze: false, + esModule: true, + name: packageName, + sourcemap: true, + globals, + exports: "named" + } + + return { + input, + external(id) { + if (id.startsWith("regenerator-runtime")) { + return false + } + return !id.startsWith(".") && !path.isAbsolute(id) + }, + treeshake: { + propertyReadSideEffects: false + }, + output: [output, ...extraOutputs], + plugins: [ + nodeResolve({ + mainFields: ["module", "main", "browser"], + extensions: [...nodeResolveDefaults.extensions, ".jsx"] + }), + commonjs({ + include: format === "umd" ? /\/node_modules\// : /\/regenerator-runtime\// + }), + json(), + stripShebang(), + typescript2({ + typescript, + tsconfig: "tsconfig.json", + tsconfigDefaults, + tsconfigOverride: { + compilerOptions: { + target: "esnext", + ...(declarations ? {} : { declaration: false, declarationMap: false }) + } + }, + check: declarations, + useTsconfigDeclarationDir: false + }), + babelPlugin(), + env !== undefined && + replace({ + preventAssignment: true, + values: { + "process.env.NODE_ENV": JSON.stringify(env) + } + }), + sourcemaps(), + shouldMinify && terser(defaultTerserOptions(format)), + shouldMinify && terser(privatePropertyTerserOptions) + ].filter(Boolean) + } +} + +export default function createRollupConfig({ + packageName, + packageBase = packageName, + input, + globals = {} +}) { + const target = process.env.TARGET + const isTest = target === "test" + const isPublish = target === "publish" + const sharedOptions = { input, packageBase, packageName, globals } + const configs = [] + + configs.push( + createConfig({ ...sharedOptions, format: "cjs", env: "development", declarations: true }) + ) + configs.push( + createConfig({ ...sharedOptions, format: "cjs", env: "production", declarations: false }) + ) + + if (!isTest) { + if (isPublish) { + configs.push( + createConfig({ + ...sharedOptions, + format: "esm", + env: "development", + declarations: false + }) + ) + configs.push( + createConfig({ + ...sharedOptions, + format: "esm", + env: "production", + declarations: false + }) + ) + } + + configs.push( + createConfig({ + ...sharedOptions, + format: "esm", + declarations: false, + extraOutputs: [ + { + file: `${dist}/${packageBase}.mjs`, + format: "esm", + freeze: false, + esModule: true, + sourcemap: true, + exports: "named" + } + ] + }) + ) + configs.push( + createConfig({ + ...sharedOptions, + format: "umd", + env: "development", + declarations: false + }) + ) + configs.push( + createConfig({ + ...sharedOptions, + format: "umd", + env: "production", + declarations: false + }) + ) + } + + fs.emptyDirSync(dist) + writeCjsEntry(packageBase) + + return configs +} diff --git a/scripts/size/index.js b/scripts/size/index.js new file mode 100644 index 0000000000..3bd2701b06 --- /dev/null +++ b/scripts/size/index.js @@ -0,0 +1,174 @@ +"use strict" + +const fs = require("fs") +const path = require("path") +const { spawnSync } = require("child_process") +const zlib = require("zlib") + +const repoRoot = path.join(__dirname, "..", "..") +const shouldBuild = !process.argv.includes("--no-build") + +const publishBuilds = [ + ["mobx", "mobx"], + ["mobx-react-lite", "mobx-react-lite"], + ["mobx-react", "mobx-react"] +] + +const artifacts = [ + { + packageName: "mobx bundle ESM", + file: "packages/mobx/dist/mobx.esm.production.min.js" + }, + { + packageName: "mobx-react-lite", + file: "packages/mobx-react-lite/dist/mobxreactlite.esm.production.min.js" + }, + { + packageName: "mobx-react", + file: "packages/mobx-react/dist/mobxreact.esm.production.min.js" + } +] + +const terserOptions = { + module: true, + compress: { + hoist_funs: true, + passes: 2, + keep_fargs: false, + pure_getters: true, + unsafe: false + }, + mangle: { + properties: { + regex: /_$/ + } + } +} + +const formatBytes = bytes => `${(bytes / 1024).toFixed(2)} KiB` + +const measureSource = source => { + const raw = typeof source === "string" ? Buffer.byteLength(source) : source.length + const gzipped = zlib.gzipSync(source, { level: 9 }) + + return { + raw, + gzip: gzipped.length + } +} + +const measureFile = file => { + const filePath = path.join(repoRoot, file) + const source = fs.readFileSync(filePath) + + return measureSource(source) +} + +const createSizeRow = ({ packageName, raw, gzip }) => ({ + package: packageName, + raw: formatBytes(raw), + "raw bytes": raw, + gzip: formatBytes(gzip), + "gzip bytes": gzip +}) + +const runBuild = ([workspaceName, packageName]) => { + console.log(`\nBuilding ${packageName} publish artifacts...`) + const result = spawnSync( + "npm", + ["-w", workspaceName, "run", "build", "--", "--environment", "TARGET:publish"], + { + cwd: repoRoot, + stdio: "inherit", + shell: process.platform === "win32" + } + ) + + if (result.error) { + throw result.error + } + if (result.status !== 0) { + throw new Error(`${packageName} publish build failed with exit code ${result.status}`) + } +} + +const measureMinimalExample = async () => { + const { rollup } = require("rollup") + const nodeResolve = require("@rollup/plugin-node-resolve") + const replace = require("@rollup/plugin-replace") + const terser = require("@rollup/plugin-terser") + const input = path.join(__dirname, "minimal-example.js") + const resolve = nodeResolve.default || nodeResolve.nodeResolve || nodeResolve + + const bundle = await rollup({ + input, + onwarn(warning, warn) { + if (warning.code === "THIS_IS_UNDEFINED") { + return + } + warn(warning) + }, + plugins: [ + { + name: "size-minimal-example", + load(id) { + if (id === input) { + return fs.readFileSync(input, "utf8") + } + return null + } + }, + replace({ + preventAssignment: true, + values: { + "process.env.NODE_ENV": JSON.stringify("production") + } + }), + resolve({ + mainFields: ["module", "main"] + }) + ] + }) + + try { + const { output } = await bundle.generate({ + format: "esm", + plugins: [terser(terserOptions)] + }) + + return { + packageName: "mobx minimal* ESM", + ...measureSource(output[0].code) + } + } finally { + await bundle.close() + } +} + +const run = async () => { + if (shouldBuild) { + publishBuilds.forEach(runBuild) + } + + const rows = [ + createSizeRow({ + packageName: artifacts[0].packageName, + ...measureFile(artifacts[0].file) + }), + createSizeRow(await measureMinimalExample()), + ...artifacts.slice(1).map(artifact => + createSizeRow({ + packageName: artifact.packageName, + ...measureFile(artifact.file) + }) + ) + ] + + console.log("\nProduction ESM gzip sizes:") + console.table(rows) +} + +run().catch(error => { + console.error(error) + process.exit(1) +}) diff --git a/scripts/size/minimal-example.js b/scripts/size/minimal-example.js new file mode 100644 index 0000000000..2eac7b934c --- /dev/null +++ b/scripts/size/minimal-example.js @@ -0,0 +1,14 @@ +import { action, autorun, computed, observable } from "mobx" + +const state = observable({ count: 1 }) +const doubled = computed(() => state.count * 2) +const increment = action(() => { + state.count += 1 +}) + +const dispose = autorun(() => { + console.log(doubled.get()) +}) + +increment() +dispose() diff --git a/tsconfig.json b/tsconfig.json index cd7ed854ab..dca1d62e9e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,6 @@ "noImplicitAny": false, "noImplicitThis": false, "noEmit": true, - "experimentalDecorators": true, "useDefineForClassFields": true, "jsx": "react", "esModuleInterop": true, diff --git a/website/i18n/en.json b/website/i18n/en.json index 85973a3fda..67b75a7e6b 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -99,6 +99,10 @@ "title": "Migrating from MobX 4/5", "sidebar_label": "Migrating from MobX 4/5 {🚀}" }, + "migrating-from-6-to-7": { + "title": "Migrating to MobX 7", + "sidebar_label": "Migrating to MobX 7 {🚀}" + }, "mobx-utils": { "title": "MobX-utils", "sidebar_label": "MobX-utils {🚀}" diff --git a/website/sidebars.json b/website/sidebars.json index 2d5d8196e8..c561a7524e 100755 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -33,6 +33,7 @@ "Fine-tuning": [ "configuration", "enabling-decorators", + "migrating-from-6-to-7", "migrating-from-4-or-5" ] } diff --git a/website/static/getting-started.html b/website/static/getting-started.html index 139ea8a016..5eda14025d 100755 --- a/website/static/getting-started.html +++ b/website/static/getting-started.html @@ -163,7 +163,7 @@

    Becoming reactive

    list. By using the observable and computed annotations we can introduce observable properties on an object. In the example below we use makeObservable to show the annotations explicitly, - but we could have used makeAutoObservable(this) instead to simplify this process. + but we could use makeAutoObservable(this) instead to simplify this process.

    That's it! We marked some properties as being observable to signal MobX that these values can change over time. - The computations are decorated with computed to identify that these can be derived from the state and caches as long as no underlying state changed. + The computations are marked with computed to identify that these can be derived from the state and caches as long as no underlying state changed.

    The pendingRequests and assignee attributes are not used so far, @@ -243,7 +243,7 @@

    Becoming reactive

    Making React reactive

    Ok, so far we made a silly report reactive. Time to build a reactive user interface around this very same store. React components are (despite their name) not reactive out of the box. - The observer HoC wrapper from the mobx-react-lite package fixes that by + The observer wrapper from the mobx-react package fixes that by basically wrapping the React component in autorun. This keeps the component in sync with the state. This is conceptually not different from what we did with the report before. @@ -311,9 +311,8 @@

    Making React reactive

    ); }) -ReactDOM.render( - , - document.getElementById('reactjs-app') +renderReactApp( + ); @@ -396,22 +395,22 @@

    Asynchronous actions

    Conclusion

    That's all! No boilerplate. Just some simple, declarative components that form our complete UI. And which are derived completely, - reactively from our state. You are now ready to start using the mobx and mobx-react-lite packages in your own applications. + reactively from our state. You are now ready to start using the mobx and mobx-react packages in your own applications. A short summary of the things you learned so far:

    1. - Use the observable decorator or observable(object or array) functions to make objects trackable for MobX. + Use makeObservable, makeAutoObservable, or observable(object or array) to make objects trackable for MobX.
    2. - The computed decorator can be used to create functions that can automatically derive value from the state and cache them. + The computed annotation can be used to create functions that can automatically derive value from the state and cache them.
    3. Use autorun to automatically run functions that depend on some observable state. This is useful for logging, making network requests, etc.
    4. - Use the observer wrapper from the mobx-react-lite package to make your React components truly reactive. + Use the observer wrapper from the mobx-react package to make your React components truly reactive. They will update automatically and efficiently. Even when used in large complex applications with large amounts of data.
    5. @@ -474,11 +473,12 @@

      Console log - - + + - - + + +