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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ import {
ReduxActionErrorTypes,
} from "ee/constants/ReduxActionConstants";
import { set, keyBy, findIndex, unset } from "lodash";
import produce from "immer";
import { create } from "mutative";
import { klona } from "klona";
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 actionUpdateSearch = keyBy(action.payload, "id");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ import type {
import type { MetaState, WidgetMetaState } from ".";
import type { ReduxAction } from "actions/ReduxActionTypes";
import type { EvalMetaUpdates } from "ee/workers/common/DataTreeEvaluator/types";
import produce from "immer";
import { create } from "mutative";
import { set, unset } from "lodash";
import { klonaRegularWithTelemetry } from "utils/helpers";
@ -82,7 +82,7 @@ export function getNextMetaStateWithUpdates(
if (!evalMetaUpdates.length) return state;
// 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 }) => {
set(draftMetaState, [widgetId, ...metaPropertyPath], value);
});

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import type { ReduxAction } from "actions/ReduxActionTypes";
import produce from "immer";
import { create } from "mutative";
export const createReducer = (
// 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
return function reducer(state = initialState, action: ReduxAction<any>) {
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 {
return state;
}

View File

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

View File

@ -1,6 +1,6 @@
import type { WidgetEntity } from "ee/entities/DataTree/types";
import { applyChange } from "deep-diff";
import produce from "immer";
import { create } from "mutative";
import { klona } from "klona/full";
import { range } from "lodash";
import moment from "moment";
@ -110,7 +110,7 @@ const oldState: dataTreeWithWidget = {
describe("generateOptimisedUpdates", () => {
describe("regular diff", () => {
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;
});
const updates = generateOptimisedUpdates(oldState, newState, []);
@ -119,7 +119,7 @@ describe("generateOptimisedUpdates", () => {
expect(updates).toEqual([]);
});
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
draft.Table1.triggerRowSelection = true;
});
@ -131,7 +131,7 @@ describe("generateOptimisedUpdates", () => {
expect(updates).toEqual([]);
});
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;
});
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 ", () => {
const validationError =
"Some validation error" as unknown as EvaluationError[];
const newState = produce(oldState, (draft) => {
const newState = create(oldState, (draft) => {
draft.Table1.__evaluation__.errors.tableData = validationError;
});
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", () => {
const newState = produce(oldState, (draft) => {
const newState = create(oldState, (draft) => {
draft.Table1.tableData = largeDataSet;
});
const updates = generateOptimisedUpdates(oldState, newState, [
@ -179,10 +179,10 @@ describe("generateOptimisedUpdates", () => {
});
describe("undefined value updates 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"];
});
const newStateWithAnElementDeleted = produce(
const newStateWithAnElementDeleted = create(
statWithLargeCollection,
(draft) => {
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", () => {
const statWithLargeCollection = produce(oldState, (draft) => {
const statWithLargeCollection = create(oldState, (draft) => {
draft.Table1.tableData = ["a", "b"];
});
const newStateWithAnElementDeleted = produce(
const newStateWithAnElementDeleted = create(
statWithLargeCollection,
(draft) => {
draft.Table1.tableData = ["a", "e"];
@ -244,7 +244,7 @@ describe("generateOptimisedUpdates", () => {
expect(serialisedUpdates).toEqual(JSON.stringify(additionalUpdates));
});
it("should ignore undefined updates", () => {
const oldStateWithUndefinedValues = produce(oldState, (draft) => {
const oldStateWithUndefinedValues = create(oldState, (draft) => {
draft.Table1.pageSize = undefined;
});
@ -260,7 +260,7 @@ describe("generateOptimisedUpdates", () => {
expect(serialisedUpdates).toEqual(JSON.stringify(additionalUpdates));
});
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;
});
@ -281,7 +281,7 @@ describe("generateOptimisedUpdates", () => {
]);
});
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
draft.Table1.filteredTableData = draft.Table1;
});
@ -302,7 +302,7 @@ describe("generateOptimisedUpdates", () => {
const someEvalFn = (() => {}) as unknown as EvaluationError[];
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.__evaluation__.errors.someEvalFn = someEvalFn;
});
@ -320,7 +320,7 @@ describe("generateOptimisedUpdates", () => {
//should delete all function updates
expect(parsedUpdates).toEqual([]);
const parseAndApplyUpdatesToOldState = produce(oldState, (draft) => {
const parseAndApplyUpdatesToOldState = create(oldState, (draft) => {
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => {
@ -333,7 +333,7 @@ describe("generateOptimisedUpdates", () => {
});
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.__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
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => {
applyChange(draft, undefined, v);
});
});
const expectedState = produce(oldState, (draft) => {
const expectedState = create(oldState, (draft) => {
delete draft.Table1.pageSize;
delete draft.Table1.__evaluation__.errors.transientTableData;
});
@ -377,14 +377,14 @@ describe("generateOptimisedUpdates", () => {
expect(parseAndApplyUpdatesToOldState).toEqual(expectedState);
});
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
draft.Table1.pageSize = () => {};
draft.Table1.__evaluation__.errors.transientTableData =
// eslint-disable-next-line @typescript-eslint/no-empty-function
someEvalFn;
});
const newStateWithFnsTransformedToUndefined = produce(
const newStateWithFnsTransformedToUndefined = create(
oldState,
(draft) => {
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
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => {
applyChange(draft, undefined, v);
});
});
const expectedState = produce(oldState, (draft) => {
const expectedState = create(oldState, (draft) => {
delete draft.Table1.pageSize;
delete draft.Table1.__evaluation__.errors.transientTableData;
});
@ -443,7 +443,7 @@ describe("generateOptimisedUpdates", () => {
rhs: { someOtherKey: BigInt(3323232) },
},
];
const newStateWithBigInt = produce(oldState, (draft) => {
const newStateWithBigInt = create(oldState, (draft) => {
draft.Table1.pageSize = someBigInt;
});
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
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => {
applyChange(draft, undefined, v);
});
});
const expectedState = produce(oldState, (draft) => {
const expectedState = create(oldState, (draft) => {
draft.Table1.pageSize = "121221";
draft.Table1.someNewProp = { someOtherKey: "3323232" };
});
@ -488,7 +488,7 @@ describe("generateOptimisedUpdates", () => {
});
describe("serialise momement updates directly", () => {
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");
});
const { serialisedUpdates } = generateSerialisedUpdates(
@ -504,7 +504,7 @@ describe("generateOptimisedUpdates", () => {
});
test("should generate a regular update when it sees a valid moment object", () => {
const validMoment = moment();
const newState = produce(oldState, (draft) => {
const newState = create(oldState, (draft) => {
draft.Table1.pageSize = validMoment;
});
const { serialisedUpdates } = generateSerialisedUpdates(
@ -529,7 +529,7 @@ describe("generateOptimisedUpdates", () => {
const parsedUpdates =
parseUpdatesAndDeleteUndefinedUpdates(serialisedUpdates);
return produce(prevState, (draft) => {
return create(prevState, (draft) => {
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedUpdates.forEach((v: any) => {
@ -552,7 +552,7 @@ describe("generateOptimisedUpdates", () => {
}
//attaching a collection to some property in the workerState
workerStateWithCollection = produce(oldState, (draft) => {
workerStateWithCollection = create(oldState, (draft) => {
draft.Table1.pageSize = largeCollection;
});
//generate serialised diff updates
@ -569,7 +569,7 @@ describe("generateOptimisedUpdates", () => {
oldState,
);
const expectedMainThreadState = produce(oldState, (draft) => {
const expectedMainThreadState = create(oldState, (draft) => {
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 ", () => {
const someNewDate = "2023-12-07T19:05:11.930Z";
// updating a single value in the prev worker state
const updatedWorkerStateWithASingleValue = produce(
const updatedWorkerStateWithASingleValue = create(
klona(workerStateWithCollection),
(draft) => {
draft.Table1.pageSize[0].c = moment(someNewDate);
@ -613,7 +613,7 @@ describe("generateOptimisedUpdates", () => {
someNewDate,
);
const expectedMainThreadState = produce(
const expectedMainThreadState = create(
mainThreadStateWithCollection,
(draft) => {
draft.Table1.pageSize[0].c = JSON.parse(
@ -628,7 +628,7 @@ describe("generateOptimisedUpdates", () => {
//some garbage value
const someNewDate = "fdfdfd";
// updating a single value in the prev worker state
const updatedWorkerStateWithASingleValue = produce(
const updatedWorkerStateWithASingleValue = create(
klona(workerStateWithCollection),
(draft) => {
// 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
expect(updatedMainThreadState.Table1.pageSize[0].c).toEqual(null);
const expectedMainThreadState = produce(
const expectedMainThreadState = create(
mainThreadStateWithCollection,
(draft) => {
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 type { ConfigTree } from "entities/DataTree/dataTreeTypes";
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 { UpdateDataTreeMessageData } from "sagas/EvalWorkerActionSagas";
import DataTreeEvaluator from "workers/common/DataTreeEvaluator";
@ -259,7 +259,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should generate updates based on the unEvalUpdates", () => {
// TODO: Fix this the next time the file is edited
// 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.label = UPDATED_LABEL;
});
@ -311,7 +311,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should generate updates based on the evalOrder", () => {
// TODO: Fix this the next time the file is edited
// 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;
});
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 ", () => {
// TODO: Fix this the next time the file is edited
// 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) {
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", () => {
// TODO: Fix this the next time the file is edited
// 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) {
draft.Text1.text = UPDATED_LABEL;
}
@ -426,7 +426,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should add metaUpdates in the webworker's response", () => {
// TODO: Fix this the next time the file is edited
// 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) {
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", () => {
// TODO: Fix this the next time the file is edited
// 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) {
draft.Text1.text = UPDATED_LABEL;
}
@ -495,7 +495,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should add unEvalUpdates to the web worker response", () => {
// TODO: Fix this the next time the file is edited
// 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) {
draft.Text1.text = UPDATED_LABEL;
}
@ -533,7 +533,7 @@ describe("evaluateAndGenerateResponse", () => {
test("should ignore generating updates when unEvalUpdates is empty", () => {
// TODO: Fix this the next time the file is edited
// 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) {
draft.Text1.text = UPDATED_LABEL;
}

View File

@ -6,7 +6,7 @@ import {
getAllPathsBasedOnDiffPaths,
type DataTreeDiff,
} from "ee/workers/Evaluation/evaluationUtils";
import produce from "immer";
import { create } from "mutative";
describe("getOnlyAffectedJSObjects", () => {
const dataTree = {
@ -170,7 +170,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
expect(initialAllKeys).toEqual(updatedAllKeys);
});
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
delete draft.WidgetName.name;
});
@ -187,7 +187,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
// we have to make a copy since allKeys is mutable
{ ...initialAllKeys },
);
const deletedWidgetNameInAllKeys = produce(initialAllKeys, (draft) => {
const deletedWidgetNameInAllKeys = create(initialAllKeys, (draft) => {
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", () => {
const addedNewWidgetProperty = produce(initialTree, (draft) => {
const addedNewWidgetProperty = create(initialTree, (draft) => {
// new property is added to the widget
draft.WidgetName.widgetNewProperty = "newValue";
});
@ -213,7 +213,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
// we have to make a copy since allKeys is mutable
{ ...initialAllKeys },
);
const addedNewWidgetPropertyInAllKeys = produce(initialAllKeys, (draft) => {
const addedNewWidgetPropertyInAllKeys = create(initialAllKeys, (draft) => {
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", () => {
const addedNewWidgetProperty = produce(initialTree, (draft) => {
const addedNewWidgetProperty = create(initialTree, (draft) => {
//existing property within the widget is edited
draft.WidgetName.name = [{ a: 1 }];
});
@ -239,7 +239,7 @@ describe("getAllPathsBasedOnDiffPaths", () => {
// we have to make a copy since allKeys is mutable
{ ...initialAllKeys },
);
const addedACollectionInAllKeys = produce(initialAllKeys, (draft) => {
const addedACollectionInAllKeys = create(initialAllKeys, (draft) => {
draft["WidgetName.name[0]"] = true;
draft["WidgetName.name[0].a"] = true;
});

View File

@ -13338,6 +13338,7 @@ __metadata:
moment: 2.29.4
moment-timezone: ^0.5.35
msw: ^0.28.0
mutative: ^1.1.0
nanoid: ^2.0.4
node-forge: ^1.3.0
object-hash: ^3.0.0
@ -25747,6 +25748,13 @@ __metadata:
languageName: node
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":
version: 0.0.8
resolution: "mute-stream@npm:0.0.8"