feat(*): react 19 support update

This commit is contained in:
AAGaming
2025-10-15 00:04:31 -04:00
parent 25b4b60e34
commit 6e443c06d3
8 changed files with 171 additions and 92 deletions

5
global.d.ts vendored
View File

@@ -1,5 +1,10 @@
import type * as React from 'react';
import type * as ReactDOM from 'react-dom';
import type * as JSXRuntime from 'react/jsx-runtime';
declare global {
interface Window {
SP_REACT: typeof React;
SP_REACTDOM: typeof ReactDOM;
SP_JSX: typeof JSXRuntime;
}
}

View File

@@ -47,8 +47,8 @@
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@types/jest": "^29.5.14",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@types/react": "19.1.1",
"@types/react-dom": "19.1.1",
"@types/react-router": "5.1.20",
"commitizen": "^4.3.1",
"husky": "^9.1.7",

32
pnpm-lock.yaml generated
View File

@@ -27,11 +27,11 @@ importers:
specifier: ^29.5.14
version: 29.5.14
'@types/react':
specifier: 18.3.3
version: 18.3.3
specifier: 19.1.1
version: 19.1.1
'@types/react-dom':
specifier: 18.3.0
version: 18.3.0
specifier: 19.1.1
version: 19.1.1(@types/react@19.1.1)
'@types/react-router':
specifier: 5.1.20
version: 5.1.20
@@ -606,17 +606,16 @@ packages:
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
'@types/prop-types@15.7.15':
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
'@types/react-dom@18.3.0':
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
'@types/react-dom@19.1.1':
resolution: {integrity: sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==}
peerDependencies:
'@types/react': ^19.0.0
'@types/react-router@5.1.20':
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
'@types/react@18.3.3':
resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==}
'@types/react@19.1.1':
resolution: {integrity: sha512-ePapxDL7qrgqSF67s0h9m412d9DbXyC1n59O2st+9rjuuamWsZuD2w55rqY12CbzsZ7uVXb5Nw0gEp9Z8MMutQ==}
'@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
@@ -3622,20 +3621,17 @@ snapshots:
'@types/normalize-package-data@2.4.4': {}
'@types/prop-types@15.7.15': {}
'@types/react-dom@18.3.0':
'@types/react-dom@19.1.1(@types/react@19.1.1)':
dependencies:
'@types/react': 18.3.3
'@types/react': 19.1.1
'@types/react-router@5.1.20':
dependencies:
'@types/history': 4.7.11
'@types/react': 18.3.3
'@types/react': 19.1.1
'@types/react@18.3.3':
'@types/react@19.1.1':
dependencies:
'@types/prop-types': 15.7.15
csstype: 3.1.3
'@types/stack-utils@2.0.3': {}

View File

@@ -17,5 +17,5 @@ export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLe
const focusableRegex = createPropListRegex(["flow-children", "onActivate", "onCancel", "focusClassName", "focusWithinClassName"]);
export const Focusable = findModuleExport((e: Export) =>
e?.render?.toString && focusableRegex.test(e.render.toString())
(typeof e == 'function' && e?.toString && focusableRegex.test(e.toString())) || (e?.render?.toString && focusableRegex.test(e.render.toString()))
) as FC<FocusableProps & RefAttributes<HTMLDivElement>>;

View File

@@ -108,7 +108,7 @@ export type ReorderableListEntryProps<T> = {
reorderEntryFunc: CallableFunction;
reorderEnabled: boolean;
animate: boolean;
children: ReactElement | null;
children: ReactElement<any> | null;
};
function ReorderableItem<T>(props: ReorderableListEntryProps<T>) {

View File

@@ -69,71 +69,145 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
let renderHookStep = 0;
// Accessed two times, once directly before class instantiation, and again in some extra logic we don't need to worry about that we hanlde below just in case.
Object.defineProperty(component, "contextType", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get contexttype", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("contextType trace");
if (renderHookStep == 0) renderHookStep = 1;
else if (renderHookStep == 3) renderHookStep = 4;
return this._contextType;
},
set: function (value) {
this._contextType = value;
}
});
// Always accessed directly after contextType for the path we want to catch.
Object.defineProperty(component, "contextTypes", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get contexttypes", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("contextTypes trace");
if (renderHookStep == 1) {
renderHookStep = 2;
applyStubsIfNeeded();
};
return this._contextTypes;
},
set: function (value) {
this._contextTypes = value;
}
});
// Set directly after class is instantiated
Object.defineProperty(component.prototype, "updater", {
configurable: true,
get: function () {
return this._updater;
},
set: function (value) {
loggingEnabled && logger.debug("set updater", this, value, stubsApplied, renderHookStep);
loggingEnabled && console.trace("updater trace");
if (renderHookStep == 2) {
renderHookStep = 0;
removeStubsIfNeeded();
if (window.SP_REACTDOM.version.startsWith("19.")) {
// Accessed two times directly before class instantiation on path A and once on path B
Object.defineProperty(component, "contextType", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get contexttype", this, this._contextType, stubsApplied, renderHookStep);
loggingEnabled && console.trace("contextType trace");
if (renderHookStep == 0) {
renderHookStep = 1;
}
if (this._contextType == null) {
this._contextType = {};
}
if (!this._contextType.appliedCurrentValueHook) {
logger.debug("applied currentvalue hook");
this._contextType.appliedCurrentValueHook = true;
Object.defineProperty(this._contextType, "_currentValue", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get currentValue", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("currentValue trace");
if (renderHookStep == 1) {
renderHookStep = 2;
applyStubsIfNeeded();
}
return this.__currentValue;
},
set: function (value) {
return this.__currentValue = value;
}
});
}
return this._contextType;
},
set: function (value) {
this._contextType = value;
}
return this._updater = value;
}
});
});
// Prevents the second contextType+contextTypes access from leaving its hooks around
Object.defineProperty(component, "getDerivedStateFromProps", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get getDerivedStateFromProps", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("getDerivedStateFromProps trace");
if (renderHookStep == 2) {
renderHookStep = 0;
removeStubsIfNeeded();
// Set directly after class is instantiated
Object.defineProperty(component.prototype, "updater", {
configurable: true,
get: function () {
return this._updater;
},
set: function (value) {
loggingEnabled && logger.debug("set updater", this, value, stubsApplied, renderHookStep);
loggingEnabled && console.trace("updater trace");
if (renderHookStep == 1 || renderHookStep == 2) {
renderHookStep = 0;
removeStubsIfNeeded();
}
return this._updater = value;
}
return this._getDerivedStateFromProps;
},
set: function (value) {
this._getDerivedStateFromProps = value;
}
});
});
// Prevents the second contextType access from leaving its hooks around
Object.defineProperty(component, "getDerivedStateFromProps", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get getDerivedStateFromProps", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("getDerivedStateFromProps trace");
if (renderHookStep == 1|| renderHookStep == 2) {
renderHookStep = 0;
removeStubsIfNeeded();
}
return this._getDerivedStateFromProps;
},
set: function (value) {
this._getDerivedStateFromProps = value;
}
});
} else if (window.SP_REACTDOM.version.startsWith("18.")) {
// Accessed two times, once directly before class instantiation, and again in some extra logic we don't need to worry about that we hanlde below just in case.
Object.defineProperty(component, "contextType", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get contexttype", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("contextType trace");
if (renderHookStep == 0) renderHookStep = 1;
else if (renderHookStep == 3) renderHookStep = 4;
return this._contextType;
},
set: function (value) {
this._contextType = value;
}
});
// Always accessed directly after contextType for the path we want to catch.
Object.defineProperty(component, "contextTypes", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get contexttypes", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("contextTypes trace");
if (renderHookStep == 1) {
renderHookStep = 2;
applyStubsIfNeeded();
};
return this._contextTypes;
},
set: function (value) {
this._contextTypes = value;
}
});
// Set directly after class is instantiated
Object.defineProperty(component.prototype, "updater", {
configurable: true,
get: function () {
return this._updater;
},
set: function (value) {
loggingEnabled && logger.debug("set updater", this, value, stubsApplied, renderHookStep);
loggingEnabled && console.trace("updater trace");
if (renderHookStep == 2) {
renderHookStep = 0;
removeStubsIfNeeded();
}
return this._updater = value;
}
});
// Prevents the second contextType+contextTypes access from leaving its hooks around
Object.defineProperty(component, "getDerivedStateFromProps", {
configurable: true,
get: function () {
loggingEnabled && logger.debug("get getDerivedStateFromProps", this, stubsApplied, renderHookStep);
loggingEnabled && console.trace("getDerivedStateFromProps trace");
if (renderHookStep == 2) {
renderHookStep = 0;
removeStubsIfNeeded();
}
return this._getDerivedStateFromProps;
},
set: function (value) {
this._getDerivedStateFromProps = value;
}
});
}
return userComponent;
}

View File

@@ -1,4 +1,6 @@
import * as React from 'react';
import type * as React from 'react';
import type * as ReactDOM from 'react-dom';
import type * as JSXRuntime from 'react/jsx-runtime';
import { Ref, useState } from 'react';
// this shouldn't need to be redeclared but it does for some reason
@@ -6,6 +8,8 @@ import { Ref, useState } from 'react';
declare global {
interface Window {
SP_REACT: typeof React;
SP_REACTDOM: typeof ReactDOM;
SP_JSX: typeof JSXRuntime;
}
}
@@ -33,9 +37,11 @@ export function createPropListRegex(propList: string[], fromStart: boolean = tru
let oldHooks = {};
export let INTERNAL_HOOKS = (window.SP_REACT as any)?.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?.ReactCurrentDispatcher
.current || Object.values((window.SP_REACT as any)?.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE).find((p: any) => p?.useEffect);
export function applyHookStubs(customHooks: any = {}): any {
const hooks = (window.SP_REACT as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher
.current;
const hooks = INTERNAL_HOOKS;
// TODO: add more hooks
@@ -50,7 +56,7 @@ export function applyHookStubs(customHooks: any = {}): any {
};
hooks.useCallback = (cb: Function) => cb;
hooks.useContext = (cb: any) => cb._currentValue;
hooks.useContext = (cb: any) => cb?._currentValue;
hooks.useLayoutEffect = (_: Function) => {}; //cb();
hooks.useMemo = (cb: Function, _: any[]) => cb;
hooks.useEffect = (_: Function) => {}; //cb();
@@ -67,8 +73,7 @@ export function applyHookStubs(customHooks: any = {}): any {
}
export function removeHookStubs() {
const hooks = (window.SP_REACT as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher
.current;
const hooks = INTERNAL_HOOKS;
Object.assign(hooks, oldHooks);
oldHooks = {};
}

View File

@@ -3,8 +3,7 @@
"outDir": "dist",
"module": "ESNext",
"target": "ES2020",
"jsx": "react",
"jsxFactory": "window.SP_REACT.createElement",
"jsx": "react-jsx",
"declaration": true,
"moduleResolution": "node",
"noUnusedLocals": true,