mobx-keystone 1.23.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/LICENSE +21 -21
- package/README.md +1 -1
- package/dist/mobx-keystone.esm.js +113 -62
- package/dist/mobx-keystone.esm.mjs +113 -62
- package/dist/mobx-keystone.umd.js +115 -61
- package/dist/types/modelShared/modelDecorator.d.ts +1 -1
- package/dist/types/utils/index.d.ts +24 -0
- package/package.json +5 -2
- package/src/actionMiddlewares/undoMiddleware.ts +19 -13
- package/src/modelShared/modelDecorator.ts +64 -53
- package/src/ref/Ref.ts +4 -5
- package/src/ref/core.ts +2 -2
- package/src/treeUtils/draft.ts +7 -8
- package/src/tweaker/tweakPlainObject.ts +3 -2
- package/src/utils/index.ts +134 -3
|
@@ -9,7 +9,7 @@ import { AnyFunction } from '../utils/AnyFunction';
|
|
|
9
9
|
* @param name Unique name for the model type. Note that this name must be unique for your whole
|
|
10
10
|
* application, so it is usually a good idea to use some prefix unique to your application domain.
|
|
11
11
|
*/
|
|
12
|
-
export declare const model: (name: string) => <MC extends ModelClass<AnyModel | AnyDataModel>>(clazz: MC,
|
|
12
|
+
export declare const model: (name: string) => <MC extends ModelClass<AnyModel | AnyDataModel>>(clazz: MC, _context?: ClassDecoratorContext<MC>) => MC;
|
|
13
13
|
/**
|
|
14
14
|
* Marks a class (which MUST inherit from the `Model` abstract class)
|
|
15
15
|
* as a model and decorates some of its methods/properties.
|
|
@@ -4,3 +4,27 @@
|
|
|
4
4
|
export declare class MobxKeystoneError extends Error {
|
|
5
5
|
constructor(msg: string);
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* MobX method action decorator compatible with both legacy decorators and
|
|
9
|
+
* MobX 7's standard decorator API.
|
|
10
|
+
*
|
|
11
|
+
* This is a decorator compatibility helper, not a replacement for other
|
|
12
|
+
* `action` overloads such as `action(fn)`. Field decorators are not supported.
|
|
13
|
+
*
|
|
14
|
+
* @hidden
|
|
15
|
+
*/
|
|
16
|
+
export declare function mobxAction<This, Args extends unknown[], Return>(value: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>): (this: This, ...args: Args) => Return;
|
|
17
|
+
export declare function mobxAction<This, Args extends unknown[], Return>(target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Return>): TypedPropertyDescriptor<(this: This, ...args: Args) => Return> | void;
|
|
18
|
+
/**
|
|
19
|
+
* MobX getter computed decorator compatible with both legacy decorators and
|
|
20
|
+
* MobX 7's standard decorator API.
|
|
21
|
+
*
|
|
22
|
+
* This is a decorator compatibility helper, not a replacement for other
|
|
23
|
+
* `computed` overloads. Field decorators and decorator options are not supported.
|
|
24
|
+
* Under MobX 7's legacy decorator transform the getter is backed by a computed
|
|
25
|
+
* value, but is not registered as a computed property for MobX introspection.
|
|
26
|
+
*
|
|
27
|
+
* @hidden
|
|
28
|
+
*/
|
|
29
|
+
export declare function mobxComputed<This, Value>(value: (this: This) => Value, context: ClassGetterDecoratorContext<This, Value>): ((this: This) => Value) | void;
|
|
30
|
+
export declare function mobxComputed<Value>(target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<Value>): TypedPropertyDescriptor<Value> | void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobx-keystone",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "A MobX powered state management solution based on data trees with first class support for TypeScript, snapshots, patches and much more",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mobx",
|
|
@@ -63,11 +63,12 @@
|
|
|
63
63
|
"build-docs": "shx rm -rf api-docs && node --import ./scripts/typedoc-typescript6-register.mjs ./node_modules/typedoc/dist/cli.js --options ./typedocconfig.js src/index.ts && shx rm -rf ../../apps/site/generated-static/api && shx mkdir -p ../../apps/site/generated-static/api && shx cp -R ./api-docs/* ../../apps/site/generated-static/api"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"mobx": "^6.0.0 || ^5.0.0 || ^4.0.0"
|
|
66
|
+
"mobx": "^7.0.0 || ^6.0.0 || ^5.0.0 || ^4.0.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@babel/core": "^8.0.1",
|
|
70
70
|
"@babel/plugin-proposal-decorators": "^8.0.2",
|
|
71
|
+
"@babel/plugin-transform-class-static-block": "^8.0.1",
|
|
71
72
|
"@babel/plugin-transform-class-properties": "^8.0.1",
|
|
72
73
|
"@babel/plugin-transform-typescript": "^8.0.1",
|
|
73
74
|
"@babel/preset-env": "^8.0.2",
|
|
@@ -78,6 +79,8 @@
|
|
|
78
79
|
"@vitest/coverage-v8": "^4.1.10",
|
|
79
80
|
"mobx-v4": "npm:mobx@^4.15.7",
|
|
80
81
|
"mobx-v5": "npm:mobx@^5.15.7",
|
|
82
|
+
"mobx-v6": "npm:mobx@^6.0.0",
|
|
83
|
+
"mobx-v7": "npm:mobx@^7.0.0",
|
|
81
84
|
"shx": "^0.4.0",
|
|
82
85
|
"spec.ts": "^1.1.3",
|
|
83
86
|
"ts-node": "^10.9.2",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { action, computed } from "mobx"
|
|
2
1
|
import type { ActionMiddlewareDisposer } from "../action/middleware"
|
|
3
2
|
import { modelAction } from "../action/modelAction"
|
|
4
3
|
import { Model } from "../model/Model"
|
|
@@ -10,7 +9,14 @@ import { assertTweakedObject } from "../tweaker/core"
|
|
|
10
9
|
import { typesArray } from "../types/arrayBased/typesArray"
|
|
11
10
|
import { tProp } from "../types/tProp"
|
|
12
11
|
import { typesUnchecked } from "../types/utility/typesUnchecked"
|
|
13
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
failure,
|
|
14
|
+
getMobxVersion,
|
|
15
|
+
makeObservableCompat,
|
|
16
|
+
mobxAction,
|
|
17
|
+
mobxComputed,
|
|
18
|
+
namespace,
|
|
19
|
+
} from "../utils"
|
|
14
20
|
import { actionTrackingMiddleware, type SimpleActionContext } from "./actionTrackingMiddleware"
|
|
15
21
|
|
|
16
22
|
/**
|
|
@@ -306,7 +312,7 @@ export class UndoManager {
|
|
|
306
312
|
* The undo stack, where the first operation to undo will be the last of the array.
|
|
307
313
|
* Do not manipulate this array directly.
|
|
308
314
|
*/
|
|
309
|
-
@
|
|
315
|
+
@mobxComputed
|
|
310
316
|
get undoQueue(): ReadonlyArray<UndoEvent> {
|
|
311
317
|
return this.store.undoEvents
|
|
312
318
|
}
|
|
@@ -315,7 +321,7 @@ export class UndoManager {
|
|
|
315
321
|
* The redo stack, where the first operation to redo will be the last of the array.
|
|
316
322
|
* Do not manipulate this array directly.
|
|
317
323
|
*/
|
|
318
|
-
@
|
|
324
|
+
@mobxComputed
|
|
319
325
|
get redoQueue(): ReadonlyArray<UndoEvent> {
|
|
320
326
|
return this.store.redoEvents
|
|
321
327
|
}
|
|
@@ -323,7 +329,7 @@ export class UndoManager {
|
|
|
323
329
|
/**
|
|
324
330
|
* The number of undo actions available.
|
|
325
331
|
*/
|
|
326
|
-
@
|
|
332
|
+
@mobxComputed
|
|
327
333
|
get undoLevels() {
|
|
328
334
|
return this.undoQueue.length
|
|
329
335
|
}
|
|
@@ -331,7 +337,7 @@ export class UndoManager {
|
|
|
331
337
|
/**
|
|
332
338
|
* If undo can be performed (if there is at least one undo action available).
|
|
333
339
|
*/
|
|
334
|
-
@
|
|
340
|
+
@mobxComputed
|
|
335
341
|
get canUndo() {
|
|
336
342
|
return this.undoLevels > 0
|
|
337
343
|
}
|
|
@@ -339,7 +345,7 @@ export class UndoManager {
|
|
|
339
345
|
/**
|
|
340
346
|
* Clears the undo queue.
|
|
341
347
|
*/
|
|
342
|
-
@
|
|
348
|
+
@mobxAction
|
|
343
349
|
clearUndo() {
|
|
344
350
|
this.store._clearUndo()
|
|
345
351
|
}
|
|
@@ -347,7 +353,7 @@ export class UndoManager {
|
|
|
347
353
|
/**
|
|
348
354
|
* The number of redo actions available.
|
|
349
355
|
*/
|
|
350
|
-
@
|
|
356
|
+
@mobxComputed
|
|
351
357
|
get redoLevels() {
|
|
352
358
|
return this.redoQueue.length
|
|
353
359
|
}
|
|
@@ -355,7 +361,7 @@ export class UndoManager {
|
|
|
355
361
|
/**
|
|
356
362
|
* If redo can be performed (if there is at least one redo action available)
|
|
357
363
|
*/
|
|
358
|
-
@
|
|
364
|
+
@mobxComputed
|
|
359
365
|
get canRedo() {
|
|
360
366
|
return this.redoLevels > 0
|
|
361
367
|
}
|
|
@@ -363,7 +369,7 @@ export class UndoManager {
|
|
|
363
369
|
/**
|
|
364
370
|
* Clears the redo queue.
|
|
365
371
|
*/
|
|
366
|
-
@
|
|
372
|
+
@mobxAction
|
|
367
373
|
clearRedo() {
|
|
368
374
|
this.store._clearRedo()
|
|
369
375
|
}
|
|
@@ -372,7 +378,7 @@ export class UndoManager {
|
|
|
372
378
|
* Undoes the last action.
|
|
373
379
|
* Will throw if there is no action to undo.
|
|
374
380
|
*/
|
|
375
|
-
@
|
|
381
|
+
@mobxAction
|
|
376
382
|
undo() {
|
|
377
383
|
if (!this.canUndo) {
|
|
378
384
|
throw failure("nothing to undo")
|
|
@@ -397,7 +403,7 @@ export class UndoManager {
|
|
|
397
403
|
* Redoes the previous action.
|
|
398
404
|
* Will throw if there is no action to redo.
|
|
399
405
|
*/
|
|
400
|
-
@
|
|
406
|
+
@mobxAction
|
|
401
407
|
redo() {
|
|
402
408
|
if (!this.canRedo) {
|
|
403
409
|
throw failure("nothing to redo")
|
|
@@ -619,7 +625,7 @@ export class UndoManager {
|
|
|
619
625
|
this.options = options
|
|
620
626
|
|
|
621
627
|
if (getMobxVersion() >= 6) {
|
|
622
|
-
|
|
628
|
+
makeObservableCompat(this)
|
|
623
629
|
}
|
|
624
630
|
|
|
625
631
|
this.store = store ?? new UndoStore({})
|
|
@@ -15,10 +15,11 @@ import {
|
|
|
15
15
|
} from "../modelShared/modelSymbols"
|
|
16
16
|
import { refreshInternalSnapshot } from "../snapshot/internal"
|
|
17
17
|
import {
|
|
18
|
+
addLateInitializationFunction,
|
|
18
19
|
failure,
|
|
19
20
|
getMobxVersion,
|
|
20
21
|
logWarning,
|
|
21
|
-
|
|
22
|
+
makeObservableCompat,
|
|
22
23
|
runAfterNewSymbol,
|
|
23
24
|
runBeforeOnInitSymbol,
|
|
24
25
|
runLateInitializationFunctions,
|
|
@@ -34,11 +35,11 @@ import type { AnyFunction } from "../utils/AnyFunction"
|
|
|
34
35
|
*/
|
|
35
36
|
export const model =
|
|
36
37
|
(name: string) =>
|
|
37
|
-
<MC extends ModelClass<AnyModel | AnyDataModel>>(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
<MC extends ModelClass<AnyModel | AnyDataModel>>(
|
|
39
|
+
clazz: MC,
|
|
40
|
+
_context?: ClassDecoratorContext<MC>
|
|
41
|
+
): MC =>
|
|
42
|
+
internalModel(name, clazz) as any
|
|
42
43
|
|
|
43
44
|
const afterClassInitializationData = new WeakMap<
|
|
44
45
|
ModelClass<AnyModel | AnyDataModel>,
|
|
@@ -56,14 +57,14 @@ const runAfterClassInitialization = (
|
|
|
56
57
|
|
|
57
58
|
const tag = afterClassInitializationData.get(target)!
|
|
58
59
|
|
|
59
|
-
// compatibility with mobx 6
|
|
60
|
+
// compatibility with mobx 6 and 7
|
|
60
61
|
if (tag.needsMakeObservable) {
|
|
61
62
|
// we know it can be done and shouldn't fail
|
|
62
|
-
|
|
63
|
+
makeObservableCompat(instance)
|
|
63
64
|
} else if (tag.needsMakeObservable === undefined) {
|
|
64
65
|
if (getMobxVersion() >= 6) {
|
|
65
66
|
try {
|
|
66
|
-
|
|
67
|
+
makeObservableCompat(instance)
|
|
67
68
|
tag.needsMakeObservable = true
|
|
68
69
|
} catch (e) {
|
|
69
70
|
const err = e as Error
|
|
@@ -121,8 +122,7 @@ const proxyClassHandler: ProxyHandler<ModelClass<AnyModel | AnyDataModel>> = {
|
|
|
121
122
|
|
|
122
123
|
const internalModel = <MC extends ModelClass<AnyModel | AnyDataModel>>(
|
|
123
124
|
name: string,
|
|
124
|
-
clazz: MC
|
|
125
|
-
addInitializer: ((initializer: (this: any) => void) => void) | undefined
|
|
125
|
+
clazz: MC
|
|
126
126
|
): MC | void => {
|
|
127
127
|
const type = isModelClass(clazz) ? "class" : isDataModelClass(clazz) ? "data" : undefined
|
|
128
128
|
if (!type) {
|
|
@@ -151,48 +151,24 @@ const internalModel = <MC extends ModelClass<AnyModel | AnyDataModel>>(
|
|
|
151
151
|
// track if we fail so we only try it once per class
|
|
152
152
|
afterClassInitializationData.set(clazz, { needsMakeObservable: undefined, type })
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
addInitializer(function (this: any) {
|
|
157
|
-
runAfterClassInitialization(clazz, this)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
const modelInfo = {
|
|
161
|
-
name,
|
|
162
|
-
class: clazz,
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
modelInfoByName[name] = modelInfo
|
|
154
|
+
const decoratedClass = new Proxy<MC>(clazz, proxyClassHandler)
|
|
155
|
+
;(decoratedClass as any)[modelUnwrappedClassSymbol] = clazz
|
|
166
156
|
|
|
167
|
-
|
|
157
|
+
decoratedClass.prototype.constructor = decoratedClass
|
|
168
158
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
// non-standard decorator API, use proxies
|
|
174
|
-
|
|
175
|
-
// trick so plain new works
|
|
176
|
-
const proxyClass = new Proxy<MC>(clazz, proxyClassHandler)
|
|
177
|
-
|
|
178
|
-
// set or else it points to the undecorated class
|
|
179
|
-
proxyClass.prototype.constructor = proxyClass
|
|
180
|
-
;(proxyClass as any)[modelUnwrappedClassSymbol] = clazz
|
|
181
|
-
|
|
182
|
-
const modelInfo = {
|
|
183
|
-
name,
|
|
184
|
-
class: proxyClass,
|
|
185
|
-
}
|
|
159
|
+
const modelInfo = {
|
|
160
|
+
name,
|
|
161
|
+
class: decoratedClass,
|
|
162
|
+
}
|
|
186
163
|
|
|
187
|
-
|
|
164
|
+
modelInfoByName[name] = modelInfo
|
|
188
165
|
|
|
189
|
-
|
|
190
|
-
|
|
166
|
+
modelInfoByClass.set(decoratedClass, modelInfo)
|
|
167
|
+
modelInfoByClass.set(clazz, modelInfo)
|
|
191
168
|
|
|
192
|
-
|
|
169
|
+
runLateInitializationFunctions(clazz, runAfterModelDecoratorSymbol)
|
|
193
170
|
|
|
194
|
-
|
|
195
|
-
}
|
|
171
|
+
return decoratedClass
|
|
196
172
|
}
|
|
197
173
|
|
|
198
174
|
// basically taken from TS
|
|
@@ -243,18 +219,53 @@ export function decoratedModel<M, MC extends abstract new (...ags: any) => M>(
|
|
|
243
219
|
[k in keyof M]?: AnyFunction | ReadonlyArray<AnyFunction>
|
|
244
220
|
}
|
|
245
221
|
): MC {
|
|
222
|
+
const mobx7Annotations: Record<string, unknown> = {}
|
|
223
|
+
|
|
246
224
|
// decorate class members
|
|
247
225
|
for (const [k, decorator] of Object.entries(decorators)) {
|
|
226
|
+
const decoratorList = Array.isArray(decorator) ? decorator : [decorator]
|
|
227
|
+
const mobx7Annotation = decoratorList.find(isMobxAnnotation)
|
|
228
|
+
if (getMobxVersion() >= 7 && mobx7Annotation) {
|
|
229
|
+
if (decoratorList.length !== 1) {
|
|
230
|
+
throw failure("MobX 7 decorators cannot be combined in decoratedModel")
|
|
231
|
+
}
|
|
232
|
+
mobx7Annotations[k] = mobx7Annotation
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
|
|
248
236
|
const prototypeValueDesc = Object.getOwnPropertyDescriptor(clazz.prototype, k)
|
|
249
237
|
// TS seems to send null for methods in the prototype
|
|
250
238
|
// (which we substitute for the descriptor to avoid a double look-up) and void 0 (undefined) for props
|
|
251
|
-
tsDecorate(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
239
|
+
tsDecorate(decoratorList, clazz.prototype, k, prototypeValueDesc ? prototypeValueDesc : void 0)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (getMobxVersion() >= 7 && Object.keys(mobx7Annotations).length > 0) {
|
|
243
|
+
addLateInitializationFunction(clazz.prototype, runAfterNewSymbol, (instance) => {
|
|
244
|
+
makeObservableCompat(instance, mobx7Annotations)
|
|
245
|
+
})
|
|
257
246
|
}
|
|
258
247
|
|
|
259
248
|
return (name ? model(name)(clazz as unknown as ModelClass<AnyModel>) : clazz) as MC
|
|
260
249
|
}
|
|
250
|
+
|
|
251
|
+
type MobxAnnotation = {
|
|
252
|
+
annotationType_: string
|
|
253
|
+
make_: AnyFunction
|
|
254
|
+
extend_: AnyFunction
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// MobX uses this same structural check in its internal `isAnnotation` helper.
|
|
258
|
+
// That helper is not part of MobX's public root API, so mirror it here instead
|
|
259
|
+
// of depending on a private deep import that may not exist in every supported version.
|
|
260
|
+
function isMobxAnnotation(value: unknown): value is MobxAnnotation {
|
|
261
|
+
if ((typeof value !== "object" || value === null) && typeof value !== "function") {
|
|
262
|
+
return false
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const annotation = value as Partial<MobxAnnotation>
|
|
266
|
+
return (
|
|
267
|
+
typeof annotation.annotationType_ === "string" &&
|
|
268
|
+
typeof annotation.make_ === "function" &&
|
|
269
|
+
typeof annotation.extend_ === "function"
|
|
270
|
+
)
|
|
271
|
+
}
|
package/src/ref/Ref.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { computed } from "mobx"
|
|
2
1
|
import { Model } from "../model/Model"
|
|
3
2
|
import { modelTypeKey } from "../model/metadata"
|
|
4
3
|
import type { ModelClass } from "../modelShared/BaseModelShared"
|
|
5
4
|
import { typesString } from "../types/primitiveBased/typesPrimitive"
|
|
6
5
|
import { tProp } from "../types/tProp"
|
|
7
|
-
import { failure } from "../utils"
|
|
6
|
+
import { failure, mobxComputed } from "../utils"
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* A reference model base type.
|
|
@@ -21,7 +20,7 @@ export abstract class Ref<T extends object> extends Model({
|
|
|
21
20
|
/**
|
|
22
21
|
* The object this reference points to, or `undefined` if the reference is currently invalid.
|
|
23
22
|
*/
|
|
24
|
-
@
|
|
23
|
+
@mobxComputed
|
|
25
24
|
get maybeCurrent(): T | undefined {
|
|
26
25
|
return this.resolve()
|
|
27
26
|
}
|
|
@@ -29,7 +28,7 @@ export abstract class Ref<T extends object> extends Model({
|
|
|
29
28
|
/**
|
|
30
29
|
* If the reference is currently valid.
|
|
31
30
|
*/
|
|
32
|
-
@
|
|
31
|
+
@mobxComputed
|
|
33
32
|
get isValid(): boolean {
|
|
34
33
|
return !!this.maybeCurrent
|
|
35
34
|
}
|
|
@@ -37,7 +36,7 @@ export abstract class Ref<T extends object> extends Model({
|
|
|
37
36
|
/**
|
|
38
37
|
* The object this reference points to, or throws if invalid.
|
|
39
38
|
*/
|
|
40
|
-
@
|
|
39
|
+
@mobxComputed
|
|
41
40
|
get current(): T {
|
|
42
41
|
const current = this.maybeCurrent
|
|
43
42
|
|
package/src/ref/core.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { fastGetRoot } from "../parent/path"
|
|
10
10
|
import { type ComputedWalkTreeAggregate, computedWalkTreeAggregate } from "../parent/walkTree"
|
|
11
11
|
import { assertTweakedObject } from "../tweaker/core"
|
|
12
|
-
import { assertIsObject, failure } from "../utils"
|
|
12
|
+
import { assertIsObject, failure, mobxAction } from "../utils"
|
|
13
13
|
import { getOrCreate } from "../utils/mapUtils"
|
|
14
14
|
import { Ref, type RefConstructor } from "./Ref"
|
|
15
15
|
|
|
@@ -103,7 +103,7 @@ export function internalCustomRef<T extends object>(
|
|
|
103
103
|
return true
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
@
|
|
106
|
+
@mobxAction
|
|
107
107
|
forceUpdateBackRefs() {
|
|
108
108
|
const currentTarget = this.maybeCurrent
|
|
109
109
|
if (!this.startTracking(currentTarget)) {
|
package/src/treeUtils/draft.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { action, computed } from "mobx"
|
|
2
1
|
import { pathToTargetPathIds } from "../actionMiddlewares/utils"
|
|
3
2
|
import { resolvePath, resolvePathCheckingIds, skipIdChecking } from "../parent/path"
|
|
4
3
|
import type { Path } from "../parent/pathTypes"
|
|
@@ -7,7 +6,7 @@ import { applySnapshot } from "../snapshot/applySnapshot"
|
|
|
7
6
|
import { fromSnapshot } from "../snapshot/fromSnapshot"
|
|
8
7
|
import { getSnapshot } from "../snapshot/getSnapshot"
|
|
9
8
|
import { assertTweakedObject } from "../tweaker/core"
|
|
10
|
-
import { failure } from "../utils"
|
|
9
|
+
import { failure, mobxAction, mobxComputed } from "../utils"
|
|
11
10
|
import { deepEquals } from "./deepEquals"
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -25,7 +24,7 @@ export class Draft<T extends object> {
|
|
|
25
24
|
/**
|
|
26
25
|
* Commits current draft changes to the original object.
|
|
27
26
|
*/
|
|
28
|
-
@
|
|
27
|
+
@mobxAction
|
|
29
28
|
commit(): void {
|
|
30
29
|
applySnapshot(this.originalData, getSnapshot(this.data))
|
|
31
30
|
}
|
|
@@ -37,7 +36,7 @@ export class Draft<T extends object> {
|
|
|
37
36
|
*
|
|
38
37
|
* @param path Path to commit.
|
|
39
38
|
*/
|
|
40
|
-
@
|
|
39
|
+
@mobxAction
|
|
41
40
|
commitByPath(path: Path): void {
|
|
42
41
|
const draftTarget = resolvePath(this.data, path)
|
|
43
42
|
if (!draftTarget.resolved) {
|
|
@@ -63,7 +62,7 @@ export class Draft<T extends object> {
|
|
|
63
62
|
/**
|
|
64
63
|
* Resets the draft to be an exact copy of the current state of the original object.
|
|
65
64
|
*/
|
|
66
|
-
@
|
|
65
|
+
@mobxAction
|
|
67
66
|
reset(): void {
|
|
68
67
|
applySnapshot(this.data, this.originalSnapshot)
|
|
69
68
|
}
|
|
@@ -75,7 +74,7 @@ export class Draft<T extends object> {
|
|
|
75
74
|
*
|
|
76
75
|
* @param path Path to reset.
|
|
77
76
|
*/
|
|
78
|
-
@
|
|
77
|
+
@mobxAction
|
|
79
78
|
resetByPath(path: Path): void {
|
|
80
79
|
const originalTarget = resolvePath(this.originalData, path)
|
|
81
80
|
if (!originalTarget.resolved) {
|
|
@@ -101,7 +100,7 @@ export class Draft<T extends object> {
|
|
|
101
100
|
/**
|
|
102
101
|
* Returns `true` if the draft has changed compared to the original object, `false` otherwise.
|
|
103
102
|
*/
|
|
104
|
-
@
|
|
103
|
+
@mobxComputed
|
|
105
104
|
get isDirty(): boolean {
|
|
106
105
|
return !deepEquals(getSnapshot(this.data), this.originalSnapshot)
|
|
107
106
|
}
|
|
@@ -135,7 +134,7 @@ export class Draft<T extends object> {
|
|
|
135
134
|
*/
|
|
136
135
|
readonly originalData: T
|
|
137
136
|
|
|
138
|
-
@
|
|
137
|
+
@mobxComputed
|
|
139
138
|
private get originalSnapshot() {
|
|
140
139
|
return getSnapshot(this.originalData)
|
|
141
140
|
}
|
|
@@ -48,7 +48,8 @@ export function tweakPlainObject<T extends Record<string, any>>(
|
|
|
48
48
|
isDataObject: boolean
|
|
49
49
|
): T {
|
|
50
50
|
const originalObj: Record<string, any> = value
|
|
51
|
-
const
|
|
51
|
+
const originalIsObservableObject = isObservableObject(originalObj)
|
|
52
|
+
const tweakedObj = originalIsObservableObject
|
|
52
53
|
? originalObj
|
|
53
54
|
: observable.object({}, undefined, observableOptions)
|
|
54
55
|
|
|
@@ -78,7 +79,7 @@ export function tweakPlainObject<T extends Record<string, any>>(
|
|
|
78
79
|
const v = originalObj[k]
|
|
79
80
|
|
|
80
81
|
if (isPrimitive(v)) {
|
|
81
|
-
if (!doNotTweakChildren) {
|
|
82
|
+
if (!doNotTweakChildren && !originalIsObservableObject) {
|
|
82
83
|
setIfDifferent(tweakedObj, k, v)
|
|
83
84
|
}
|
|
84
85
|
if (k === "__proto__") {
|
package/src/utils/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as mobx from "mobx"
|
|
2
2
|
import {
|
|
3
|
+
type IComputedValue,
|
|
3
4
|
type IObservableArray,
|
|
4
5
|
isObservableArray,
|
|
5
6
|
isObservableMap,
|
|
@@ -419,6 +420,127 @@ export const mobx6 = {
|
|
|
419
420
|
],
|
|
420
421
|
}
|
|
421
422
|
|
|
423
|
+
/**
|
|
424
|
+
* MobX method action decorator compatible with both legacy decorators and
|
|
425
|
+
* MobX 7's standard decorator API.
|
|
426
|
+
*
|
|
427
|
+
* This is a decorator compatibility helper, not a replacement for other
|
|
428
|
+
* `action` overloads such as `action(fn)`. Field decorators are not supported.
|
|
429
|
+
*
|
|
430
|
+
* @hidden
|
|
431
|
+
*/
|
|
432
|
+
export function mobxAction<This, Args extends unknown[], Return>(
|
|
433
|
+
value: (this: This, ...args: Args) => Return,
|
|
434
|
+
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
|
|
435
|
+
): (this: This, ...args: Args) => Return
|
|
436
|
+
export function mobxAction<This, Args extends unknown[], Return>(
|
|
437
|
+
target: object,
|
|
438
|
+
propertyKey: string | symbol,
|
|
439
|
+
descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Return>
|
|
440
|
+
): TypedPropertyDescriptor<(this: This, ...args: Args) => Return> | void
|
|
441
|
+
export function mobxAction(...args: any[]): any {
|
|
442
|
+
if (isStandardDecoratorContext(args[1])) {
|
|
443
|
+
return (mobx.action as any)(args[0], args[1])
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (getMobxVersion() < 7) {
|
|
447
|
+
return (mobx.action as any)(...args)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const descriptor = args[2] as PropertyDescriptor | undefined
|
|
451
|
+
if (!descriptor?.value) {
|
|
452
|
+
return descriptor
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (mobx.isAction(descriptor.value)) {
|
|
456
|
+
return descriptor
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
...descriptor,
|
|
461
|
+
value: mobx.action(String(args[1]), descriptor.value),
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* MobX getter computed decorator compatible with both legacy decorators and
|
|
467
|
+
* MobX 7's standard decorator API.
|
|
468
|
+
*
|
|
469
|
+
* This is a decorator compatibility helper, not a replacement for other
|
|
470
|
+
* `computed` overloads. Field decorators and decorator options are not supported.
|
|
471
|
+
* Under MobX 7's legacy decorator transform the getter is backed by a computed
|
|
472
|
+
* value, but is not registered as a computed property for MobX introspection.
|
|
473
|
+
*
|
|
474
|
+
* @hidden
|
|
475
|
+
*/
|
|
476
|
+
export function mobxComputed<This, Value>(
|
|
477
|
+
value: (this: This) => Value,
|
|
478
|
+
context: ClassGetterDecoratorContext<This, Value>
|
|
479
|
+
): ((this: This) => Value) | void
|
|
480
|
+
export function mobxComputed<Value>(
|
|
481
|
+
target: object,
|
|
482
|
+
propertyKey: string | symbol,
|
|
483
|
+
descriptor: TypedPropertyDescriptor<Value>
|
|
484
|
+
): TypedPropertyDescriptor<Value> | void
|
|
485
|
+
export function mobxComputed(...args: any[]): any {
|
|
486
|
+
if (isStandardDecoratorContext(args[1])) {
|
|
487
|
+
return (mobx.computed as any)(args[0], args[1])
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (getMobxVersion() < 7) {
|
|
491
|
+
return (mobx.computed as any)(...args)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const descriptor = args[2] as PropertyDescriptor | undefined
|
|
495
|
+
if (!descriptor?.get) {
|
|
496
|
+
return descriptor
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const getter = descriptor.get
|
|
500
|
+
const computedValues = new WeakMap<object, IComputedValue<unknown>>()
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
...descriptor,
|
|
504
|
+
get(this: object) {
|
|
505
|
+
let computedValue = computedValues.get(this)
|
|
506
|
+
if (!computedValue) {
|
|
507
|
+
const newComputedValue = mobx.computed(() => getter.call(this), {
|
|
508
|
+
name: String(args[1]),
|
|
509
|
+
})
|
|
510
|
+
computedValues.set(this, newComputedValue)
|
|
511
|
+
computedValue = newComputedValue
|
|
512
|
+
}
|
|
513
|
+
return computedValue.get()
|
|
514
|
+
},
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Mirrors MobX's standard-decorator call detection. Legacy decorator calls use
|
|
519
|
+
// a string or symbol property key as their second argument, so checking `kind`
|
|
520
|
+
// distinguishes the call shapes without claiming to validate the full context.
|
|
521
|
+
function isStandardDecoratorContext(value: unknown): boolean {
|
|
522
|
+
return (
|
|
523
|
+
typeof value === "object" &&
|
|
524
|
+
value !== null &&
|
|
525
|
+
typeof (value as { kind?: unknown }).kind === "string"
|
|
526
|
+
)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Calls `makeObservable` across MobX versions. MobX 7 requires an annotations
|
|
531
|
+
* object, while MobX 6 and older versions use decorator metadata when no
|
|
532
|
+
* annotations are passed.
|
|
533
|
+
*
|
|
534
|
+
* @internal
|
|
535
|
+
*/
|
|
536
|
+
export function makeObservableCompat(target: object, annotations?: object): void {
|
|
537
|
+
if (getMobxVersion() >= 7) {
|
|
538
|
+
mobx6.makeObservable(target, annotations ?? {})
|
|
539
|
+
} else {
|
|
540
|
+
mobx6.makeObservable(target)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
422
544
|
/**
|
|
423
545
|
* @internal
|
|
424
546
|
*/
|
|
@@ -426,17 +548,26 @@ export function propNameToSetterName(propName: string): string {
|
|
|
426
548
|
return `set${propName[0].toUpperCase()}${propName.slice(1)}`
|
|
427
549
|
}
|
|
428
550
|
|
|
551
|
+
let cachedMobxVersion: number | undefined
|
|
552
|
+
|
|
429
553
|
/**
|
|
430
554
|
* @internal
|
|
431
555
|
*/
|
|
432
556
|
export function getMobxVersion(): number {
|
|
557
|
+
if (cachedMobxVersion !== undefined) {
|
|
558
|
+
return cachedMobxVersion
|
|
559
|
+
}
|
|
560
|
+
|
|
433
561
|
if (mobx6.makeObservable!) {
|
|
434
|
-
|
|
562
|
+
const version = (mobx as any)._getGlobalState?.().version
|
|
563
|
+
cachedMobxVersion = typeof version === "number" && version >= 7 ? version : 6
|
|
435
564
|
} else if ("extendShallowObservable" in mobx) {
|
|
436
|
-
|
|
565
|
+
cachedMobxVersion = 4
|
|
437
566
|
} else {
|
|
438
|
-
|
|
567
|
+
cachedMobxVersion = 5
|
|
439
568
|
}
|
|
569
|
+
|
|
570
|
+
return cachedMobxVersion
|
|
440
571
|
}
|
|
441
572
|
|
|
442
573
|
/**
|