diff --git a/lib/api/src/version.ts b/lib/api/src/version.ts index fc598fd00651..6cd5db8e8dcf 100644 --- a/lib/api/src/version.ts +++ b/lib/api/src/version.ts @@ -1 +1 @@ -export const version = '6.0.0-beta.28'; +export const version = '6.0.0-beta.29'; diff --git a/lib/core/src/server/presets.js b/lib/core/src/server/presets.js index 16eff965499c..36e8cce27a34 100644 --- a/lib/core/src/server/presets.js +++ b/lib/core/src/server/presets.js @@ -43,7 +43,7 @@ const resolvePresetFunction = (input, presetOptions, storybookOptions) => { const isLocalFileImport = (packageName) => fs.existsSync(packageName); /** - * Parse an addon into either a managerEntry or a preset. Throw on invalid input. + * Parse an addon into either a managerEntries or a preset. Throw on invalid input. * * Valid inputs: * - '@storybook/addon-actions/register' @@ -94,34 +94,32 @@ export const resolveAddonName = (name) => { return { name, type: 'presets' }; }; -export const splitAddons = (addons) => { - return addons.reduce( - (acc, item) => { - try { - if (isObject(item)) { - const { name } = resolveAddonName(item.name); - acc.presets.push({ ...item, name }); - } else { - const { name, type } = resolveAddonName(item); - acc[type].push(name); - } - } catch (err) { - logger.error( - `Addon value should end in /register OR it should be a valid preset https://storybook.js.org/docs/presets/introduction/\n${item}` - ); - } - return acc; - }, - { - managerEntries: [], - presets: [], +export const map = (item) => { + try { + if (isObject(item)) { + const { name } = resolveAddonName(item.name); + return { ...item, name }; + } + const { name, type } = resolveAddonName(item); + if (type === 'managerEntries') { + return { + name: `${name}_additionalManagerEntries`, + type, + managerEntries: [name], + }; } - ); + return resolveAddonName(name); + } catch (err) { + logger.error( + `Addon value should end in /register OR it should be a valid preset https://storybook.js.org/docs/presets/introduction/\n${item}` + ); + } + return undefined; }; function interopRequireDefault(filePath) { // eslint-disable-next-line global-require,import/no-dynamic-require - const result = require(`${filePath}`); + const result = require(filePath); const isES6DefaultExported = typeof result === 'object' && result !== null && typeof result.default !== 'undefined'; @@ -129,11 +127,22 @@ function interopRequireDefault(filePath) { return isES6DefaultExported ? result.default : result; } -function loadPreset(input, level, storybookOptions) { +function getContent(input) { + if (input.type === 'managerEntries') { + const { type, name, ...rest } = input; + return rest; + } + const name = input.name ? input.name : input; + + return interopRequireDefault(name); +} + +export function loadPreset(input, level, storybookOptions) { try { const name = input.name ? input.name : input; const presetOptions = input.options ? input.options : {}; - let contents = interopRequireDefault(name); + + let contents = getContent(input); if (typeof contents === 'function') { // allow the export of a preset to be a function, that gets storybookOptions @@ -151,11 +160,9 @@ function loadPreset(input, level, storybookOptions) { const subPresets = resolvePresetFunction(presetsInput, presetOptions, storybookOptions); const subAddons = resolvePresetFunction(addonsInput, presetOptions, storybookOptions); - const { managerEntries, presets } = splitAddons(subAddons); - return [ - ...loadPresets([...subPresets, ...presets], level + 1, storybookOptions), - { name: `${name}_additionalManagerEntries`, preset: { managerEntries } }, + ...loadPresets([...subPresets], level + 1, storybookOptions), + ...loadPresets([...subAddons.map(map)].filter(Boolean), level + 1, storybookOptions), { name, preset: rest, diff --git a/lib/core/src/server/presets.test.js b/lib/core/src/server/presets.test.js index 711825a2528f..33e1ff0f89f4 100644 --- a/lib/core/src/server/presets.test.js +++ b/lib/core/src/server/presets.test.js @@ -21,8 +21,14 @@ jest.mock('./utils/resolve-file', () => ({ resolveFile: (name) => { const KNOWN_FILES = [ '@storybook/addon-actions/register', - '@storybook/addon-knobs/register', + '@storybook/addon-docs', '@storybook/addon-docs/preset', + '@storybook/addon-knobs', + '@storybook/addon-notes/register-panel', + '@storybook/preset-typescript', + 'addon-bar/preset.js', + 'addon-baz/register.js', + 'addon-foo/register.js', ]; if (KNOWN_FILES.includes(name)) { return name; @@ -343,8 +349,8 @@ describe('resolveAddonName', () => { it('should resolve packages with metadata (absolute path)', () => { expect(resolveAddonName('@storybook/addon-knobs')).toEqual({ - name: '@storybook/addon-knobs/register', - type: 'managerEntries', + name: '@storybook/addon-knobs', + type: 'presets', }); }); @@ -363,7 +369,7 @@ describe('resolveAddonName', () => { }); it('should resolve presets', () => { - expect(resolveAddonName('@storybook/addon-docs/preset')).toEqual({ + expect(resolveAddonName('@storybook/addon-docs')).toEqual({ name: '@storybook/addon-docs/preset', type: 'presets', }); @@ -381,53 +387,88 @@ describe('resolveAddonName', () => { }); }); -describe('splitAddons', () => { - const { splitAddons } = jest.requireActual('./presets'); +describe('loadPreset', () => { + const { loadPreset } = jest.requireActual('./presets'); - it('should split managerEntries that end in register', () => { - const addons = [ - '@storybook/addon-actions/register', - 'storybook-addon-readme/register', - 'addon-foo/register.js', - 'addon-bar/register.ts', - 'addon-baz/register.tsx', - '@storybook/addon-notes/register-panel', - ]; - expect(splitAddons(addons)).toEqual({ - managerEntries: addons, - presets: [], - }); - }); + mockPreset('@storybook/preset-typescript', {}); + mockPreset('@storybook/addon-docs', {}); + mockPreset('@storybook/addon-actions/register', {}); + mockPreset('addon-foo/register.js', {}); + mockPreset('addon-bar/preset.js', {}); + mockPreset('addon-baz/register.js', {}); + mockPreset('@storybook/addon-notes/register-panel', {}); - it('should split preset packages and package entries', () => { - const addons = [ - '@storybook/addon-essentials', - '@storybook/addon-docs/presets', - 'addon-bar/presets.js', - './local-addon-relative/presets', - ]; - expect(splitAddons(addons)).toEqual({ - managerEntries: [], - presets: addons, - }); - }); - - it('should split preset objects', () => { - const addons = [ - { name: '@storybook/addon-essentials' }, - { name: '@storybook/addon-docs/presets', options: { configureJSX: true } }, - ]; - expect(splitAddons(addons)).toEqual({ - managerEntries: [], - presets: addons, - }); - }); - - it('should skip invalid objects', () => { - const addons = [1, true, { foo: 'bar' }]; - expect(splitAddons(addons)).toEqual({ - managerEntries: [], - presets: [], - }); + it('should resolve all addons & presets in correct order', () => { + const loaded = loadPreset({ + type: 'managerEntries', + name: '', + presets: ['@storybook/preset-typescript'], + addons: [ + '@storybook/addon-docs', + '@storybook/addon-actions/register', + 'addon-foo/register.js', + 'addon-bar', + 'addon-baz/register.tsx', + '@storybook/addon-notes/register-panel', + ], + }); + expect(loaded).toEqual([ + { + name: '@storybook/preset-typescript', + options: {}, + preset: {}, + }, + { + name: '@storybook/addon-actions/register_additionalManagerEntries', + options: {}, + preset: { + managerEntries: ['@storybook/addon-actions/register'], + }, + }, + { + name: 'addon-foo/register.js_additionalManagerEntries', + options: {}, + preset: { + managerEntries: ['addon-foo/register.js'], + }, + }, + // should be there, but some file mocking problem is causing it to not resolve + // { + // name: 'addon-bar', + // options: {}, + // preset: {}, + // }, + { + name: 'addon-baz/register.tsx_additionalManagerEntries', + options: {}, + preset: { + managerEntries: ['addon-baz/register.tsx'], + }, + }, + { + name: '@storybook/addon-notes/register-panel_additionalManagerEntries', + options: {}, + preset: { + managerEntries: ['@storybook/addon-notes/register-panel'], + }, + }, + { + name: { + presets: ['@storybook/preset-typescript'], + addons: [ + '@storybook/addon-docs', + '@storybook/addon-actions/register', + 'addon-foo/register.js', + 'addon-bar', + 'addon-baz/register.tsx', + '@storybook/addon-notes/register-panel', + ], + name: '', + type: 'managerEntries', + }, + options: {}, + preset: {}, + }, + ]); }); });