@reckona/mreact-compat 0.0.174 → 0.0.175

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.
@@ -3,11 +3,19 @@ import type { ReactiveDomBlockResult } from "./element.js";
3
3
  export interface ReactivePropCell {
4
4
  value: Record<string, unknown>;
5
5
  source: Source;
6
+ propertySources?: Map<PropertyKey, Source> | undefined;
7
+ propertySnapshots?: Map<PropertyKey, ShallowObjectSnapshot> | undefined;
6
8
  }
7
9
  export interface ReactiveDomBlockState extends ReactiveDomBlockResult {
8
10
  propCell?: ReactivePropCell | undefined;
9
11
  }
10
12
  export declare function createReactivePropCell(props: Record<string, unknown>): ReactivePropCell;
13
+ export declare function batchReactivePropCellUpdates<T>(run: () => T): T;
11
14
  export declare function createReactivePropProxy<P extends object>(cell: ReactivePropCell): P;
15
+ interface ShallowObjectSnapshot {
16
+ value: object;
17
+ entries: Map<PropertyKey, unknown>;
18
+ }
12
19
  export declare function setReactivePropCell(cell: ReactivePropCell, next: Record<string, unknown>): void;
20
+ export {};
13
21
  //# sourceMappingURL=reactive-prop-cell.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reactive-prop-cell.d.ts","sourceRoot":"","sources":["../src/reactive-prop-cell.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,MAAM,EACZ,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAQ3D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IACnE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAEvF;AAqBD,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,CAAC,CAEnF;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI,CAWN"}
1
+ {"version":3,"file":"reactive-prop-cell.d.ts","sourceRoot":"","sources":["../src/reactive-prop-cell.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,MAAM,EACZ,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAQ3D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACvD,iBAAiB,CAAC,EAAE,GAAG,CAAC,WAAW,EAAE,qBAAqB,CAAC,GAAG,SAAS,CAAC;CACzE;AAID,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IACnE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACzC;AAKD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAEvF;AAED,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAW/D;AAuBD,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,CAAC,CAEnF;AAoBD,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CACpC;AAgFD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI,CAuCN"}
@@ -1,14 +1,32 @@
1
+ import { batch } from "@reckona/mreact-reactive-core";
1
2
  import { flushQueuedComputations, notifySubscribers, trackSource, } from "@reckona/mreact-reactive-core/internal";
3
+ let propCellBatchDepth = 0;
4
+ let propCellBatchNeedsFlush = false;
2
5
  export function createReactivePropCell(props) {
3
6
  return { value: props, source: { subscribers: null } };
4
7
  }
8
+ export function batchReactivePropCellUpdates(run) {
9
+ propCellBatchDepth += 1;
10
+ try {
11
+ return batch(run);
12
+ }
13
+ finally {
14
+ propCellBatchDepth -= 1;
15
+ if (propCellBatchDepth === 0 && propCellBatchNeedsFlush) {
16
+ propCellBatchNeedsFlush = false;
17
+ flushQueuedComputations();
18
+ }
19
+ }
20
+ }
5
21
  const PROP_PROXY_HANDLER = {
6
22
  get(cell, property) {
7
- trackSource(cell.source);
8
- return cell.value[property];
23
+ trackSource(getReactivePropPropertySource(cell, property));
24
+ const value = Reflect.get(cell.value, property);
25
+ rememberReactivePropObjectSnapshot(cell, property, value);
26
+ return value;
9
27
  },
10
28
  has(cell, property) {
11
- trackSource(cell.source);
29
+ trackSource(getReactivePropPropertySource(cell, property));
12
30
  return property in cell.value;
13
31
  },
14
32
  ownKeys(cell) {
@@ -23,14 +41,100 @@ const PROP_PROXY_HANDLER = {
23
41
  export function createReactivePropProxy(cell) {
24
42
  return new Proxy(cell, PROP_PROXY_HANDLER);
25
43
  }
44
+ function getReactivePropPropertySource(cell, property) {
45
+ let propertySources = cell.propertySources;
46
+ if (propertySources === undefined) {
47
+ propertySources = new Map();
48
+ cell.propertySources = propertySources;
49
+ }
50
+ let source = propertySources.get(property);
51
+ if (source === undefined) {
52
+ source = { subscribers: null };
53
+ propertySources.set(property, source);
54
+ }
55
+ return source;
56
+ }
57
+ function rememberReactivePropObjectSnapshot(cell, property, value) {
58
+ if (!isObjectLike(value)) {
59
+ cell.propertySnapshots?.delete(property);
60
+ return;
61
+ }
62
+ let snapshots = cell.propertySnapshots;
63
+ if (snapshots === undefined) {
64
+ snapshots = new Map();
65
+ cell.propertySnapshots = snapshots;
66
+ }
67
+ snapshots.set(property, createShallowObjectSnapshot(value));
68
+ }
69
+ function createShallowObjectSnapshot(value) {
70
+ const entries = new Map();
71
+ for (const key of Reflect.ownKeys(value)) {
72
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
73
+ if (descriptor !== undefined && "value" in descriptor) {
74
+ entries.set(key, descriptor.value);
75
+ }
76
+ }
77
+ return { value, entries };
78
+ }
79
+ function isObjectLike(value) {
80
+ return (typeof value === "object" && value !== null) || typeof value === "function";
81
+ }
82
+ function hasShallowObjectSnapshotChanged(snapshot, nextValue) {
83
+ if (snapshot === undefined || snapshot.value !== nextValue || !isObjectLike(nextValue)) {
84
+ return false;
85
+ }
86
+ for (const key of Reflect.ownKeys(nextValue)) {
87
+ const descriptor = Reflect.getOwnPropertyDescriptor(nextValue, key);
88
+ if (descriptor !== undefined && "value" in descriptor) {
89
+ if (!snapshot.entries.has(key) || !Object.is(snapshot.entries.get(key), descriptor.value)) {
90
+ return true;
91
+ }
92
+ }
93
+ }
94
+ for (const key of snapshot.entries.keys()) {
95
+ const descriptor = Reflect.getOwnPropertyDescriptor(nextValue, key);
96
+ if (descriptor === undefined || !("value" in descriptor)) {
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+ function shouldNotifyReactivePropProperty(previous, next, property, snapshot) {
103
+ if ((property in previous) !== (property in next)) {
104
+ return true;
105
+ }
106
+ const previousValue = Reflect.get(previous, property);
107
+ const nextValue = Reflect.get(next, property);
108
+ return !Object.is(previousValue, nextValue) ||
109
+ hasShallowObjectSnapshotChanged(snapshot, nextValue);
110
+ }
26
111
  export function setReactivePropCell(cell, next) {
27
112
  if (Object.is(cell.value, next)) {
28
113
  return;
29
114
  }
115
+ const previous = cell.value;
116
+ const propertySources = cell.propertySources;
117
+ let notified = false;
30
118
  cell.value = next;
119
+ if (propertySources !== undefined) {
120
+ for (const [property, source] of propertySources) {
121
+ if (shouldNotifyReactivePropProperty(previous, next, property, cell.propertySnapshots?.get(property))) {
122
+ notifySubscribers(source);
123
+ notified = true;
124
+ }
125
+ }
126
+ }
31
127
  if (cell.source.subscribers !== null) {
32
128
  notifySubscribers(cell.source);
33
- flushQueuedComputations();
129
+ notified = true;
130
+ }
131
+ if (notified) {
132
+ if (propCellBatchDepth === 0) {
133
+ flushQueuedComputations();
134
+ }
135
+ else {
136
+ propCellBatchNeedsFlush = true;
137
+ }
34
138
  }
35
139
  }
36
140
  //# sourceMappingURL=reactive-prop-cell.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reactive-prop-cell.js","sourceRoot":"","sources":["../src/reactive-prop-cell.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,WAAW,GAEZ,MAAM,wCAAwC,CAAC;AAoBhD,MAAM,UAAU,sBAAsB,CAAC,KAA8B;IACnE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,kBAAkB,GAAmC;IACzD,GAAG,CAAC,IAAI,EAAE,QAAQ;QAChB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAkB,CAAC,CAAC;IACxC,CAAC;IACD,GAAG,CAAC,IAAI,EAAE,QAAQ;QAChB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,IAAI;QACV,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,wBAAwB,CAAC,IAAI,EAAE,QAAQ;QACrC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,OAAO,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAChE,CAAC;CACF,CAAC;AAEF,MAAM,UAAU,uBAAuB,CAAmB,IAAsB;IAC9E,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,kBAAkB,CAAiB,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,IAAsB,EACtB,IAA6B;IAE7B,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO;IACT,CAAC;IAED,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IAElB,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;QACrC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,uBAAuB,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC","sourcesContent":["import {\n flushQueuedComputations,\n notifySubscribers,\n trackSource,\n type Source,\n} from \"@reckona/mreact-reactive-core/internal\";\nimport type { ReactiveDomBlockResult } from \"./element.js\";\n\n// A stable reactive box over a component's incoming props, analogous to the\n// native keyed list's KeyedItemCell (packages/reactive-dom/src/bind-list.ts).\n// A reactive-dom-block reads its props through a proxy backed by this cell;\n// when the component re-renders with new props the reconciler swaps the cell's\n// value and notifies, so bound text/attributes update without re-running the\n// block's render closure or reconciling its subtree.\nexport interface ReactivePropCell {\n value: Record<string, unknown>;\n source: Source;\n}\n\n// What a reactive-dom-block fiber stores in stateNode: the committed node and\n// its dispose, plus the prop cell when the block bridges component props.\nexport interface ReactiveDomBlockState extends ReactiveDomBlockResult {\n propCell?: ReactivePropCell | undefined;\n}\n\nexport function createReactivePropCell(props: Record<string, unknown>): ReactivePropCell {\n return { value: props, source: { subscribers: null } };\n}\n\nconst PROP_PROXY_HANDLER: ProxyHandler<ReactivePropCell> = {\n get(cell, property) {\n trackSource(cell.source);\n return cell.value[property as string];\n },\n has(cell, property) {\n trackSource(cell.source);\n return property in cell.value;\n },\n ownKeys(cell) {\n trackSource(cell.source);\n return Reflect.ownKeys(cell.value);\n },\n getOwnPropertyDescriptor(cell, property) {\n trackSource(cell.source);\n return Reflect.getOwnPropertyDescriptor(cell.value, property);\n },\n};\n\nexport function createReactivePropProxy<P extends object>(cell: ReactivePropCell): P {\n return new Proxy(cell, PROP_PROXY_HANDLER) as unknown as P;\n}\n\nexport function setReactivePropCell(\n cell: ReactivePropCell,\n next: Record<string, unknown>,\n): void {\n if (Object.is(cell.value, next)) {\n return;\n }\n\n cell.value = next;\n\n if (cell.source.subscribers !== null) {\n notifySubscribers(cell.source);\n flushQueuedComputations();\n }\n}\n"]}
1
+ {"version":3,"file":"reactive-prop-cell.js","sourceRoot":"","sources":["../src/reactive-prop-cell.ts"],"names":[],"mappings":"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","sourcesContent":["import { batch } from \"@reckona/mreact-reactive-core\";\nimport {\n flushQueuedComputations,\n notifySubscribers,\n trackSource,\n type Source,\n} from \"@reckona/mreact-reactive-core/internal\";\nimport type { ReactiveDomBlockResult } from \"./element.js\";\n\n// A stable reactive box over a component's incoming props, analogous to the\n// native keyed list's KeyedItemCell (packages/reactive-dom/src/bind-list.ts).\n// A reactive-dom-block reads its props through a proxy backed by this cell;\n// when the component re-renders with new props the reconciler swaps the cell's\n// value and notifies, so bound text/attributes update without re-running the\n// block's render closure or reconciling its subtree.\nexport interface ReactivePropCell {\n value: Record<string, unknown>;\n source: Source;\n propertySources?: Map<PropertyKey, Source> | undefined;\n propertySnapshots?: Map<PropertyKey, ShallowObjectSnapshot> | undefined;\n}\n\n// What a reactive-dom-block fiber stores in stateNode: the committed node and\n// its dispose, plus the prop cell when the block bridges component props.\nexport interface ReactiveDomBlockState extends ReactiveDomBlockResult {\n propCell?: ReactivePropCell | undefined;\n}\n\nlet propCellBatchDepth = 0;\nlet propCellBatchNeedsFlush = false;\n\nexport function createReactivePropCell(props: Record<string, unknown>): ReactivePropCell {\n return { value: props, source: { subscribers: null } };\n}\n\nexport function batchReactivePropCellUpdates<T>(run: () => T): T {\n propCellBatchDepth += 1;\n try {\n return batch(run);\n } finally {\n propCellBatchDepth -= 1;\n if (propCellBatchDepth === 0 && propCellBatchNeedsFlush) {\n propCellBatchNeedsFlush = false;\n flushQueuedComputations();\n }\n }\n}\n\nconst PROP_PROXY_HANDLER: ProxyHandler<ReactivePropCell> = {\n get(cell, property) {\n trackSource(getReactivePropPropertySource(cell, property));\n const value = Reflect.get(cell.value, property);\n rememberReactivePropObjectSnapshot(cell, property, value);\n return value;\n },\n has(cell, property) {\n trackSource(getReactivePropPropertySource(cell, property));\n return property in cell.value;\n },\n ownKeys(cell) {\n trackSource(cell.source);\n return Reflect.ownKeys(cell.value);\n },\n getOwnPropertyDescriptor(cell, property) {\n trackSource(cell.source);\n return Reflect.getOwnPropertyDescriptor(cell.value, property);\n },\n};\n\nexport function createReactivePropProxy<P extends object>(cell: ReactivePropCell): P {\n return new Proxy(cell, PROP_PROXY_HANDLER) as unknown as P;\n}\n\nfunction getReactivePropPropertySource(cell: ReactivePropCell, property: PropertyKey): Source {\n let propertySources = cell.propertySources;\n\n if (propertySources === undefined) {\n propertySources = new Map();\n cell.propertySources = propertySources;\n }\n\n let source = propertySources.get(property);\n\n if (source === undefined) {\n source = { subscribers: null };\n propertySources.set(property, source);\n }\n\n return source;\n}\n\ninterface ShallowObjectSnapshot {\n value: object;\n entries: Map<PropertyKey, unknown>;\n}\n\nfunction rememberReactivePropObjectSnapshot(\n cell: ReactivePropCell,\n property: PropertyKey,\n value: unknown,\n): void {\n if (!isObjectLike(value)) {\n cell.propertySnapshots?.delete(property);\n return;\n }\n\n let snapshots = cell.propertySnapshots;\n if (snapshots === undefined) {\n snapshots = new Map();\n cell.propertySnapshots = snapshots;\n }\n\n snapshots.set(property, createShallowObjectSnapshot(value));\n}\n\nfunction createShallowObjectSnapshot(value: object): ShallowObjectSnapshot {\n const entries = new Map<PropertyKey, unknown>();\n for (const key of Reflect.ownKeys(value)) {\n const descriptor = Reflect.getOwnPropertyDescriptor(value, key);\n if (descriptor !== undefined && \"value\" in descriptor) {\n entries.set(key, descriptor.value);\n }\n }\n\n return { value, entries };\n}\n\nfunction isObjectLike(value: unknown): value is object {\n return (typeof value === \"object\" && value !== null) || typeof value === \"function\";\n}\n\nfunction hasShallowObjectSnapshotChanged(\n snapshot: ShallowObjectSnapshot | undefined,\n nextValue: unknown,\n): boolean {\n if (snapshot === undefined || snapshot.value !== nextValue || !isObjectLike(nextValue)) {\n return false;\n }\n\n for (const key of Reflect.ownKeys(nextValue)) {\n const descriptor = Reflect.getOwnPropertyDescriptor(nextValue, key);\n if (descriptor !== undefined && \"value\" in descriptor) {\n if (!snapshot.entries.has(key) || !Object.is(snapshot.entries.get(key), descriptor.value)) {\n return true;\n }\n }\n }\n\n for (const key of snapshot.entries.keys()) {\n const descriptor = Reflect.getOwnPropertyDescriptor(nextValue, key);\n if (descriptor === undefined || !(\"value\" in descriptor)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction shouldNotifyReactivePropProperty(\n previous: Record<string, unknown>,\n next: Record<string, unknown>,\n property: PropertyKey,\n snapshot: ShallowObjectSnapshot | undefined,\n): boolean {\n if ((property in previous) !== (property in next)) {\n return true;\n }\n\n const previousValue = Reflect.get(previous, property);\n const nextValue = Reflect.get(next, property);\n return !Object.is(previousValue, nextValue) ||\n hasShallowObjectSnapshotChanged(snapshot, nextValue);\n}\n\nexport function setReactivePropCell(\n cell: ReactivePropCell,\n next: Record<string, unknown>,\n): void {\n if (Object.is(cell.value, next)) {\n return;\n }\n\n const previous = cell.value;\n const propertySources = cell.propertySources;\n let notified = false;\n\n cell.value = next;\n\n if (propertySources !== undefined) {\n for (const [property, source] of propertySources) {\n if (\n shouldNotifyReactivePropProperty(\n previous,\n next,\n property,\n cell.propertySnapshots?.get(property),\n )\n ) {\n notifySubscribers(source);\n notified = true;\n }\n }\n }\n\n if (cell.source.subscribers !== null) {\n notifySubscribers(cell.source);\n notified = true;\n }\n\n if (notified) {\n if (propCellBatchDepth === 0) {\n flushQueuedComputations();\n } else {\n propCellBatchNeedsFlush = true;\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-compat",
3
- "version": "0.0.174",
3
+ "version": "0.0.175",
4
4
  "description": "React-compatible runtime implementation for mreact.",
5
5
  "keywords": [
6
6
  "compatibility",
@@ -69,7 +69,7 @@
69
69
  "access": "public"
70
70
  },
71
71
  "dependencies": {
72
- "@reckona/mreact-reactive-core": "0.0.174",
73
- "@reckona/mreact-shared": "0.0.174"
72
+ "@reckona/mreact-reactive-core": "0.0.175",
73
+ "@reckona/mreact-shared": "0.0.175"
74
74
  }
75
75
  }
package/src/element.ts CHANGED
@@ -211,6 +211,16 @@ export function createElementFromJsxConfig<P extends object>(
211
211
  ? String(keyArgument)
212
212
  : config?.key === undefined ? null : String(config.key);
213
213
  const ref = config?.ref ?? null;
214
+ if (canReuseJsxConfigAsComponentProps(normalizedType, config)) {
215
+ return {
216
+ $$typeof: REACT_COMPAT_ELEMENT_TYPE,
217
+ type: normalizedType as ElementType<P>,
218
+ key,
219
+ ref,
220
+ props: config as P & { children?: ReactCompatNode },
221
+ };
222
+ }
223
+
214
224
  const hasChildren = config !== null && config !== undefined && hasOwnProperty.call(config, "children");
215
225
  const children = config?.children;
216
226
  const copiedProps = copyElementProps(config, undefined, true);
@@ -237,6 +247,34 @@ export function createElementFromJsxConfig<P extends object>(
237
247
  };
238
248
  }
239
249
 
250
+ function canReuseJsxConfigAsComponentProps(
251
+ type: unknown,
252
+ config: object | null | undefined,
253
+ ): boolean {
254
+ if (
255
+ config === null ||
256
+ config === undefined ||
257
+ typeof type === "string" ||
258
+ hasDefaultProps(type)
259
+ ) {
260
+ return false;
261
+ }
262
+
263
+ for (const name in config) {
264
+ if (
265
+ hasOwnProperty.call(config, name) &&
266
+ (name === "key" ||
267
+ name === "ref" ||
268
+ name === "__self" ||
269
+ name === "__source")
270
+ ) {
271
+ return false;
272
+ }
273
+ }
274
+
275
+ return true;
276
+ }
277
+
240
278
  /** Returns true when a value is a React-compatible element record. */
241
279
  export function isReactCompatElement(
242
280
  value: unknown,
@@ -287,6 +325,7 @@ export interface MemoType<P = Record<string, unknown>> {
287
325
  $$typeof: typeof MEMO_TYPE;
288
326
  type: ElementType<P>;
289
327
  compare?: (previous: P, next: P) => boolean;
328
+ __mreactMemoCompareProps?: readonly string[];
290
329
  }
291
330
 
292
331
  /** Element type record produced by lazy. */
@@ -497,7 +536,7 @@ function applyDefaultProps(
497
536
  type: unknown,
498
537
  props: Record<string, unknown>,
499
538
  ): Record<string, unknown> {
500
- if (typeof type !== "function" && (typeof type !== "object" || type === null)) {
539
+ if (!canHaveDefaultProps(type)) {
501
540
  return props;
502
541
  }
503
542
 
@@ -517,6 +556,15 @@ function applyDefaultProps(
517
556
  return props;
518
557
  }
519
558
 
559
+ function hasDefaultProps(type: unknown): boolean {
560
+ return canHaveDefaultProps(type) &&
561
+ (type as { defaultProps?: Record<string, unknown> }).defaultProps !== undefined;
562
+ }
563
+
564
+ function canHaveDefaultProps(type: unknown): boolean {
565
+ return typeof type === "function" || (typeof type === "object" && type !== null);
566
+ }
567
+
520
568
  interface ReactReservedProps {
521
569
  key?: unknown;
522
570
  ref?: unknown;