@xh/hoist 73.0.0-SNAPSHOT.1747323994993 → 73.0.0-SNAPSHOT.1747334203807

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 CHANGED
@@ -12,6 +12,8 @@
12
12
  initialize a new `ViewManagerModel`.
13
13
  * For clarity, [here is where Toolbox makes that call](https://github.com/xh/toolbox/blob/f15a8018ce36c2ae998b45724b48a16320b88e49/client-app/src/admin/AppModel.ts#L12).
14
14
  * Requires call to `makeObservable(this)` in model constructors with `@bindable` (see below).
15
+ Note that there is a new runtime check on all instances of `HoistBase` to warn if this call has not
16
+ been made.
15
17
  * Requires @xh/hoist-dev-utils >= 11.0
16
18
  * Apps must also rename their .eslintrc file to eslint.client.cjs and copy the configuration found in Toolbox.
17
19
 
@@ -19,6 +19,7 @@ export interface HoistBaseClass {
19
19
  export declare abstract class HoistBase {
20
20
  static get isHoistBase(): boolean;
21
21
  get isHoistBase(): boolean;
22
+ constructor();
22
23
  /**
23
24
  * For XH internal use only - marks this instance as created by and for Hoist as part of its
24
25
  * own implementation. Used as a filter within Hoist Inspector to distinguish services and
@@ -6,4 +6,9 @@ export declare function makeObservable(target: any, annotations?: AnnotationsMap
6
6
  /**
7
7
  * An enhanced version of the native mobx isObservableProp
8
8
  */
9
- export declare function isObservableProp(target: any, propertyKey: PropertyKey): boolean;
9
+ export declare function isObservableProp(target: any, propertyKey: string): boolean;
10
+ /**
11
+ * Check that if a class property was annotated with @bindable or @observable that
12
+ * makeObservable was actually called on the instance. Log error on fail.
13
+ */
14
+ export declare function checkMakeObservable(target: any): void;
package/core/HoistBase.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  import {
8
8
  action,
9
9
  autorun as mobxAutorun,
10
+ checkMakeObservable,
10
11
  comparer,
11
12
  reaction as mobxReaction,
12
13
  runInAction,
@@ -34,6 +35,7 @@ import {
34
35
  import {IAutorunOptions, IReactionOptions} from 'mobx/dist/api/autorun';
35
36
  import {IEqualsComparer, IReactionDisposer} from 'mobx/dist/internal';
36
37
  import {DebounceSpec, PersistableState, PersistenceProvider, PersistOptions, Some, XH} from './';
38
+ import {wait} from '@xh/hoist/promise';
37
39
 
38
40
  export interface HoistBaseClass {
39
41
  new (...args: any[]): HoistBase;
@@ -58,6 +60,12 @@ export abstract class HoistBase {
58
60
  return true;
59
61
  }
60
62
 
63
+ constructor() {
64
+ if (XH.isDevelopmentMode) {
65
+ wait().then(() => checkMakeObservable(this));
66
+ }
67
+ }
68
+
61
69
  /**
62
70
  * For XH internal use only - marks this instance as created by and for Hoist as part of its
63
71
  * own implementation. Used as a filter within Hoist Inspector to distinguish services and
package/mobx/overrides.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
- import {forEach} from 'lodash';
7
+ import {forEach, isEmpty} from 'lodash';
8
8
  import {
9
9
  AnnotationsMap,
10
10
  CreateObservableOptions,
@@ -13,6 +13,7 @@ import {
13
13
  observable,
14
14
  runInAction
15
15
  } from 'mobx';
16
+ import {logError} from '@xh/hoist/utils/js';
16
17
 
17
18
  /**
18
19
  * An enhanced version of the native mobx makeObservable.
@@ -24,15 +25,14 @@ export function makeObservable(
24
25
  ) {
25
26
  // Finish creating 'bindable' properties for this instance.
26
27
  forEach(target._xhBindableProperties, ({isRef}, name) => {
27
- const propName = `_${name}_bindable`;
28
-
29
28
  // makeObservable is called by each constructor in the class hierarchy.
30
29
  // Don't process the property before initialized...or if its already processed
31
- if (!target.hasOwnProperty(name) || target.hasOwnProperty(propName)) {
30
+ if (!target.hasOwnProperty(name) || isBindableCreated(target, name)) {
32
31
  return;
33
32
  }
34
33
 
35
- const initVal = target[name];
34
+ const initVal = target[name],
35
+ propName = `_${name}_bindable`;
36
36
  target[propName] = isRef ? observable.box(initVal, {deep: false}) : observable.box(initVal);
37
37
  Object.defineProperty(target, name, {
38
38
  get() {
@@ -52,8 +52,44 @@ export function makeObservable(
52
52
  /**
53
53
  * An enhanced version of the native mobx isObservableProp
54
54
  */
55
- export function isObservableProp(target: any, propertyKey: PropertyKey): boolean {
56
- return (
57
- baseIsObservableProp(target, propertyKey) || !!target?._xhBindableProperties?.[propertyKey]
55
+ export function isObservableProp(target: any, propertyKey: string): boolean {
56
+ return baseIsObservableProp(target, propertyKey) || isBindableCreated(target, propertyKey);
57
+ }
58
+
59
+ /**
60
+ * Check that if a class property was annotated with @bindable or @observable that
61
+ * makeObservable was actually called on the instance. Log error on fail.
62
+ */
63
+ export function checkMakeObservable(target: any) {
64
+ const missing = [];
65
+
66
+ // Check @bindable props
67
+ forEach(target._xhBindableProperties, (v, k) => {
68
+ if (!isBindableCreated(target, k)) missing.push(k);
69
+ });
70
+
71
+ // Check @observable props -- use internal mobx collection containing unprocessed annotations.
72
+ const sym = Object.getOwnPropertySymbols(target).find(
73
+ it => it.toString() == 'Symbol(mobx-stored-annotations)'
58
74
  );
75
+ if (sym) {
76
+ forEach(target[sym], (v, k) => {
77
+ if (v.annotationType_?.startsWith('observable')) missing.push(k);
78
+ });
79
+ }
80
+
81
+ if (!isEmpty(missing)) {
82
+ logError(
83
+ `Observable properties [${missing.join(', ')}] not initialized properly. ` +
84
+ 'Ensure you call makeObservable() in your constructor',
85
+ target
86
+ );
87
+ }
88
+ }
89
+
90
+ //--------------------
91
+ // Implementation
92
+ //--------------------
93
+ function isBindableCreated(target: any, name: string): boolean {
94
+ return target.hasOwnProperty(`_${name}_bindable`);
59
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "73.0.0-SNAPSHOT.1747323994993",
3
+ "version": "73.0.0-SNAPSHOT.1747334203807",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",
@@ -96,7 +96,7 @@
96
96
  "@ag-grid-community/react": "31.x",
97
97
  "@types/react": "18.x",
98
98
  "@types/react-dom": "18.x",
99
- "@xh/hoist-dev-utils": "11.0.0-SNAPSHOT.1747323244863",
99
+ "@xh/hoist-dev-utils": "11.0.0-SNAPSHOT.1747333512169",
100
100
  "csstype": "3.x",
101
101
  "eslint": "9.x",
102
102
  "eslint-config-prettier": "10.x",