Skip to content

Commit

Permalink
Merge pull request #28981 from storybookjs/norbert/lodash-es
Browse files Browse the repository at this point in the history
Core: Replace `lodash` with `es-toolkit`
  • Loading branch information
ndelangen authored Sep 26, 2024
2 parents be1b97e + 5a30b77 commit f529029
Show file tree
Hide file tree
Showing 52 changed files with 168 additions and 122 deletions.
2 changes: 1 addition & 1 deletion code/.storybook/manager.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { addons } from 'storybook/internal/manager-api';

import startCase from 'lodash/startCase.js';
import { startCase } from 'es-toolkit/compat';

addons.setConfig({
sidebar: {
Expand Down
1 change: 0 additions & 1 deletion code/addons/a11y/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.10",
"@testing-library/react": "^14.0.0",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-resize-detector": "^7.1.2",
Expand Down
1 change: 0 additions & 1 deletion code/addons/controls/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
"dependencies": {
"@storybook/global": "^5.0.0",
"dequal": "^2.0.2",
"lodash": "^4.17.21",
"ts-dedent": "^2.0.0"
},
"devDependencies": {
Expand Down
3 changes: 1 addition & 2 deletions code/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,6 @@
"@types/ejs": "^3.1.1",
"@types/find-cache-dir": "^5.0.0",
"@types/js-yaml": "^4.0.5",
"@types/lodash": "^4.14.167",
"@types/node": "^22.0.0",
"@types/npmlog": "^7.0.0",
"@types/picomatch": "^2.3.0",
Expand Down Expand Up @@ -361,6 +360,7 @@
"diff": "^5.2.0",
"downshift": "^9.0.4",
"ejs": "^3.1.10",
"es-toolkit": "^1.21.0",
"esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0",
"esbuild-plugin-alias": "^0.2.1",
"execa": "^8.0.1",
Expand All @@ -380,7 +380,6 @@
"js-yaml": "^4.1.0",
"lazy-universal-dotenv": "^4.0.0",
"leven": "^4.0.0",
"lodash": "^4.17.21",
"markdown-to-jsx": "^7.4.5",
"memfs": "^4.11.1",
"memoizerific": "^1.11.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
export const getComponentVariableName = async (name: string) => {
const camelCase = await import('camelcase');

const camelCased = camelCase.default(name.replace(/^[^a-zA-Z_$]*/, ''), { pascalCase: true });
const sanitized = camelCased.replace(/[^a-zA-Z_$]+/, '');
return sanitized;
Expand Down
7 changes: 3 additions & 4 deletions code/core/src/core-server/utils/stories-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { normalizeStoriesEntry } from '@storybook/core/common';

import { STORY_INDEX_INVALIDATED } from '@storybook/core/core-events';

import { debounce } from 'es-toolkit/compat';
import type { Request, Response, Router } from 'express';
import debounce from 'lodash/debounce.js';
import Watchpack from 'watchpack';

import { csfIndexer } from '../presets/common-preset';
Expand All @@ -17,7 +17,7 @@ import type { ServerChannel } from './get-server-channel';
import { DEBOUNCE, useStoriesJson } from './stories-json';

vi.mock('watchpack');
vi.mock('lodash/debounce');
vi.mock('es-toolkit/compat');
vi.mock('@storybook/core/node-logger');

const workingDir = join(__dirname, '__mockdata__');
Expand Down Expand Up @@ -471,8 +471,7 @@ describe('useStoriesJson', () => {

it('debounces invalidation events', async () => {
vi.mocked(debounce).mockImplementation(
// @ts-expect-error it doesn't think default exists
(await vi.importActual<typeof import('lodash/debounce.js')>('lodash/debounce.js')).default
(await vi.importActual<typeof import('es-toolkit/compat')>('es-toolkit/compat')).debounce
);

const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
Expand Down
2 changes: 1 addition & 1 deletion code/core/src/core-server/utils/stories-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import type { NormalizedStoriesSpecifier, StoryIndex } from '@storybook/core/typ

import { STORY_INDEX_INVALIDATED } from '@storybook/core/core-events';

import { debounce } from 'es-toolkit/compat';
import type { Request, Response, Router } from 'express';
import debounce from 'lodash/debounce.js';

import type { StoryIndexGenerator } from './StoryIndexGenerator';
import type { ServerChannel } from './get-server-channel';
Expand Down
2 changes: 1 addition & 1 deletion code/core/src/docs-tools/argTypes/convert/convert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { describe, expect, it } from 'vitest';

import { transformSync } from '@storybook/core/babel';

import mapValues from 'lodash/mapValues.js';
import { mapValues } from 'es-toolkit';
import requireFromString from 'require-from-string';

import { normalizeNewlines } from '../utils';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { SBType } from '@storybook/core/types';

import mapValues from 'lodash/mapValues.js';
import { mapValues } from 'es-toolkit';

import { parseLiteral } from '../utils';
import type { PTType } from './types';
Expand Down
60 changes: 55 additions & 5 deletions code/core/src/manager-api/lib/merge.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { logger } from '@storybook/core/client-logger';

import isEqual from 'lodash/isEqual.js';
import mergeWith from 'lodash/mergeWith.js';
import { isEqual, mergeWith, omitBy, pick } from 'es-toolkit';

export default <TObj = any>(a: TObj, b: Partial<TObj>) =>
mergeWith({}, a, b, (objValue: TObj, srcValue: Partial<TObj>) => {
export default <TObj = any>(a: TObj, ...b: Partial<TObj>[]): TObj => {
// start with empty object
let target = {};

// merge object a unto target
target = mergeWith({}, a, (objValue: TObj, srcValue: Partial<TObj>) => {
if (Array.isArray(srcValue) && Array.isArray(objValue)) {
srcValue.forEach((s) => {
const existing = objValue.find((o) => o === s || isEqual(o, s));
Expand All @@ -19,5 +22,52 @@ export default <TObj = any>(a: TObj, b: Partial<TObj>) =>
logger.log(['the types mismatch, picking', objValue]);
return objValue;
}
return undefined;
});

for (const obj of b) {
// merge object b unto target
target = mergeWith(target, obj, (objValue: TObj, srcValue: Partial<TObj>) => {
if (Array.isArray(srcValue) && Array.isArray(objValue)) {
srcValue.forEach((s) => {
const existing = objValue.find((o) => o === s || isEqual(o, s));
if (!existing) {
objValue.push(s);
}
});

return objValue;
}
if (Array.isArray(objValue)) {
logger.log(['the types mismatch, picking', objValue]);
return objValue;
}
});
}

return target as TObj;
};

export const noArrayMerge = <TObj = any>(a: TObj, ...b: Partial<TObj>[]): TObj => {
// start with empty object
let target = {};

// merge object a unto target
target = mergeWith({}, a, (objValue: TObj, srcValue: Partial<TObj>) => {
// Treat arrays as scalars:
if (Array.isArray(srcValue)) {
return srcValue;
}
});

for (const obj of b) {
// merge object b unto target
target = mergeWith(target, obj, (objValue: TObj, srcValue: Partial<TObj>) => {
// Treat arrays as scalars:
if (Array.isArray(srcValue)) {
return srcValue;
}
});
}

return target as TObj;
};
7 changes: 3 additions & 4 deletions code/core/src/manager-api/lib/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import type {
} from '@storybook/core/types';
import { sanitize } from '@storybook/csf';

import countBy from 'lodash/countBy.js';
import mapValues from 'lodash/mapValues.js';
import { countBy, mapValues } from 'es-toolkit';
import memoize from 'memoizerific';
import { dedent } from 'ts-dedent';

Expand All @@ -41,7 +40,7 @@ export const denormalizeStoryParameters = ({
kindParameters[storyData.kind],
storyData.parameters as unknown as Parameters
),
}));
})) as SetStoriesStoryData;
};

export const transformSetStoriesStoryDataToStoriesHash = (
Expand Down Expand Up @@ -112,7 +111,7 @@ export const transformStoryIndexV2toV3 = (index: StoryIndexV2): StoryIndexV3 =>
};

export const transformStoryIndexV3toV4 = (index: StoryIndexV3): API_PreparedStoryIndex => {
const countByTitle = countBy(Object.values(index.stories), 'title');
const countByTitle = countBy(Object.values(index.stories), (item) => item.title);
return {
v: 4,
entries: Object.values(index.stories).reduce(
Expand Down
20 changes: 9 additions & 11 deletions code/core/src/manager-api/modules/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { global } from '@storybook/global';

import { SET_CONFIG } from '@storybook/core/core-events';

import { dequal as deepEqual } from 'dequal';
import pick from 'lodash/pick.js';
import { isEqual as deepEqual, pick, toMerged } from 'es-toolkit';

import merge from '../lib/merge';
import type { ModuleFn } from '../lib/types';
Expand Down Expand Up @@ -319,14 +318,13 @@ export const init: ModuleFn<SubAPI, SubState> = ({ store, provider, singleStory
return {
...defaultLayoutState,
layout: {
...defaultLayoutState.layout,
...pick(options, Object.keys(defaultLayoutState.layout)),
...toMerged(
defaultLayoutState.layout,
pick(options, Object.keys(defaultLayoutState.layout))
),
...(singleStory && { navSize: 0 }),
},
ui: {
...defaultLayoutState.ui,
...pick(options, Object.keys(defaultLayoutState.ui)),
},
ui: toMerged(defaultLayoutState.ui, pick(options, Object.keys(defaultLayoutState.ui))),
selectedPanel: selectedPanel || defaultLayoutState.selectedPanel,
theme: theme || defaultLayoutState.theme,
};
Expand All @@ -351,15 +349,15 @@ export const init: ModuleFn<SubAPI, SubState> = ({ store, provider, singleStory

const updatedLayout = {
...layout,
...options.layout,
...(options.layout || {}),
...pick(options, Object.keys(layout)),
...(singleStory && { navSize: 0 }),
};

const updatedUi = {
...ui,
...options.ui,
...pick(options, Object.keys(ui)),
...toMerged(options.ui || {}, pick(options, Object.keys(ui))),
};

const updatedTheme = {
Expand Down Expand Up @@ -388,7 +386,7 @@ export const init: ModuleFn<SubAPI, SubState> = ({ store, provider, singleStory
},
};

const persisted = pick(store.getState(), 'layout', 'selectedPanel');
const persisted = pick(store.getState(), ['layout', 'selectedPanel']);

provider.channel?.on(SET_CONFIG, () => {
api.setOptions(merge(api.getInitialOptions(), persisted));
Expand Down
2 changes: 1 addition & 1 deletion code/core/src/manager-api/modules/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { API_Notification } from '@storybook/core/types';

import partition from 'lodash/partition.js';
import { partition } from 'es-toolkit';

import type { ModuleFn } from '../lib/types';

Expand Down
12 changes: 3 additions & 9 deletions code/core/src/manager-api/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ import {
STORY_CHANGED,
} from '@storybook/core/core-events';

import mergeWith from 'lodash/mergeWith.js';
import { mergeWith } from 'es-toolkit';

import { createContext } from './context';
import getInitialState from './initial-state';
import { types } from './lib/addons';
import { noArrayMerge } from './lib/merge';
import type { ModuleFn } from './lib/types';
import * as addons from './modules/addons';
import * as channel from './modules/channel';
Expand Down Expand Up @@ -129,14 +130,7 @@ export type ManagerProviderProps = RouterData &

// This is duplicated from @storybook/preview-api for the reasons mentioned in lib-addons/types.js
export const combineParameters = (...parameterSets: Parameters[]) =>
mergeWith({}, ...parameterSets, (objValue: any, srcValue: any) => {
// Treat arrays as scalars:
if (Array.isArray(srcValue)) {
return srcValue;
}

return undefined;
});
noArrayMerge({}, ...parameterSets);

class ManagerProvider extends Component<ManagerProviderProps, State> {
api: API = {} as API;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { within } from '@storybook/test';

import { ManagerContext } from '@storybook/core/manager-api';

import { startCase } from 'lodash';
import { startCase } from 'es-toolkit';

import { LayoutProvider, useLayout } from '../../layout/LayoutProvider';
import { MobileNavigation } from './MobileNavigation';
Expand Down
2 changes: 1 addition & 1 deletion code/core/src/manager/components/preview/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export function filterTabs(panels: Addon_BaseType[], parameters?: Record<string,

if (previewTabs || parametersTabs) {
// deep merge global and local settings
const tabs = merge(previewTabs, parametersTabs);
const tabs = merge(previewTabs || {}, parametersTabs || {});
const arrTabs = Object.keys(tabs).map((key, index) => ({
index,
...(typeof tabs[key] === 'string' ? { title: tabs[key] } : tabs[key]),
Expand Down
5 changes: 4 additions & 1 deletion code/core/src/manager/components/preview/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ function toolbarItemHasBeenExcluded(item: Partial<Addon_BaseType>, entry: LeafEn
const toolbarItemsFromStoryParameters = 'toolbar' in parameters ? parameters.toolbar : undefined;
const { toolbar: toolbarItemsFromAddonsConfig } = addons.getConfig();

const toolbarItems = merge(toolbarItemsFromAddonsConfig, toolbarItemsFromStoryParameters);
const toolbarItems = merge(
toolbarItemsFromAddonsConfig || {},
toolbarItemsFromStoryParameters || {}
);

// @ts-expect-error (non strict)
return toolbarItems ? !!toolbarItems[item?.id]?.hidden : false;
Expand Down
2 changes: 1 addition & 1 deletion code/core/src/manager/components/sidebar/useExpanded.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { STORIES_COLLAPSE_ALL, STORIES_EXPAND_ALL } from '@storybook/core/core-e
import type { StoriesHash } from '@storybook/core/manager-api';
import { useStorybookApi } from '@storybook/core/manager-api';

import throttle from 'lodash/throttle.js';
import { throttle } from 'es-toolkit';

import { matchesKeyCode, matchesModifiers } from '../../keybinding';
import { getAncestorIds, getDescendantIds, isAncestor, scrollIntoView } from '../../utils/tree';
Expand Down
2 changes: 1 addition & 1 deletion code/core/src/manager/components/sidebar/useLastViewed.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';

import debounce from 'lodash/debounce.js';
import { debounce } from 'es-toolkit/compat';
import store from 'store2';

import type { Selection, StoryRef } from './types';
Expand Down
10 changes: 5 additions & 5 deletions code/core/src/preview-api/modules/preview-web/PreviewWeb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
UPDATE_STORY_ARGS,
} from '@storybook/core/core-events';

import merge from 'lodash/merge.js';
import { merge, toMerged } from 'es-toolkit';

import { addons } from '../addons';
import type { StoryStore } from '../store';
Expand Down Expand Up @@ -308,7 +308,7 @@ describe('PreviewWeb', () => {
expect(mockChannel.emit).toHaveBeenCalledWith(STORY_MISSING, 'component-one--missing');

mockChannel.emit.mockClear();
const newComponentOneExports = merge({}, componentOneExports, {
const newComponentOneExports = toMerged(componentOneExports, {
d: { args: { foo: 'd' }, play: vi.fn() },
});
const newImportFn = vi.fn(async (path) => {
Expand Down Expand Up @@ -362,7 +362,7 @@ describe('PreviewWeb', () => {
});
await waitForSetCurrentStory();

const newComponentOneExports = merge({}, componentOneExports, {
const newComponentOneExports = toMerged(componentOneExports, {
d: { args: { foo: 'd' }, play: vi.fn() },
});
const newImportFn = vi.fn(async (path) => {
Expand Down Expand Up @@ -2927,7 +2927,7 @@ describe('PreviewWeb', () => {
});

describe('when the current story changes', () => {
const newComponentOneExports = merge({}, componentOneExports, {
const newComponentOneExports = toMerged(componentOneExports, {
a: { args: { foo: 'edited' } },
});
const newImportFn = vi.fn(async (path) => {
Expand Down Expand Up @@ -3282,7 +3282,7 @@ describe('PreviewWeb', () => {
afterEach(() => {
vi.useRealTimers();
});
const newComponentOneExports = merge({}, componentOneExports, {
const newComponentOneExports = toMerged(componentOneExports, {
a: { args: { bar: 'edited' }, argTypes: { bar: { type: { name: 'string' } } } },
});
const newImportFn = vi.fn(async (path) => {
Expand Down
Loading

0 comments on commit f529029

Please sign in to comment.