Merge pull request #11178 from storybookjs/fix/9819-addon-order

Core: Fix addon load order
This commit is contained in:
Michael Shilman 2020-06-16 18:57:54 +08:00 committed by GitHub
commit 7f137ec004
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 128 additions and 80 deletions

View File

@ -1 +1 @@
export const version = '6.0.0-beta.28'; export const version = '6.0.0-beta.29';

View File

@ -43,7 +43,7 @@ const resolvePresetFunction = (input, presetOptions, storybookOptions) => {
const isLocalFileImport = (packageName) => fs.existsSync(packageName); 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: * Valid inputs:
* - '@storybook/addon-actions/register' * - '@storybook/addon-actions/register'
@ -94,34 +94,32 @@ export const resolveAddonName = (name) => {
return { name, type: 'presets' }; return { name, type: 'presets' };
}; };
export const splitAddons = (addons) => { export const map = (item) => {
return addons.reduce(
(acc, item) => {
try { try {
if (isObject(item)) { if (isObject(item)) {
const { name } = resolveAddonName(item.name); const { name } = resolveAddonName(item.name);
acc.presets.push({ ...item, name }); return { ...item, name };
} else {
const { name, type } = resolveAddonName(item);
acc[type].push(name);
} }
const { name, type } = resolveAddonName(item);
if (type === 'managerEntries') {
return {
name: `${name}_additionalManagerEntries`,
type,
managerEntries: [name],
};
}
return resolveAddonName(name);
} catch (err) { } catch (err) {
logger.error( logger.error(
`Addon value should end in /register OR it should be a valid preset https://storybook.js.org/docs/presets/introduction/\n${item}` `Addon value should end in /register OR it should be a valid preset https://storybook.js.org/docs/presets/introduction/\n${item}`
); );
} }
return acc; return undefined;
},
{
managerEntries: [],
presets: [],
}
);
}; };
function interopRequireDefault(filePath) { function interopRequireDefault(filePath) {
// eslint-disable-next-line global-require,import/no-dynamic-require // eslint-disable-next-line global-require,import/no-dynamic-require
const result = require(`${filePath}`); const result = require(filePath);
const isES6DefaultExported = const isES6DefaultExported =
typeof result === 'object' && result !== null && typeof result.default !== 'undefined'; typeof result === 'object' && result !== null && typeof result.default !== 'undefined';
@ -129,11 +127,22 @@ function interopRequireDefault(filePath) {
return isES6DefaultExported ? result.default : result; 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 { try {
const name = input.name ? input.name : input; const name = input.name ? input.name : input;
const presetOptions = input.options ? input.options : {}; const presetOptions = input.options ? input.options : {};
let contents = interopRequireDefault(name);
let contents = getContent(input);
if (typeof contents === 'function') { if (typeof contents === 'function') {
// allow the export of a preset to be a function, that gets storybookOptions // 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 subPresets = resolvePresetFunction(presetsInput, presetOptions, storybookOptions);
const subAddons = resolvePresetFunction(addonsInput, presetOptions, storybookOptions); const subAddons = resolvePresetFunction(addonsInput, presetOptions, storybookOptions);
const { managerEntries, presets } = splitAddons(subAddons);
return [ return [
...loadPresets([...subPresets, ...presets], level + 1, storybookOptions), ...loadPresets([...subPresets], level + 1, storybookOptions),
{ name: `${name}_additionalManagerEntries`, preset: { managerEntries } }, ...loadPresets([...subAddons.map(map)].filter(Boolean), level + 1, storybookOptions),
{ {
name, name,
preset: rest, preset: rest,

View File

@ -21,8 +21,14 @@ jest.mock('./utils/resolve-file', () => ({
resolveFile: (name) => { resolveFile: (name) => {
const KNOWN_FILES = [ const KNOWN_FILES = [
'@storybook/addon-actions/register', '@storybook/addon-actions/register',
'@storybook/addon-knobs/register', '@storybook/addon-docs',
'@storybook/addon-docs/preset', '@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)) { if (KNOWN_FILES.includes(name)) {
return name; return name;
@ -343,8 +349,8 @@ describe('resolveAddonName', () => {
it('should resolve packages with metadata (absolute path)', () => { it('should resolve packages with metadata (absolute path)', () => {
expect(resolveAddonName('@storybook/addon-knobs')).toEqual({ expect(resolveAddonName('@storybook/addon-knobs')).toEqual({
name: '@storybook/addon-knobs/register', name: '@storybook/addon-knobs',
type: 'managerEntries', type: 'presets',
}); });
}); });
@ -363,7 +369,7 @@ describe('resolveAddonName', () => {
}); });
it('should resolve presets', () => { it('should resolve presets', () => {
expect(resolveAddonName('@storybook/addon-docs/preset')).toEqual({ expect(resolveAddonName('@storybook/addon-docs')).toEqual({
name: '@storybook/addon-docs/preset', name: '@storybook/addon-docs/preset',
type: 'presets', type: 'presets',
}); });
@ -381,53 +387,88 @@ describe('resolveAddonName', () => {
}); });
}); });
describe('splitAddons', () => { describe('loadPreset', () => {
const { splitAddons } = jest.requireActual('./presets'); const { loadPreset } = jest.requireActual('./presets');
it('should split managerEntries that end in register', () => { mockPreset('@storybook/preset-typescript', {});
const addons = [ 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 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', '@storybook/addon-actions/register',
'storybook-addon-readme/register',
'addon-foo/register.js', 'addon-foo/register.js',
'addon-bar/register.ts', 'addon-bar',
'addon-baz/register.tsx', 'addon-baz/register.tsx',
'@storybook/addon-notes/register-panel', '@storybook/addon-notes/register-panel',
]; ],
expect(splitAddons(addons)).toEqual({
managerEntries: addons,
presets: [],
});
});
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: [],
}); });
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: {},
},
]);
}); });
}); });