chore: bypass immer for first evaluation, fixed cloneDeep issue and using mutative instead of immer (#38993)

## Description
- Using mutative instead of immer, this has reduced the main thread
scripting by about 1 second.
- Removed a cloneDeep during table onMount which saves about 70ms in
main thread scripting.
- Bypassed mutative when applying the first tree to reduce the overhead
associated to mutative.

Fixes #`Issue Number`  
_or_  
Fixes `Issue URL`
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.All"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/13164792224>
> Commit: 4cb821723d10198c9db70312a9604df5aa5f80c1
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13164792224&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Thu, 06 Feb 2025 04:21:41 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Dependency**
  - Integrated a new library to enhance overall state management.

- **Refactor**
- Updated state update mechanisms across interactive components and data
flows.
  - Improved table widget processing for more consistent behavior.

- **Chore**
  - Removed legacy development-only configuration settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Vemparala Surya Vamsi 2025-02-06 11:20:08 +05:30 committed by GitHub
parent 6604f36bc1
commit 2b9299e2d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 126 additions and 106 deletions

View File

@ -140,7 +140,6 @@
"fusioncharts": "^3.18.0", "fusioncharts": "^3.18.0",
"graphql": "^16.8.1", "graphql": "^16.8.1",
"history": "^4.10.1", "history": "^4.10.1",
"immer": "^9.0.6",
"interweave": "^12.7.2", "interweave": "^12.7.2",
"interweave-autolink": "^4.4.2", "interweave-autolink": "^4.4.2",
"js-regex-pl": "^1.0.1", "js-regex-pl": "^1.0.1",
@ -161,6 +160,7 @@
"mixpanel-browser": "^2.55.1", "mixpanel-browser": "^2.55.1",
"moment": "2.29.4", "moment": "2.29.4",
"moment-timezone": "^0.5.35", "moment-timezone": "^0.5.35",
"mutative": "^1.1.0",
"nanoid": "^2.0.4", "nanoid": "^2.0.4",
"node-forge": "^1.3.0", "node-forge": "^1.3.0",
"object-hash": "^3.0.0", "object-hash": "^3.0.0",
@ -331,6 +331,7 @@
"eslint-plugin-testing-library": "^6.2.0", "eslint-plugin-testing-library": "^6.2.0",
"factory.ts": "^0.5.1", "factory.ts": "^0.5.1",
"husky": "^8.0.0", "husky": "^8.0.0",
"immer": "^9.0.6",
"jest": "^29.6.1", "jest": "^29.6.1",
"jest-canvas-mock": "^2.3.1", "jest-canvas-mock": "^2.3.1",
"jest-environment-jsdom": "^29.6.1", "jest-environment-jsdom": "^29.6.1",

View File

@ -35,7 +35,7 @@ import {
import type { RegisteredWidgetFeatures } from "../../utils/WidgetFeatures"; import type { RegisteredWidgetFeatures } from "../../utils/WidgetFeatures";
import type { SetterConfig } from "entities/AppTheming"; import type { SetterConfig } from "entities/AppTheming";
import { freeze, memoize } from "./decorators"; import { freeze, memoize } from "./decorators";
import produce from "immer"; import { create } from "mutative";
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import type { import type {
CopiedWidgetData, CopiedWidgetData,
@ -418,7 +418,7 @@ class WidgetFactory {
if (dynamicProperties && dynamicProperties.length) { if (dynamicProperties && dynamicProperties.length) {
addPropertyConfigIds(dynamicProperties, false); addPropertyConfigIds(dynamicProperties, false);
section = produce(section, (draft) => { section = create(section, (draft) => {
draft.children = [...dynamicProperties, ...section.children]; draft.children = [...dynamicProperties, ...section.children];
}); });
} }

View File

@ -6,7 +6,7 @@ import type { Datasource } from "entities/Datasource";
import { isStoredDatasource } from "entities/Action"; import { isStoredDatasource } from "entities/Action";
import type { WidgetProps } from "widgets/BaseWidget"; import type { WidgetProps } from "widgets/BaseWidget";
import log from "loglevel"; import log from "loglevel";
import produce from "immer"; import { create } from "mutative";
import type { CanvasStructure } from "reducers/uiReducers/pageCanvasStructureReducer"; import type { CanvasStructure } from "reducers/uiReducers/pageCanvasStructureReducer";
import { getActions, getDatasources } from "ee/selectors/entitiesSelector"; import { getActions, getDatasources } from "ee/selectors/entitiesSelector";
import type { ActionData } from "ee/reducers/entityReducers/actionsReducer"; import type { ActionData } from "ee/reducers/entityReducers/actionsReducer";
@ -184,7 +184,7 @@ export const useActions = (searchKeyword?: string) => {
return useMemo(() => { return useMemo(() => {
if (searchKeyword) { if (searchKeyword) {
const start = performance.now(); const start = performance.now();
const filteredActions = produce(actions, (draft) => { const filteredActions = create(actions, (draft) => {
for (const [key, value] of Object.entries(draft)) { for (const [key, value] of Object.entries(draft)) {
if (pageIds.includes(key)) { if (pageIds.includes(key)) {
draft[key] = value; draft[key] = value;
@ -225,7 +225,7 @@ export const useWidgets = (searchKeyword?: string) => {
return useMemo(() => { return useMemo(() => {
if (searchKeyword && pageCanvasStructures) { if (searchKeyword && pageCanvasStructures) {
const start = performance.now(); const start = performance.now();
const filteredDSLs = produce(pageCanvasStructures, (draft) => { const filteredDSLs = create(pageCanvasStructures, (draft) => {
for (const [key, value] of Object.entries(draft)) { for (const [key, value] of Object.entries(draft)) {
if (pageIds.includes(key)) { if (pageIds.includes(key)) {
draft[key] = value; draft[key] = value;
@ -256,7 +256,7 @@ export const usePageIds = (searchKeyword?: string) => {
return useMemo(() => { return useMemo(() => {
if (searchKeyword) { if (searchKeyword) {
const filteredPages = produce(pages, (draft) => { const filteredPages = create(pages, (draft) => {
draft.forEach((page, index) => { draft.forEach((page, index) => {
const searchMatches = const searchMatches =
page.pageName.toLowerCase().indexOf(searchKeyword.toLowerCase()) > page.pageName.toLowerCase().indexOf(searchKeyword.toLowerCase()) >

View File

@ -6,7 +6,7 @@ import {
ReduxActionErrorTypes, ReduxActionErrorTypes,
} from "ee/constants/ReduxActionConstants"; } from "ee/constants/ReduxActionConstants";
import { set, keyBy, findIndex, unset } from "lodash"; import { set, keyBy, findIndex, unset } from "lodash";
import produce from "immer"; import { create } from "mutative";
import { klona } from "klona"; import { klona } from "klona";
export const initialState: JSCollectionDataState = []; export const initialState: JSCollectionDataState = [];
@ -400,7 +400,7 @@ export const handlers = {
}> }>
>, >,
) => { ) => {
return produce(state, (draft) => { return create(state, (draft) => {
const CollectionUpdateSearch = keyBy(action.payload, "collectionId"); const CollectionUpdateSearch = keyBy(action.payload, "collectionId");
const actionUpdateSearch = keyBy(action.payload, "id"); const actionUpdateSearch = keyBy(action.payload, "id");

View File

@ -22,7 +22,7 @@ import {
defaultNavigationSetting, defaultNavigationSetting,
defaultThemeSetting, defaultThemeSetting,
} from "constants/AppConstants"; } from "constants/AppConstants";
import produce from "immer"; import { create } from "mutative";
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import type { ApplicationPayload } from "entities/Application"; import type { ApplicationPayload } from "entities/Application";
import { gitConnectSuccess, type GitConnectSuccessPayload } from "git"; import { gitConnectSuccess, type GitConnectSuccessPayload } from "git";
@ -530,7 +530,7 @@ export const handlers = {
state: ApplicationsReduxState, state: ApplicationsReduxState,
action: ReduxAction<NavigationSetting["logoAssetId"]>, action: ReduxAction<NavigationSetting["logoAssetId"]>,
) => { ) => {
return produce(state, (draftState: ApplicationsReduxState) => { return create(state, (draftState: ApplicationsReduxState) => {
draftState.isUploadingNavigationLogo = false; draftState.isUploadingNavigationLogo = false;
if ( if (

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import produce from "immer"; import { create } from "mutative";
import { noop, set } from "lodash"; import { noop, set } from "lodash";
import { CommonControls } from "./CommonControls"; import { CommonControls } from "./CommonControls";
@ -179,7 +179,7 @@ function WidgetQueryGeneratorForm(props: Props) {
setPristine(false); setPristine(false);
setConfig( setConfig(
produce(config, (draftConfig) => { create(config, (draftConfig) => {
if ( if (
property === "datasource" || property === "datasource" ||
(typeof property === "object" && (typeof property === "object" &&

View File

@ -3,7 +3,7 @@ import type { ControlData, ControlProps } from "./BaseControl";
import BaseControl from "./BaseControl"; import BaseControl from "./BaseControl";
import type { ToggleGroupOption } from "@appsmith/ads"; import type { ToggleGroupOption } from "@appsmith/ads";
import { ToggleButtonGroup } from "@appsmith/ads"; import { ToggleButtonGroup } from "@appsmith/ads";
import produce from "immer"; import { create } from "mutative";
import type { DSEventDetail } from "utils/AppsmithUtils"; import type { DSEventDetail } from "utils/AppsmithUtils";
import { import {
DSEventTypes, DSEventTypes,
@ -62,7 +62,7 @@ class ButtonTabControl extends BaseControl<ButtonTabControlProps> {
isUpdatedViaKeyboard, isUpdatedViaKeyboard,
); );
} else { } else {
const updatedValues: string[] = produce(values, (draft: string[]) => { const updatedValues: string[] = create(values, (draft: string[]) => {
draft.push(value); draft.push(value);
}); });

View File

@ -23,15 +23,12 @@ import "./assets/styles/index.css";
import "./polyfills"; import "./polyfills";
import GlobalStyles from "globalStyles"; import GlobalStyles from "globalStyles";
// enable autofreeze only in development // enable autofreeze only in development
import { setAutoFreeze } from "immer";
import AppErrorBoundary from "./AppErrorBoundry"; import AppErrorBoundary from "./AppErrorBoundry";
import log from "loglevel"; import log from "loglevel";
import { FaroErrorBoundary } from "@grafana/faro-react"; import { FaroErrorBoundary } from "@grafana/faro-react";
import { isTracingEnabled } from "instrumentation/utils"; import { isTracingEnabled } from "instrumentation/utils";
const shouldAutoFreeze = process.env.NODE_ENV === "development";
setAutoFreeze(shouldAutoFreeze);
runSagaMiddleware(); runSagaMiddleware();
appInitializer(); appInitializer();

View File

@ -13,7 +13,7 @@ import type {
import { ToastMessageType } from "entities/Datasource"; import { ToastMessageType } from "entities/Datasource";
import { TEMP_DATASOURCE_ID } from "constants/Datasource"; import { TEMP_DATASOURCE_ID } from "constants/Datasource";
import type { DropdownOption } from "@appsmith/ads-old"; import type { DropdownOption } from "@appsmith/ads-old";
import produce from "immer"; import { create } from "mutative";
import { assign } from "lodash"; import { assign } from "lodash";
export interface DatasourceDataState { export interface DatasourceDataState {
@ -466,7 +466,7 @@ const datasourceReducer = createReducer(initialState, {
state: DatasourceDataState, state: DatasourceDataState,
action: ReduxAction<Datasource>, action: ReduxAction<Datasource>,
): DatasourceDataState => { ): DatasourceDataState => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.loading = false; draftState.loading = false;
draftState.list.forEach((datasource) => { draftState.list.forEach((datasource) => {
if (datasource.id === action.payload.id) { if (datasource.id === action.payload.id) {
@ -656,7 +656,7 @@ const datasourceReducer = createReducer(initialState, {
[ReduxActionTypes.FETCH_GSHEET_SPREADSHEETS]: ( [ReduxActionTypes.FETCH_GSHEET_SPREADSHEETS]: (
state: DatasourceDataState, state: DatasourceDataState,
) => { ) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.isFetchingSpreadsheets = true; draftState.gsheetStructure.isFetchingSpreadsheets = true;
}); });
}, },
@ -664,7 +664,7 @@ const datasourceReducer = createReducer(initialState, {
state: DatasourceDataState, state: DatasourceDataState,
action: ReduxAction<{ id: string; data: DropdownOption[] }>, action: ReduxAction<{ id: string; data: DropdownOption[] }>,
) => { ) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.spreadsheets[action.payload.id] = { draftState.gsheetStructure.spreadsheets[action.payload.id] = {
value: action.payload.data, value: action.payload.data,
}; };
@ -676,7 +676,7 @@ const datasourceReducer = createReducer(initialState, {
state: DatasourceDataState, state: DatasourceDataState,
action: ReduxAction<{ id: string; error: string }>, action: ReduxAction<{ id: string; error: string }>,
) => { ) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.spreadsheets[action.payload.id] = { draftState.gsheetStructure.spreadsheets[action.payload.id] = {
error: action.payload.error, error: action.payload.error,
}; };
@ -685,7 +685,7 @@ const datasourceReducer = createReducer(initialState, {
}); });
}, },
[ReduxActionTypes.FETCH_GSHEET_SHEETS]: (state: DatasourceDataState) => { [ReduxActionTypes.FETCH_GSHEET_SHEETS]: (state: DatasourceDataState) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.isFetchingSheets = true; draftState.gsheetStructure.isFetchingSheets = true;
}); });
}, },
@ -693,7 +693,7 @@ const datasourceReducer = createReducer(initialState, {
state: DatasourceDataState, state: DatasourceDataState,
action: ReduxAction<{ id: string; data: DropdownOption[] }>, action: ReduxAction<{ id: string; data: DropdownOption[] }>,
) => { ) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.sheets[action.payload.id] = { draftState.gsheetStructure.sheets[action.payload.id] = {
value: action.payload.data, value: action.payload.data,
}; };
@ -704,7 +704,7 @@ const datasourceReducer = createReducer(initialState, {
state: DatasourceDataState, state: DatasourceDataState,
action: ReduxAction<{ id: string; error: string }>, action: ReduxAction<{ id: string; error: string }>,
) => { ) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.sheets[action.payload.id] = { draftState.gsheetStructure.sheets[action.payload.id] = {
error: action.payload.error, error: action.payload.error,
}; };
@ -712,7 +712,7 @@ const datasourceReducer = createReducer(initialState, {
}); });
}, },
[ReduxActionTypes.FETCH_GSHEET_COLUMNS]: (state: DatasourceDataState) => { [ReduxActionTypes.FETCH_GSHEET_COLUMNS]: (state: DatasourceDataState) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.isFetchingColumns = true; draftState.gsheetStructure.isFetchingColumns = true;
}); });
}, },
@ -720,7 +720,7 @@ const datasourceReducer = createReducer(initialState, {
state: DatasourceDataState, state: DatasourceDataState,
action: ReduxAction<{ id: string; data: DropdownOption[] }>, action: ReduxAction<{ id: string; data: DropdownOption[] }>,
) => { ) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.columns[action.payload.id] = { draftState.gsheetStructure.columns[action.payload.id] = {
value: action.payload.data, value: action.payload.data,
}; };
@ -731,7 +731,7 @@ const datasourceReducer = createReducer(initialState, {
state: DatasourceDataState, state: DatasourceDataState,
action: ReduxAction<{ id: string; error: string }>, action: ReduxAction<{ id: string; error: string }>,
) => { ) => {
return produce(state, (draftState) => { return create(state, (draftState) => {
draftState.gsheetStructure.columns[action.payload.id] = { draftState.gsheetStructure.columns[action.payload.id] = {
error: action.payload.error, error: action.payload.error,
}; };

View File

@ -11,7 +11,7 @@ import {
ReduxActionTypes, ReduxActionTypes,
WidgetReduxActionTypes, WidgetReduxActionTypes,
} from "ee/constants/ReduxActionConstants"; } from "ee/constants/ReduxActionConstants";
import produce from "immer"; import { create } from "mutative";
import type { EvalMetaUpdates } from "ee/workers/common/DataTreeEvaluator/types"; import type { EvalMetaUpdates } from "ee/workers/common/DataTreeEvaluator/types";
import { import {
getMetaWidgetResetObj, getMetaWidgetResetObj,
@ -38,7 +38,7 @@ export const metaReducer = createReducer(initialState, {
state: MetaState, state: MetaState,
action: ReduxAction<UpdateWidgetMetaPropertyPayload>, action: ReduxAction<UpdateWidgetMetaPropertyPayload>,
) => { ) => {
const nextState = produce(state, (draftMetaState) => { const nextState = create(state, (draftMetaState) => {
set( set(
draftMetaState, draftMetaState,
`${action.payload.widgetId}.${action.payload.propertyName}`, `${action.payload.widgetId}.${action.payload.propertyName}`,
@ -54,7 +54,7 @@ export const metaReducer = createReducer(initialState, {
state: MetaState, state: MetaState,
action: ReduxAction<BatchUpdateWidgetMetaPropertyPayload>, action: ReduxAction<BatchUpdateWidgetMetaPropertyPayload>,
) => { ) => {
const nextState = produce(state, (draftMetaState) => { const nextState = create(state, (draftMetaState) => {
const { batchMetaUpdates } = action.payload; const { batchMetaUpdates } = action.payload;
batchMetaUpdates.forEach(({ propertyName, propertyValue, widgetId }) => { batchMetaUpdates.forEach(({ propertyName, propertyValue, widgetId }) => {
@ -70,7 +70,7 @@ export const metaReducer = createReducer(initialState, {
state: MetaState, state: MetaState,
action: ReduxAction<UpdateWidgetMetaPropertyPayload>, action: ReduxAction<UpdateWidgetMetaPropertyPayload>,
) => { ) => {
const nextState = produce(state, (draftMetaState) => { const nextState = create(state, (draftMetaState) => {
set( set(
draftMetaState, draftMetaState,
`${action.payload.widgetId}.${action.payload.propertyName}`, `${action.payload.widgetId}.${action.payload.propertyName}`,

View File

@ -6,7 +6,7 @@ import type {
import type { MetaState, WidgetMetaState } from "."; import type { MetaState, WidgetMetaState } from ".";
import type { ReduxAction } from "actions/ReduxActionTypes"; import type { ReduxAction } from "actions/ReduxActionTypes";
import type { EvalMetaUpdates } from "ee/workers/common/DataTreeEvaluator/types"; import type { EvalMetaUpdates } from "ee/workers/common/DataTreeEvaluator/types";
import produce from "immer"; import { create } from "mutative";
import { set, unset } from "lodash"; import { set, unset } from "lodash";
import { klonaRegularWithTelemetry } from "utils/helpers"; import { klonaRegularWithTelemetry } from "utils/helpers";
@ -82,7 +82,7 @@ export function getNextMetaStateWithUpdates(
if (!evalMetaUpdates.length) return state; if (!evalMetaUpdates.length) return state;
// if metaObject is updated in dataTree we also update meta values, to keep meta state in sync. // if metaObject is updated in dataTree we also update meta values, to keep meta state in sync.
const newMetaState = produce(state, (draftMetaState) => { const newMetaState = create(state, (draftMetaState) => {
evalMetaUpdates.forEach(({ metaPropertyPath, value, widgetId }) => { evalMetaUpdates.forEach(({ metaPropertyPath, value, widgetId }) => {
set(draftMetaState, [widgetId, ...metaPropertyPath], value); set(draftMetaState, [widgetId, ...metaPropertyPath], value);
}); });

View File

@ -1,10 +1,9 @@
import type { ReduxAction } from "actions/ReduxActionTypes"; import type { ReduxAction } from "actions/ReduxActionTypes";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import { applyChange } from "deep-diff"; import { applyChange, type Diff } from "deep-diff";
import type { DataTree } from "entities/DataTree/dataTreeTypes"; import type { DataTree } from "entities/DataTree/dataTreeTypes";
import { createImmerReducer } from "utils/ReducerUtils"; import { createImmerReducer } from "utils/ReducerUtils";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import type { DiffWithNewTreeState } from "workers/Evaluation/helpers";
export type EvaluatedTreeState = DataTree; export type EvaluatedTreeState = DataTree;
@ -15,7 +14,7 @@ const evaluatedTreeReducer = createImmerReducer(initialState, {
state: EvaluatedTreeState, state: EvaluatedTreeState,
action: ReduxAction<{ action: ReduxAction<{
dataTree: DataTree; dataTree: DataTree;
updates: DiffWithNewTreeState[]; updates: Diff<DataTree, DataTree>[];
removedPaths: [string]; removedPaths: [string];
}>, }>,
) => { ) => {
@ -27,15 +26,11 @@ const evaluatedTreeReducer = createImmerReducer(initialState, {
for (const update of updates) { for (const update of updates) {
try { try {
if (update.kind === "newTree") { if (!update.path || update.path.length === 0) {
return update.rhs; continue;
} else {
if (!update.path || update.path.length === 0) {
continue;
}
applyChange(state, undefined, update);
} }
applyChange(state, undefined, update);
} catch (e) { } catch (e) {
Sentry.captureException(e, { Sentry.captureException(e, {
extra: { extra: {

View File

@ -20,7 +20,7 @@ import {
} from "constants/WidgetConstants"; } from "constants/WidgetConstants";
import { toast } from "@appsmith/ads"; import { toast } from "@appsmith/ads";
import type { DataTree } from "entities/DataTree/dataTreeTypes"; import type { DataTree } from "entities/DataTree/dataTreeTypes";
import produce from "immer"; import { create } from "mutative";
import { klona as clone } from "klona/full"; import { klona as clone } from "klona/full";
import { getWidgetMinMaxDimensionsInPixel } from "layoutSystems/autolayout/utils/flexWidgetUtils"; import { getWidgetMinMaxDimensionsInPixel } from "layoutSystems/autolayout/utils/flexWidgetUtils";
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants"; import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
@ -112,7 +112,7 @@ function* getChildWidgetProps(
// if (props) props.children = []; // if (props) props.children = [];
if (props) { if (props) {
props = produce(props, (draft: WidgetProps) => { props = create(props, (draft) => {
if (!draft.children || !Array.isArray(draft.children)) { if (!draft.children || !Array.isArray(draft.children)) {
draft.children = []; draft.children = [];
} }

View File

@ -1,5 +1,5 @@
import type { ReduxAction } from "actions/ReduxActionTypes"; import type { ReduxAction } from "actions/ReduxActionTypes";
import produce from "immer"; import { create } from "mutative";
export const createReducer = ( export const createReducer = (
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
@ -32,7 +32,19 @@ export const createImmerReducer = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
return function reducer(state = initialState, action: ReduxAction<any>) { return function reducer(state = initialState, action: ReduxAction<any>) {
if (handlers.hasOwnProperty(action.type)) { if (handlers.hasOwnProperty(action.type)) {
return produce(handlers[action.type])(state, action); if (action?.payload?.updates) {
const updates = action?.payload?.updates;
for (const update of updates) {
if (update.kind === "newTree") {
return update.rhs;
}
}
}
const fn = handlers[action.type];
return create(state, (draft) => fn(draft, action));
} else { } else {
return state; return state;
} }

View File

@ -3,7 +3,6 @@ import log from "loglevel";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import _, { import _, {
cloneDeep,
filter, filter,
isArray, isArray,
isEmpty, isEmpty,
@ -142,6 +141,7 @@ import IconSVG from "../icon.svg";
import ThumbnailSVG from "../thumbnail.svg"; import ThumbnailSVG from "../thumbnail.svg";
import { klonaRegularWithTelemetry } from "utils/helpers"; import { klonaRegularWithTelemetry } from "utils/helpers";
import HTMLCell from "../component/cellComponents/HTMLCell"; import HTMLCell from "../component/cellComponents/HTMLCell";
import { objectKeys } from "@appsmith/utils";
const ReactTableComponent = lazy(async () => const ReactTableComponent = lazy(async () =>
retryPromise(async () => import("../component")), retryPromise(async () => import("../component")),
@ -928,16 +928,23 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
* @rahulbarwal Remove this once we remove the feature flag * @rahulbarwal Remove this once we remove the feature flag
*/ */
if (!TableWidgetV2.getFeatureFlag(HTML_COLUMN_TYPE_ENABLED)) { if (!TableWidgetV2.getFeatureFlag(HTML_COLUMN_TYPE_ENABLED)) {
const updatedPrimaryColumns = cloneDeep(this.props.primaryColumns);
let hasHTMLColumns = false; let hasHTMLColumns = false;
const { primaryColumns } = this.props;
const updatedPrimaryColumns = objectKeys(primaryColumns).reduce(
(acc, widgetId) => {
const column = primaryColumns[widgetId];
Object.values(updatedPrimaryColumns).forEach(
(column: ColumnProperties) => {
if (column.columnType === ColumnTypes.HTML) { if (column.columnType === ColumnTypes.HTML) {
column.columnType = ColumnTypes.TEXT; acc[widgetId] = { ...column, columnType: ColumnTypes.TEXT };
hasHTMLColumns = true; hasHTMLColumns = true;
} else {
acc[widgetId] = column;
} }
return acc;
}, },
{} as Record<string, ColumnProperties>,
); );
if (hasHTMLColumns) { if (hasHTMLColumns) {

View File

@ -1,6 +1,6 @@
import type { WidgetEntity } from "ee/entities/DataTree/types"; import type { WidgetEntity } from "ee/entities/DataTree/types";
import { applyChange } from "deep-diff"; import { applyChange } from "deep-diff";
import produce from "immer"; import { create } from "mutative";
import { klona } from "klona/full"; import { klona } from "klona/full";
import { range } from "lodash"; import { range } from "lodash";
import moment from "moment"; import moment from "moment";
@ -110,7 +110,7 @@ const oldState: dataTreeWithWidget = {
describe("generateOptimisedUpdates", () => { describe("generateOptimisedUpdates", () => {
describe("regular diff", () => { describe("regular diff", () => {
test("should not generate any diff when the constrainedDiffPaths is empty", () => { test("should not generate any diff when the constrainedDiffPaths is empty", () => {
const newState = produce(oldState, (draft) => { const newState = create(oldState, (draft) => {
draft.Table1.pageSize = 17; draft.Table1.pageSize = 17;
}); });
const updates = generateOptimisedUpdates(oldState, newState, []); const updates = generateOptimisedUpdates(oldState, newState, []);
@ -119,7 +119,7 @@ describe("generateOptimisedUpdates", () => {
expect(updates).toEqual([]); expect(updates).toEqual([]);
}); });
test("should not generate any diff when the constrainedDiffPaths nodes are the same ", () => { test("should not generate any diff when the constrainedDiffPaths nodes are the same ", () => {
const newState = produce(oldState, (draft) => { const newState = create(oldState, (draft) => {
//making an unrelated change //making an unrelated change
draft.Table1.triggerRowSelection = true; draft.Table1.triggerRowSelection = true;
}); });
@ -131,7 +131,7 @@ describe("generateOptimisedUpdates", () => {
expect(updates).toEqual([]); expect(updates).toEqual([]);
}); });
test("should generate regular diff updates when a simple property changes in the widget property segment", () => { test("should generate regular diff updates when a simple property changes in the widget property segment", () => {
const newState = produce(oldState, (draft) => { const newState = create(oldState, (draft) => {
draft.Table1.pageSize = 17; draft.Table1.pageSize = 17;
}); });
const updates = generateOptimisedUpdates(oldState, newState, [ const updates = generateOptimisedUpdates(oldState, newState, [
@ -145,7 +145,7 @@ describe("generateOptimisedUpdates", () => {
test("should generate regular diff updates when a simple property changes in the __evaluation__ segment ", () => { test("should generate regular diff updates when a simple property changes in the __evaluation__ segment ", () => {
const validationError = const validationError =
"Some validation error" as unknown as EvaluationError[]; "Some validation error" as unknown as EvaluationError[];
const newState = produce(oldState, (draft) => { const newState = create(oldState, (draft) => {
draft.Table1.__evaluation__.errors.tableData = validationError; draft.Table1.__evaluation__.errors.tableData = validationError;
}); });
const updates = generateOptimisedUpdates(oldState, newState, [ const updates = generateOptimisedUpdates(oldState, newState, [
@ -162,7 +162,7 @@ describe("generateOptimisedUpdates", () => {
]); ]);
}); });
test("should generate a replace collection patch when the size of the collection exceeds 100 instead of generating granular updates", () => { test("should generate a replace collection patch when the size of the collection exceeds 100 instead of generating granular updates", () => {
const newState = produce(oldState, (draft) => { const newState = create(oldState, (draft) => {
draft.Table1.tableData = largeDataSet; draft.Table1.tableData = largeDataSet;
}); });
const updates = generateOptimisedUpdates(oldState, newState, [ const updates = generateOptimisedUpdates(oldState, newState, [
@ -179,10 +179,10 @@ describe("generateOptimisedUpdates", () => {
}); });
describe("undefined value updates in a collection", () => { describe("undefined value updates in a collection", () => {
test("should generate replace patch when a single node is set to undefined in a collection", () => { test("should generate replace patch when a single node is set to undefined in a collection", () => {
const statWithLargeCollection = produce(oldState, (draft) => { const statWithLargeCollection = create(oldState, (draft) => {
draft.Table1.tableData = ["a", "b"]; draft.Table1.tableData = ["a", "b"];
}); });
const newStateWithAnElementDeleted = produce( const newStateWithAnElementDeleted = create(
statWithLargeCollection, statWithLargeCollection,
(draft) => { (draft) => {
draft.Table1.tableData = ["a", undefined]; draft.Table1.tableData = ["a", undefined];
@ -204,10 +204,10 @@ describe("generateOptimisedUpdates", () => {
]); ]);
}); });
test("should generate generate regular diff updates for non undefined updates in a collection", () => { test("should generate generate regular diff updates for non undefined updates in a collection", () => {
const statWithLargeCollection = produce(oldState, (draft) => { const statWithLargeCollection = create(oldState, (draft) => {
draft.Table1.tableData = ["a", "b"]; draft.Table1.tableData = ["a", "b"];
}); });
const newStateWithAnElementDeleted = produce( const newStateWithAnElementDeleted = create(
statWithLargeCollection, statWithLargeCollection,
(draft) => { (draft) => {
draft.Table1.tableData = ["a", "e"]; draft.Table1.tableData = ["a", "e"];
@ -244,7 +244,7 @@ describe("generateOptimisedUpdates", () => {
expect(serialisedUpdates).toEqual(JSON.stringify(additionalUpdates)); expect(serialisedUpdates).toEqual(JSON.stringify(additionalUpdates));
}); });
it("should ignore undefined updates", () => { it("should ignore undefined updates", () => {
const oldStateWithUndefinedValues = produce(oldState, (draft) => { const oldStateWithUndefinedValues = create(oldState, (draft) => {
draft.Table1.pageSize = undefined; draft.Table1.pageSize = undefined;
}); });
@ -260,7 +260,7 @@ describe("generateOptimisedUpdates", () => {
expect(serialisedUpdates).toEqual(JSON.stringify(additionalUpdates)); expect(serialisedUpdates).toEqual(JSON.stringify(additionalUpdates));
}); });
it("should generate a delete patch when a property is transformed to undefined", () => { it("should generate a delete patch when a property is transformed to undefined", () => {
const oldStateWithUndefinedValues = produce(oldState, (draft) => { const oldStateWithUndefinedValues = create(oldState, (draft) => {
draft.Table1.pageSize = undefined; draft.Table1.pageSize = undefined;
}); });
@ -281,7 +281,7 @@ describe("generateOptimisedUpdates", () => {
]); ]);
}); });
it("should generate an error when there is a serialisation error", () => { it("should generate an error when there is a serialisation error", () => {
const oldStateWithUndefinedValues = produce(oldState, (draft) => { const oldStateWithUndefinedValues = create(oldState, (draft) => {
//generate a cyclical object //generate a cyclical object
draft.Table1.filteredTableData = draft.Table1; draft.Table1.filteredTableData = draft.Table1;
}); });
@ -302,7 +302,7 @@ describe("generateOptimisedUpdates", () => {
const someEvalFn = (() => {}) as unknown as EvaluationError[]; const someEvalFn = (() => {}) as unknown as EvaluationError[];
it("should clean out new function properties added to the generated state", () => { it("should clean out new function properties added to the generated state", () => {
const newStateWithSomeFnProperty = produce(oldState, (draft) => { const newStateWithSomeFnProperty = create(oldState, (draft) => {
draft.Table1.someFn = () => {}; draft.Table1.someFn = () => {};
draft.Table1.__evaluation__.errors.someEvalFn = someEvalFn; draft.Table1.__evaluation__.errors.someEvalFn = someEvalFn;
}); });
@ -320,7 +320,7 @@ describe("generateOptimisedUpdates", () => {
//should delete all function updates //should delete all function updates
expect(parsedUpdates).toEqual([]); expect(parsedUpdates).toEqual([]);
const parseAndApplyUpdatesToOldState = produce(oldState, (draft) => { const parseAndApplyUpdatesToOldState = create(oldState, (draft) => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => { parsedUpdates.forEach((v: any) => {
@ -333,7 +333,7 @@ describe("generateOptimisedUpdates", () => {
}); });
it("should delete properties which get updated to a function", () => { it("should delete properties which get updated to a function", () => {
const newStateWithSomeFnProperty = produce(oldState, (draft) => { const newStateWithSomeFnProperty = create(oldState, (draft) => {
draft.Table1.pageSize = () => {}; draft.Table1.pageSize = () => {};
draft.Table1.__evaluation__.errors.transientTableData = someEvalFn; draft.Table1.__evaluation__.errors.transientTableData = someEvalFn;
}); });
@ -362,14 +362,14 @@ describe("generateOptimisedUpdates", () => {
}, },
]); ]);
const parseAndApplyUpdatesToOldState = produce(oldState, (draft) => { const parseAndApplyUpdatesToOldState = create(oldState, (draft) => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => { parsedUpdates.forEach((v: any) => {
applyChange(draft, undefined, v); applyChange(draft, undefined, v);
}); });
}); });
const expectedState = produce(oldState, (draft) => { const expectedState = create(oldState, (draft) => {
delete draft.Table1.pageSize; delete draft.Table1.pageSize;
delete draft.Table1.__evaluation__.errors.transientTableData; delete draft.Table1.__evaluation__.errors.transientTableData;
}); });
@ -377,14 +377,14 @@ describe("generateOptimisedUpdates", () => {
expect(parseAndApplyUpdatesToOldState).toEqual(expectedState); expect(parseAndApplyUpdatesToOldState).toEqual(expectedState);
}); });
it("should delete function properties which get updated to undefined", () => { it("should delete function properties which get updated to undefined", () => {
const oldStateWithSomeFnProperty = produce(oldState, (draft) => { const oldStateWithSomeFnProperty = create(oldState, (draft) => {
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
draft.Table1.pageSize = () => {}; draft.Table1.pageSize = () => {};
draft.Table1.__evaluation__.errors.transientTableData = draft.Table1.__evaluation__.errors.transientTableData =
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
someEvalFn; someEvalFn;
}); });
const newStateWithFnsTransformedToUndefined = produce( const newStateWithFnsTransformedToUndefined = create(
oldState, oldState,
(draft) => { (draft) => {
draft.Table1.pageSize = undefined; draft.Table1.pageSize = undefined;
@ -417,14 +417,14 @@ describe("generateOptimisedUpdates", () => {
}, },
]); ]);
const parseAndApplyUpdatesToOldState = produce(oldState, (draft) => { const parseAndApplyUpdatesToOldState = create(oldState, (draft) => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => { parsedUpdates.forEach((v: any) => {
applyChange(draft, undefined, v); applyChange(draft, undefined, v);
}); });
}); });
const expectedState = produce(oldState, (draft) => { const expectedState = create(oldState, (draft) => {
delete draft.Table1.pageSize; delete draft.Table1.pageSize;
delete draft.Table1.__evaluation__.errors.transientTableData; delete draft.Table1.__evaluation__.errors.transientTableData;
}); });
@ -443,7 +443,7 @@ describe("generateOptimisedUpdates", () => {
rhs: { someOtherKey: BigInt(3323232) }, rhs: { someOtherKey: BigInt(3323232) },
}, },
]; ];
const newStateWithBigInt = produce(oldState, (draft) => { const newStateWithBigInt = create(oldState, (draft) => {
draft.Table1.pageSize = someBigInt; draft.Table1.pageSize = someBigInt;
}); });
const { serialisedUpdates } = generateSerialisedUpdates( const { serialisedUpdates } = generateSerialisedUpdates(
@ -472,14 +472,14 @@ describe("generateOptimisedUpdates", () => {
}, },
]); ]);
const parseAndApplyUpdatesToOldState = produce(oldState, (draft) => { const parseAndApplyUpdatesToOldState = create(oldState, (draft) => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => { parsedUpdates.forEach((v: any) => {
applyChange(draft, undefined, v); applyChange(draft, undefined, v);
}); });
}); });
const expectedState = produce(oldState, (draft) => { const expectedState = create(oldState, (draft) => {
draft.Table1.pageSize = "121221"; draft.Table1.pageSize = "121221";
draft.Table1.someNewProp = { someOtherKey: "3323232" }; draft.Table1.someNewProp = { someOtherKey: "3323232" };
}); });
@ -488,7 +488,7 @@ describe("generateOptimisedUpdates", () => {
}); });
describe("serialise momement updates directly", () => { describe("serialise momement updates directly", () => {
test("should generate a null update when it sees an invalid moment object", () => { test("should generate a null update when it sees an invalid moment object", () => {
const newState = produce(oldState, (draft) => { const newState = create(oldState, (draft) => {
draft.Table1.pageSize = moment("invalid value"); draft.Table1.pageSize = moment("invalid value");
}); });
const { serialisedUpdates } = generateSerialisedUpdates( const { serialisedUpdates } = generateSerialisedUpdates(
@ -504,7 +504,7 @@ describe("generateOptimisedUpdates", () => {
}); });
test("should generate a regular update when it sees a valid moment object", () => { test("should generate a regular update when it sees a valid moment object", () => {
const validMoment = moment(); const validMoment = moment();
const newState = produce(oldState, (draft) => { const newState = create(oldState, (draft) => {
draft.Table1.pageSize = validMoment; draft.Table1.pageSize = validMoment;
}); });
const { serialisedUpdates } = generateSerialisedUpdates( const { serialisedUpdates } = generateSerialisedUpdates(
@ -529,7 +529,7 @@ describe("generateOptimisedUpdates", () => {
const parsedUpdates = const parsedUpdates =
parseUpdatesAndDeleteUndefinedUpdates(serialisedUpdates); parseUpdatesAndDeleteUndefinedUpdates(serialisedUpdates);
return produce(prevState, (draft) => { return create(prevState, (draft) => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => { parsedUpdates.forEach((v: any) => {
@ -552,7 +552,7 @@ describe("generateOptimisedUpdates", () => {
} }
//attaching a collection to some property in the workerState //attaching a collection to some property in the workerState
workerStateWithCollection = produce(oldState, (draft) => { workerStateWithCollection = create(oldState, (draft) => {
draft.Table1.pageSize = largeCollection; draft.Table1.pageSize = largeCollection;
}); });
//generate serialised diff updates //generate serialised diff updates
@ -569,7 +569,7 @@ describe("generateOptimisedUpdates", () => {
oldState, oldState,
); );
const expectedMainThreadState = produce(oldState, (draft) => { const expectedMainThreadState = create(oldState, (draft) => {
draft.Table1.pageSize = JSON.parse(JSON.stringify(largeCollection)); draft.Table1.pageSize = JSON.parse(JSON.stringify(largeCollection));
}); });
@ -583,7 +583,7 @@ describe("generateOptimisedUpdates", () => {
test("update in a single moment value in a collection should always be serialised ", () => { test("update in a single moment value in a collection should always be serialised ", () => {
const someNewDate = "2023-12-07T19:05:11.930Z"; const someNewDate = "2023-12-07T19:05:11.930Z";
// updating a single value in the prev worker state // updating a single value in the prev worker state
const updatedWorkerStateWithASingleValue = produce( const updatedWorkerStateWithASingleValue = create(
klona(workerStateWithCollection), klona(workerStateWithCollection),
(draft) => { (draft) => {
draft.Table1.pageSize[0].c = moment(someNewDate); draft.Table1.pageSize[0].c = moment(someNewDate);
@ -613,7 +613,7 @@ describe("generateOptimisedUpdates", () => {
someNewDate, someNewDate,
); );
const expectedMainThreadState = produce( const expectedMainThreadState = create(
mainThreadStateWithCollection, mainThreadStateWithCollection,
(draft) => { (draft) => {
draft.Table1.pageSize[0].c = JSON.parse( draft.Table1.pageSize[0].c = JSON.parse(
@ -628,7 +628,7 @@ describe("generateOptimisedUpdates", () => {
//some garbage value //some garbage value
const someNewDate = "fdfdfd"; const someNewDate = "fdfdfd";
// updating a single value in the prev worker state // updating a single value in the prev worker state
const updatedWorkerStateWithASingleValue = produce( const updatedWorkerStateWithASingleValue = create(
klona(workerStateWithCollection), klona(workerStateWithCollection),
(draft) => { (draft) => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
@ -658,7 +658,7 @@ describe("generateOptimisedUpdates", () => {
// check if the main thread state has the updated invalid value which should be null // check if the main thread state has the updated invalid value which should be null
expect(updatedMainThreadState.Table1.pageSize[0].c).toEqual(null); expect(updatedMainThreadState.Table1.pageSize[0].c).toEqual(null);
const expectedMainThreadState = produce( const expectedMainThreadState = create(
mainThreadStateWithCollection, mainThreadStateWithCollection,
(draft) => { (draft) => {
draft.Table1.pageSize[0].c = JSON.parse( draft.Table1.pageSize[0].c = JSON.parse(

View File

@ -4,7 +4,7 @@ import { RenderModes } from "constants/WidgetConstants";
import { ENTITY_TYPE } from "ee/entities/DataTree/types"; import { ENTITY_TYPE } from "ee/entities/DataTree/types";
import type { ConfigTree } from "entities/DataTree/dataTreeTypes"; import type { ConfigTree } from "entities/DataTree/dataTreeTypes";
import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget"; import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget";
import produce from "immer"; import { create } from "mutative";
import type { WidgetEntity } from "plugins/Linting/lib/entity/WidgetEntity"; import type { WidgetEntity } from "plugins/Linting/lib/entity/WidgetEntity";
import type { UpdateDataTreeMessageData } from "sagas/EvalWorkerActionSagas"; import type { UpdateDataTreeMessageData } from "sagas/EvalWorkerActionSagas";
import DataTreeEvaluator from "workers/common/DataTreeEvaluator"; import DataTreeEvaluator from "workers/common/DataTreeEvaluator";
@ -259,7 +259,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should generate updates based on the unEvalUpdates", () => { test("should generate updates based on the unEvalUpdates", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
draft.Text1.label = UPDATED_LABEL; draft.Text1.label = UPDATED_LABEL;
}); });
@ -311,7 +311,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should generate updates based on the evalOrder", () => { test("should generate updates based on the evalOrder", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
}); });
const updateTreeResponse = evaluator.setupUpdateTree( const updateTreeResponse = evaluator.setupUpdateTree(
@ -346,7 +346,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should generate the correct updates to be sent to the main thread's state when the value tied to a binding changes ", () => { test("should generate the correct updates to be sent to the main thread's state when the value tied to a binding changes ", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
if (draft.Text1?.text) { if (draft.Text1?.text) {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
} }
@ -386,7 +386,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should merge additional updates to the dataTree as well as push the updates back to the main thread's state when unEvalUpdates is ignored", () => { test("should merge additional updates to the dataTree as well as push the updates back to the main thread's state when unEvalUpdates is ignored", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
if (draft.Text1?.text) { if (draft.Text1?.text) {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
} }
@ -426,7 +426,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should add metaUpdates in the webworker's response", () => { test("should add metaUpdates in the webworker's response", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
if (draft.Text1?.text) { if (draft.Text1?.text) {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
} }
@ -456,7 +456,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should sanitise metaUpdates in the webworker's response and strip out non serialisable properties", () => { test("should sanitise metaUpdates in the webworker's response and strip out non serialisable properties", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
if (draft.Text1?.text) { if (draft.Text1?.text) {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
} }
@ -495,7 +495,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should add unEvalUpdates to the web worker response", () => { test("should add unEvalUpdates to the web worker response", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
if (draft.Text1?.text) { if (draft.Text1?.text) {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
} }
@ -533,7 +533,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should ignore generating updates when unEvalUpdates is empty", () => { test("should ignore generating updates when unEvalUpdates is empty", () => {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const updatedLabelUnevalTree = produce(unEvalTree, (draft: any) => { const updatedLabelUnevalTree = create(unEvalTree, (draft: any) => {
if (draft.Text1?.text) { if (draft.Text1?.text) {
draft.Text1.text = UPDATED_LABEL; draft.Text1.text = UPDATED_LABEL;
} }

View File

@ -6,7 +6,7 @@ import {
getAllPathsBasedOnDiffPaths, getAllPathsBasedOnDiffPaths,
type DataTreeDiff, type DataTreeDiff,
} from "ee/workers/Evaluation/evaluationUtils"; } from "ee/workers/Evaluation/evaluationUtils";
import produce from "immer"; import { create } from "mutative";
describe("getOnlyAffectedJSObjects", () => { describe("getOnlyAffectedJSObjects", () => {
const dataTree = { const dataTree = {
@ -170,7 +170,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
expect(initialAllKeys).toEqual(updatedAllKeys); expect(initialAllKeys).toEqual(updatedAllKeys);
}); });
test("should delete the correct paths within allKeys when a node within a widget is deleted", () => { test("should delete the correct paths within allKeys when a node within a widget is deleted", () => {
const deletedWidgetName = produce(initialTree, (draft) => { const deletedWidgetName = create(initialTree, (draft) => {
// a property within the widget is deleted // a property within the widget is deleted
delete draft.WidgetName.name; delete draft.WidgetName.name;
}); });
@ -187,7 +187,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
// we have to make a copy since allKeys is mutable // we have to make a copy since allKeys is mutable
{ ...initialAllKeys }, { ...initialAllKeys },
); );
const deletedWidgetNameInAllKeys = produce(initialAllKeys, (draft) => { const deletedWidgetNameInAllKeys = create(initialAllKeys, (draft) => {
delete draft["WidgetName.name"]; delete draft["WidgetName.name"];
}); });
@ -195,7 +195,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
}); });
test("should add the correct paths to the allKeys when a node within a widget is added", () => { test("should add the correct paths to the allKeys when a node within a widget is added", () => {
const addedNewWidgetProperty = produce(initialTree, (draft) => { const addedNewWidgetProperty = create(initialTree, (draft) => {
// new property is added to the widget // new property is added to the widget
draft.WidgetName.widgetNewProperty = "newValue"; draft.WidgetName.widgetNewProperty = "newValue";
}); });
@ -213,7 +213,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
// we have to make a copy since allKeys is mutable // we have to make a copy since allKeys is mutable
{ ...initialAllKeys }, { ...initialAllKeys },
); );
const addedNewWidgetPropertyInAllKeys = produce(initialAllKeys, (draft) => { const addedNewWidgetPropertyInAllKeys = create(initialAllKeys, (draft) => {
draft["WidgetName.widgetNewProperty"] = true; draft["WidgetName.widgetNewProperty"] = true;
}); });
@ -221,7 +221,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
}); });
test("should generate the correct paths when the value changes form a simple primitive to a collection, this is for EDIT diffs", () => { test("should generate the correct paths when the value changes form a simple primitive to a collection, this is for EDIT diffs", () => {
const addedNewWidgetProperty = produce(initialTree, (draft) => { const addedNewWidgetProperty = create(initialTree, (draft) => {
//existing property within the widget is edited //existing property within the widget is edited
draft.WidgetName.name = [{ a: 1 }]; draft.WidgetName.name = [{ a: 1 }];
}); });
@ -239,7 +239,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
// we have to make a copy since allKeys is mutable // we have to make a copy since allKeys is mutable
{ ...initialAllKeys }, { ...initialAllKeys },
); );
const addedACollectionInAllKeys = produce(initialAllKeys, (draft) => { const addedACollectionInAllKeys = create(initialAllKeys, (draft) => {
draft["WidgetName.name[0]"] = true; draft["WidgetName.name[0]"] = true;
draft["WidgetName.name[0].a"] = true; draft["WidgetName.name[0].a"] = true;
}); });

View File

@ -13338,6 +13338,7 @@ __metadata:
moment: 2.29.4 moment: 2.29.4
moment-timezone: ^0.5.35 moment-timezone: ^0.5.35
msw: ^0.28.0 msw: ^0.28.0
mutative: ^1.1.0
nanoid: ^2.0.4 nanoid: ^2.0.4
node-forge: ^1.3.0 node-forge: ^1.3.0
object-hash: ^3.0.0 object-hash: ^3.0.0
@ -25747,6 +25748,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mutative@npm:^1.1.0":
version: 1.1.0
resolution: "mutative@npm:1.1.0"
checksum: 5eaf505a97c713ecb45bdadfe905045692414ff6f08e35d9cad0e6e27d9005a06e9696350c23bff70d39488973841384ed01d800d67f8b2957c7a1daf3d24d3c
languageName: node
linkType: hard
"mute-stream@npm:0.0.8": "mute-stream@npm:0.0.8":
version: 0.0.8 version: 0.0.8
resolution: "mute-stream@npm:0.0.8" resolution: "mute-stream@npm:0.0.8"