diff --git a/addons/docs/src/frameworks/common/preset.ts b/addons/docs/src/frameworks/common/preset.ts index ce48d65c65f..f1c5764d650 100644 --- a/addons/docs/src/frameworks/common/preset.ts +++ b/addons/docs/src/frameworks/common/preset.ts @@ -99,7 +99,7 @@ export function webpack(webpackConfig: any = {}, options: any = {}) { return result; } -export function addons(entry: any[] = [], options: any) { +export function managerEntries(entry: any[] = [], options: any) { return [...entry, require.resolve('../../register')]; } diff --git a/addons/docs/src/preset.ts b/addons/docs/src/preset.ts index ba24891b7bf..48f1033657d 100644 --- a/addons/docs/src/preset.ts +++ b/addons/docs/src/preset.ts @@ -11,7 +11,7 @@ const PRESET_METHODS = [ 'webpack', 'webpackFinal', 'managerWebpack', - 'addons', + 'managerEntries', 'entries', 'config', ]; diff --git a/addons/essentials/src/index.ts b/addons/essentials/src/index.ts index fc9dbd0f125..35fc43c9f16 100644 --- a/addons/essentials/src/index.ts +++ b/addons/essentials/src/index.ts @@ -22,7 +22,7 @@ const isInstalled = (addon: string) => { const makeAddon = (key: string) => `@storybook/addon-${key}`; -export function addons(entry: any[] = [], options: PresetOptions = {}) { +export function managerEntries(entry: any[] = [], options: PresetOptions = {}) { const registerAddons = ['backgrounds', 'viewport'] .filter(key => (options as any)[key] !== false) .map(key => makeAddon(key)) diff --git a/addons/knobs/src/preset/index.ts b/addons/knobs/src/preset/index.ts index 0bce78742e0..835b0fffa16 100644 --- a/addons/knobs/src/preset/index.ts +++ b/addons/knobs/src/preset/index.ts @@ -2,7 +2,7 @@ type KnobsOptions = { addDecorator?: boolean; }; -export function addons(entry: any[] = [], options: any) { +export function managerEntries(entry: any[] = [], options: any) { return [...entry, require.resolve('../register')]; } diff --git a/addons/storysource/src/preset.js b/addons/storysource/src/preset.js index 34ff01a97f1..3bf0a6d9fab 100644 --- a/addons/storysource/src/preset.js +++ b/addons/storysource/src/preset.js @@ -24,8 +24,8 @@ function webpack(webpackConfig = {}, options = {}) { }; } -function addons(entry = []) { +function managerEntries(entry = []) { return [...entry, require.resolve('@storybook/addon-storysource/register')]; } -module.exports = { webpack, addons }; +module.exports = { webpack, managerEntries }; diff --git a/docs/src/pages/presets/writing-presets/index.md b/docs/src/pages/presets/writing-presets/index.md index 16422a4089b..79c59f95fd5 100644 --- a/docs/src/pages/presets/writing-presets/index.md +++ b/docs/src/pages/presets/writing-presets/index.md @@ -86,7 +86,7 @@ The addon config `addons` allows you to add addons to Storybook from within a pr For example, the Storysource preset contains the following code: ```js -export function addons(entry = []) { +export function managerEntries(entry = []) { return [...entry, require.resolve('@storybook/addon-storysource/register')]; } ``` @@ -95,8 +95,8 @@ This is equivalent to [registering the addon manually](../../addons/using-addons ```js module.exports = { - addons: ['@storybook/addon-storysource/register'] -} + addons: ['@storybook/addon-storysource/register'], +}; ``` ### Entries @@ -128,7 +128,7 @@ module.exports = { return config; }, addons: [], -} +}; ``` ## Sharing advanced configuration @@ -162,7 +162,7 @@ module.exports = { return config; }, addons: [], -} +}; ``` -Place your `my-preset.js` file where ever you want, if you want to share if far and wide you'll want to make it it's own package. +Place your `my-preset.js` file where ever you want, if you want to share if far and wide you'll want to make it it's own package. diff --git a/lib/core/src/server/manager/manager-preset.js b/lib/core/src/server/manager/manager-preset.js index 7b4906d45ea..e9b8ef0c5f1 100644 --- a/lib/core/src/server/manager/manager-preset.js +++ b/lib/core/src/server/manager/manager-preset.js @@ -5,12 +5,10 @@ export async function managerWebpack(_, options) { return createDevConfig(options); } -export async function managerEntries(_, options) { - const { presets, managerEntry = '../../client/manager' } = options; +export async function managerEntries(installedAddons, options) { + const { managerEntry = '../../client/manager' } = options; const entries = [require.resolve('../common/polyfills')]; - const installedAddons = await presets.apply('addons', [], options); - if (installedAddons && installedAddons.length) { entries.push(...installedAddons); } @@ -20,8 +18,4 @@ export async function managerEntries(_, options) { return entries; } -export async function addons(_, options) { - return loadCustomAddons(options); -} - export * from '../common/common-preset'; diff --git a/lib/core/src/server/presets.js b/lib/core/src/server/presets.js index 411573f2e1e..81bf727d779 100644 --- a/lib/core/src/server/presets.js +++ b/lib/core/src/server/presets.js @@ -4,7 +4,7 @@ import dedent from 'ts-dedent'; const isObject = val => val != null && typeof val === 'object' && Array.isArray(val) === false; const isFunction = val => typeof val === 'function'; -const sanitizeSubPresets = (input, presetOptions, storybookOptions) => { +const resolvePresetFunction = (input, presetOptions, storybookOptions) => { if (isFunction(input)) { return input({ ...storybookOptions, ...presetOptions }); } @@ -15,6 +15,29 @@ const sanitizeSubPresets = (input, presetOptions, storybookOptions) => { return []; }; +export const splitAddons = addons => { + return addons.reduce( + (acc, item) => { + if (typeof item === 'string' && item.match(/register$/)) { + acc.managerEntries.push(item); + } else if (typeof item === 'string' || (isObject(item) && item.name)) { + acc.presets.push(item); + } else { + logger.error( + 'Addon value should end in /register OR it should be a valid preset https://storybook.js.org/docs/presets/introduction/', + item + ); + } + + return acc; + }, + { + managerEntries: [], + presets: [], + } + ); +}; + function interopRequireDefault(filePath) { // eslint-disable-next-line global-require,import/no-dynamic-require const result = require(`${filePath}`); @@ -37,11 +60,16 @@ function loadPreset(input, level, storybookOptions) { } if (isObject(contents)) { - const { presets: subPresetsInput, ...rest } = contents; - const subPresets = sanitizeSubPresets(subPresetsInput, presetOptions, storybookOptions); + const { addons: addonsInput, presets: presetsInput, ...rest } = contents; + + const subPresets = resolvePresetFunction(presetsInput, presetOptions, storybookOptions); + const subAddons = resolvePresetFunction(addonsInput, presetOptions, storybookOptions); + + const { managerEntries, presets } = splitAddons(subAddons); return [ - ...loadPresets(subPresets, level + 1, storybookOptions), + ...loadPresets([...subPresets, ...presets], level + 1, storybookOptions), + { name: `${name}_additionalManagerEntries`, preset: { managerEntries } }, { name, preset: rest, diff --git a/lib/core/src/server/presets.test.js b/lib/core/src/server/presets.test.js index 613f4cddd5a..86be48c3bd5 100644 --- a/lib/core/src/server/presets.test.js +++ b/lib/core/src/server/presets.test.js @@ -316,3 +316,40 @@ describe('presets', () => { jest.resetModules(); }); }); + +describe('splitAddons', () => { + const { splitAddons } = require.requireActual('./presets'); + + it('should split managerEntries that end in register', () => { + const addons = ['@storybook/addon-actions/register', '@storybook-addon-readme/register']; + expect(splitAddons(addons)).toEqual({ + managerEntries: addons, + presets: [], + }); + }); + it('should split preset packages and package entries', () => { + const addons = ['@storybook/addon-essentials', '@storybook/addon-docs/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: [], + }); + }); +});