Skip to content

Commit 3c5539a

Browse files
HsifnusJonathan Sun
and
Jonathan Sun
authored
fix: Short-circuit File and Date constructor access in isObject (#4413)
* fix: short-circuit File and Date constructor access in isObject if thing is missing required property * chore: update changelog with isObject short-circuiting fix --------- Co-authored-by: Jonathan Sun <[email protected]>
1 parent 4359bd1 commit 3c5539a

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ should change the heading of the (upcoming) version to include a major version b
2222

2323
- Fix default value population when switching between options in `MultiSchemaField` [#4375](https://github.com/rjsf-team/react-jsonschema-form/pull/4375). Fixes [#4367](https://github.com/rjsf-team/react-jsonschema-form/issues/4367)
2424

25+
## @rjsf/utils
26+
27+
- Short-circuit `File` and `Date` constructor access in isObject to optimize performance in scenarios where `globalThis` is a `Proxy` that incurs overhead for each class constructor access ([#4413](https://github.com/rjsf-team/react-jsonschema-form/pull/4413)). Fixes [#4409](https://github.com/rjsf-team/react-jsonschema-form/issues/4409)
28+
2529
## @rjsf/validator-ajv8
2630

2731
- Fixed issue where `ui:title` in anyOf/oneOf is not shown in error messages. Fixes [#4368](https://github.com/rjsf-team/react-jsonschema-form/issues/4368)

packages/utils/src/isObject.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
/** Determines whether a `thing` is an object for the purposes of RSJF. In this case, `thing` is an object if it has
1+
/** Determines whether a `thing` is an object for the purposes of RJSF. In this case, `thing` is an object if it has
22
* the type `object` but is NOT null, an array or a File.
33
*
44
* @param thing - The thing to check to see whether it is an object
55
* @returns - True if it is a non-null, non-array, non-File object
66
*/
77
export default function isObject(thing: any) {
8-
if (typeof File !== 'undefined' && thing instanceof File) {
8+
if (typeof thing !== 'object' || thing === null) {
99
return false;
1010
}
11-
if (typeof Date !== 'undefined' && thing instanceof Date) {
11+
// lastModified is guaranteed to be a number on a File instance
12+
// as per https://w3c.github.io/FileAPI/#dfn-lastModified
13+
if (typeof thing.lastModified === 'number' && typeof File !== 'undefined' && thing instanceof File) {
1214
return false;
1315
}
14-
return typeof thing === 'object' && thing !== null && !Array.isArray(thing);
16+
// getMonth is guaranteed to be a method on a Date instance
17+
// as per https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-date.prototype.getmonth
18+
if (typeof thing.getMonth === 'function' && typeof Date !== 'undefined' && thing instanceof Date) {
19+
return false;
20+
}
21+
return !Array.isArray(thing);
1522
}

packages/utils/test/isObject.test.ts

+34
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,38 @@ describe('isObject()', () => {
2626
expect(isObject(object)).toBe(true);
2727
});
2828
});
29+
describe('without accessing File and Date classes', () => {
30+
const NativeFile = File;
31+
const NativeDate = Date;
32+
33+
beforeEach(() => {
34+
Object.defineProperty(global, 'File', {
35+
get() {
36+
throw new Error('File should not have been accessed');
37+
},
38+
});
39+
Object.defineProperty(global, 'Date', {
40+
get() {
41+
throw new Error('Date should not have been accessed');
42+
},
43+
});
44+
});
45+
46+
afterEach(() => {
47+
Object.defineProperty(global, 'File', NativeFile);
48+
Object.defineProperty(global, 'Date', NativeDate);
49+
});
50+
51+
it('returns false when a non-object is provided', () => {
52+
NON_OBJECTS.forEach((nonObject: string | number | boolean | null | undefined) => {
53+
expect(isObject(nonObject)).toBe(false);
54+
});
55+
});
56+
57+
it('returns true when an object is provided', () => {
58+
OBJECTS.forEach((object: any) => {
59+
expect(isObject(object)).toBe(true);
60+
});
61+
});
62+
});
2963
});

0 commit comments

Comments
 (0)