From ba0a5128dc1f7a96053cbc2d8b675ef83ddd68e0 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 9 Apr 2024 09:32:12 +0200 Subject: [PATCH] Final refactorings --- .../create-new-story-channel.test.ts | 54 +++---------------- .../create-new-story-channel.ts | 48 ++--------------- .../src/utils/get-new-story-file.test.ts | 5 +- .../src/utils/get-story-id.test.ts | 45 ++++++++++++++++ .../lib/core-server/src/utils/get-story-id.ts | 44 +++++++++++++++ 5 files changed, 102 insertions(+), 94 deletions(-) create mode 100644 code/lib/core-server/src/utils/get-story-id.test.ts create mode 100644 code/lib/core-server/src/utils/get-story-id.ts diff --git a/code/lib/core-server/src/server-channel/create-new-story-channel.test.ts b/code/lib/core-server/src/server-channel/create-new-story-channel.test.ts index b725ed036946..0898c9fab2b2 100644 --- a/code/lib/core-server/src/server-channel/create-new-story-channel.test.ts +++ b/code/lib/core-server/src/server-channel/create-new-story-channel.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { getStoryId, initCreateNewStoryChannel } from './create-new-story-channel'; +import { initCreateNewStoryChannel } from './create-new-story-channel'; import path from 'path'; import type { ChannelTransport } from '@storybook/channels'; import { Channel } from '@storybook/channels'; @@ -15,16 +15,16 @@ vi.mock('@storybook/core-common', async (importOriginal) => { const mockFs = vi.hoisted(() => { return { - writeFileSync: vi.fn(), + writeFile: vi.fn(), }; }); -vi.mock('fs', async (importOriginal) => { - const actual = await importOriginal(); +vi.mock('fs/promises', async (importOriginal) => { + const actual = await importOriginal(); return { default: { ...actual, - writeFileSync: mockFs.writeFileSync, + writeFile: mockFs.writeFile, }, }; }); @@ -40,48 +40,6 @@ describe('createNewStoryChannel', () => { createNewStoryFileEventListener.mockClear(); }); - describe('getStoryId', () => { - it('should return the storyId', async () => { - const cwd = process.cwd(); - const options = { - configDir: path.join(cwd, '.storybook'), - presets: { - apply: (val: string) => { - if (val === 'stories') { - return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']); - } - }, - }, - } as any; - const storyFilePath = path.join(cwd, 'src', 'components', 'stories', 'Page1.stories.ts'); - const exportedStoryName = 'Default'; - - const storyId = await getStoryId(options, storyFilePath, exportedStoryName); - - expect(storyId).toBe('components-stories-page1--default'); - }); - - it('should throw an error if the storyId cannot be calculated', async () => { - const cwd = process.cwd(); - const options = { - configDir: path.join(cwd, '.storybook'), - presets: { - apply: (val: string) => { - if (val === 'stories') { - return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']); - } - }, - }, - } as any; - const storyFilePath = path.join(cwd, 'not-covered-path', 'stories', 'Page1.stories.ts'); - const exportedStoryName = 'Default'; - - await expect(() => - getStoryId(options, storyFilePath, exportedStoryName) - ).rejects.toThrowError(); - }); - }); - describe('initCreateNewStoryChannel', () => { it('should emit an event with a story id', async () => { mockChannel.addListener(CREATE_NEW_STORYFILE_RESULT, createNewStoryFileEventListener); @@ -124,7 +82,7 @@ describe('createNewStoryChannel', () => { mockChannel.addListener(CREATE_NEW_STORYFILE_RESULT, createNewStoryFileEventListener); const cwd = process.cwd(); - mockFs.writeFileSync.mockImplementation(() => { + mockFs.writeFile.mockImplementation(() => { throw new Error('Failed to write file'); }); diff --git a/code/lib/core-server/src/server-channel/create-new-story-channel.ts b/code/lib/core-server/src/server-channel/create-new-story-channel.ts index 180589ea8ce2..5a6cc70648e5 100644 --- a/code/lib/core-server/src/server-channel/create-new-story-channel.ts +++ b/code/lib/core-server/src/server-channel/create-new-story-channel.ts @@ -2,13 +2,9 @@ import type { Options } from '@storybook/types'; import type { Channel } from '@storybook/channels'; import { CREATE_NEW_STORYFILE, CREATE_NEW_STORYFILE_RESULT } from '@storybook/core-events'; import dedent from 'ts-dedent'; -import { normalizeStories, normalizeStoryPath } from '@storybook/core-common'; -import path from 'path'; -import fs from 'fs'; -import { storyNameFromExport, toId } from '@storybook/csf'; -import slash from 'slash'; -import { userOrAutoTitleFromSpecifier } from '@storybook/preview-api'; +import fs from 'fs/promises'; import { getNewStoryFile } from '../utils/get-new-story-file'; +import { getStoryId } from '../utils/get-story-id'; interface Data { // The filepath of the component for which the Story should be generated for (relative to the project root) @@ -38,11 +34,11 @@ export function initCreateNewStoryChannel(channel: Channel, options: Options) { options ); - fs.writeFileSync(storyFilePath, storyFileContent, { + await fs.writeFile(storyFilePath, storyFileContent, { encoding: 'utf-8', }); - const storyId = await getStoryId(options, storyFilePath, exportedStoryName); + const storyId = await getStoryId({ storyFilePath, exportedStoryName }, options); channel.emit(CREATE_NEW_STORYFILE_RESULT, { success: true, @@ -65,39 +61,3 @@ export function initCreateNewStoryChannel(channel: Channel, options: Options) { return channel; } - -export async function getStoryId( - options: Options, - storyFilePath: string, - exportedStoryName: string -) { - const stories = await options.presets.apply('stories', [], options); - - const workingDir = process.cwd(); - - const normalizedStories = normalizeStories(stories, { - configDir: options.configDir, - workingDir, - }); - - const relativePath = path.relative(workingDir, storyFilePath); - const importPath = slash(normalizeStoryPath(relativePath)); - - const autoTitle = normalizedStories - .map((normalizeStory) => userOrAutoTitleFromSpecifier(importPath, normalizeStory)) - .filter(Boolean)[0]; - - if (autoTitle === undefined) { - // eslint-disable-next-line local-rules/no-uncategorized-errors - throw new Error(dedent` - The generation of your new Story file was successful! But it seems that we are unable to index it. - Please make sure that the new Story file is matched by the 'stories' glob pattern in your Storybook configuration. - The location of the new Story file is: ${relativePath} - `); - } - - const storyName = storyNameFromExport(exportedStoryName); - const storyId = toId(autoTitle as string, storyName); - - return storyId; -} diff --git a/code/lib/core-server/src/utils/get-new-story-file.test.ts b/code/lib/core-server/src/utils/get-new-story-file.test.ts index 96533d243ed3..9886cd56bc35 100644 --- a/code/lib/core-server/src/utils/get-new-story-file.test.ts +++ b/code/lib/core-server/src/utils/get-new-story-file.test.ts @@ -31,8 +31,9 @@ describe('get-new-story-file', () => { expect(exportedStoryName).toBe('Default'); expect(storyFileContent).toMatchInlineSnapshot(` - "import { Page } from './Page'; - import type { Meta, StoryObj } from '@storybook/nextjs'; + "import type { Meta, StoryObj } from '@storybook/nextjs'; + + import { Page } from './Page'; const meta = { component: Page diff --git a/code/lib/core-server/src/utils/get-story-id.test.ts b/code/lib/core-server/src/utils/get-story-id.test.ts new file mode 100644 index 000000000000..4fb0230bc21e --- /dev/null +++ b/code/lib/core-server/src/utils/get-story-id.test.ts @@ -0,0 +1,45 @@ +import path from 'path'; +import { describe, expect, it } from 'vitest'; +import { getStoryId } from './get-story-id'; + +describe('getStoryId', () => { + it('should return the storyId', async () => { + const cwd = process.cwd(); + const options = { + configDir: path.join(cwd, '.storybook'), + presets: { + apply: (val: string) => { + if (val === 'stories') { + return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']); + } + }, + }, + } as any; + const storyFilePath = path.join(cwd, 'src', 'components', 'stories', 'Page1.stories.ts'); + const exportedStoryName = 'Default'; + + const storyId = await getStoryId({ storyFilePath, exportedStoryName }, options); + + expect(storyId).toBe('components-stories-page1--default'); + }); + + it('should throw an error if the storyId cannot be calculated', async () => { + const cwd = process.cwd(); + const options = { + configDir: path.join(cwd, '.storybook'), + presets: { + apply: (val: string) => { + if (val === 'stories') { + return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']); + } + }, + }, + } as any; + const storyFilePath = path.join(cwd, 'not-covered-path', 'stories', 'Page1.stories.ts'); + const exportedStoryName = 'Default'; + + await expect(() => + getStoryId({ storyFilePath, exportedStoryName }, options) + ).rejects.toThrowError(); + }); +}); diff --git a/code/lib/core-server/src/utils/get-story-id.ts b/code/lib/core-server/src/utils/get-story-id.ts new file mode 100644 index 000000000000..299a9eadaaf5 --- /dev/null +++ b/code/lib/core-server/src/utils/get-story-id.ts @@ -0,0 +1,44 @@ +import type { Options } from '@storybook/types'; +import dedent from 'ts-dedent'; +import { normalizeStories, normalizeStoryPath } from '@storybook/core-common'; +import path from 'path'; +import { storyNameFromExport, toId } from '@storybook/csf'; +import slash from 'slash'; +import { userOrAutoTitleFromSpecifier } from '@storybook/preview-api'; + +interface Data { + storyFilePath: string; + exportedStoryName: string; +} + +export async function getStoryId(data: Data, options: Options) { + const stories = await options.presets.apply('stories', [], options); + + const workingDir = process.cwd(); + + const normalizedStories = normalizeStories(stories, { + configDir: options.configDir, + workingDir, + }); + + const relativePath = path.relative(workingDir, data.storyFilePath); + const importPath = slash(normalizeStoryPath(relativePath)); + + const autoTitle = normalizedStories + .map((normalizeStory) => userOrAutoTitleFromSpecifier(importPath, normalizeStory)) + .filter(Boolean)[0]; + + if (autoTitle === undefined) { + // eslint-disable-next-line local-rules/no-uncategorized-errors + throw new Error(dedent` + The generation of your new Story file was successful! But it seems that we are unable to index it. + Please make sure that the new Story file is matched by the 'stories' glob pattern in your Storybook configuration. + The location of the new Story file is: ${relativePath} + `); + } + + const storyName = storyNameFromExport(data.exportedStoryName); + const storyId = toId(autoTitle as string, storyName); + + return storyId; +}