Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config V2: storybook.config.js - Monoconfig / Single Configuration File #6806

Closed
ndelangen opened this issue May 17, 2019 · 7 comments
Closed
Assignees
Labels

Comments

@ndelangen
Copy link
Member

Is your feature request related to a problem? Please describe.
The method of configuring Storybook is broken. We have config.js and addons.js which are loaded in either the preview or manager. It isn't really obvious to anyone that's what's going on, and what impact it has when you require some code in either.

config.js main purpose is to require all story files. This is problematic since it prevents code-splitting, making the storybook bundles really big, and output warnings.

Describe the solution you'd like
We implement a storybook.config.js file that exports a preset.

the preset can have names exports:

  • entries - a glob for which files storybook should consider story files
  • webpack - a async function that allows users to modify the preview webpack settings
  • managerWebpack - a async function that allows users to modify the manager webpack settings
  • server - an object with settings for the server (port, hostname, sll, etc)
  • theme - a rich object for theming (can have functions even)
  • more... - this is extendable.

This file will never be directly part of the user's bundle, we'll use it in node. perhaps for some exports (like theme) we need to import that export only into a bundle.

Are you able to assist bring the feature to reality?
Yes, I'm working on this. It's on pause right now, I'm trying to make improvements to the addon APIs & documentation first.

Additional context
origin/tech/monoconfig-fork

@ndelangen
Copy link
Member Author

API

The storybook.config.js will be at a root level of the repo.

The file is a ES module, and has named exports.

The exports that storybook will support initially are:

entries?: Array<string> | string
logLevel?: 'info' | 'warn' | 'error' | 'debug' | 'verbose' | number
addons?: Array<string>
presets?: Array<string> | Array<object>
webpack?: (base: WebpackConfig, env: 'production' | 'development' | string) => WebpackConfig
babel?: (base: BabelConfig, env: 'production' | 'development' | string) => BabelConfig
managerWebpack?: (base: WebpackConfig, env: 'production' | 'development' | string) => WebpackConfig
managerBabel?: (base: BabelConfig, env: 'production' | 'development' | string) => BabelConfig
output?: {
  location: string
  compress: boolean
  preview: boolean
}
server?: {
  port: number
  host: string
  static: {
    [path: string]: string,
  }
  ssl: {
    ca: string[],
    cert: string,
    key: string,
  }
  middleware: (app: ExpressApp, server: HttpServer) => Promise<void>
}
theme: Theme
previewInit: string[]
managerInit: string[]

Custom exports will be allowed, but how to use those will have to be determined later.

entries

Let's not call this field stories, because semantics will maybe changed

both a single string and array of strings are valid:

export const entries = 'src/**/*.story.js'
export const entries = ['src/**/*.stories.js', 'src/**/*.examples.js']

We'll have a sensible default, so it will work without any configuration, if the user does provide a configuration, it replaces the default.

addons

An array of addons in the following structure:

export const addons = ['@storybook/addon-X', '@storybook/addon-Y']

We will register the addons by ourselves (no need of addons.js).
This will auto-add the addons to the manager, but many addons have some preview part to them as well. We want to be able to auto load those as well.

We'll have to come up with a pattern for addons to easily declare what file should go in the manager, and which part should go in the preview.

This pattern pretty much exists for the manager: @storybook/addon-X/register, but not really for the preview. Because many addons currently exist as decorators which require the user to set it up in the story files.

Long term addon & presets will be the same thing, or at least technically have the exact same api & capability.

An addon could use the presets api for previewInit & managerInit for the purpose of injecting code into either bundle.

presets

presets is an array of string or preset defining objects:

export const presets = ['@storybook/preset-scss', '@storybook/preset-typescript']

array of objects

export const presets = [
  {
    preset: '@storybook/preset-scss',
    options: {}
  }
]

webpack

We only allow function composition to customise storybook's preview webpack config.
So this means no magic object merging. If the user wants to merge, they should use webpack-merge within this function. This config has no effect on the manager.

export const webpack = async (config, env) => {
  // make changes, preferable in a non-mutating fashion for debug-ability
  return config;
}

babel

Similar to webpack, no magic merging on our side.

What's really going on here, is that you're just changing the webpack config here; namely the webpack_config.module.rules[0].options. To be determined if having this options at all is a good idea, though perhaps it's needed for things like storysource, storyshots?

export const babel = (config, env) => {
  // make changes, preferable in a non-mutating fashion for debug-ability
  return config;
} 

managerWebpack & managerBabel

Fairly self-explanatory maybe, I won't repeat the api again, but these 2 export influence the manager's webpack config and have no effect on the preview.

output

This export is a collection of settings related to the output; so it lets storybook know where & what to output when building.

A future version of storybook will have the option of running the preview from another location/app possibly.

export const output = {
  location: './',
  compress: false,
  preview: true, // would enable/disable or set a custom location
}

server

This export is a collection of setting related to how to serve storybook. You can add proxies, additional static routes, configure SSL, change the port etc.

export const server = {
  port: 1337,
  host: 'localhost',
  static: {
    '/': 'assets',
  },
  ssl: {
    ca: [],
    cert: '',
    key: '',
  },
  middleware: async (app, server) => {},
}

initManager & initPreview

When you need certain code to run in the manager, create a setup file for this code and reference it in the array.

export const initManager = ['./src/globals']
export const initpreview = ['./src/globals']

This could be used to inject addons.

it can also be used to get compatibility with existing storybooks! Do this:

export const initManager = ['./.storybook/addons']
export const initpreview = ['./.storybook/config']

It's great for setting up global decorators, global framework things etc.

Presets can add things here.

Technically these would just be prepended as webpack entry points maybe?

things to cover

  • there are apps that has a customisation of configuration, for example we support custom tsconfig.json to be set in .storybook dir, only for angular app. How we will configure this ? maybe with presets?
  • React Native

deprecations:

  • ./storybook dir
  • custom webpack.config.js
  • custom babelrc
  • addons.js
  • config.js
  • parts of the @storybook/app api, like a configure method, that is used in config.js

@ndelangen ndelangen changed the title Config V2 (monoconfig) Config V2: storybook.config.js - Monoconfig / Single Configuration File May 23, 2019
This was referenced Jun 3, 2019
@ericberens
Copy link

I'd be keen to know the execution lifecycle of each hook. Currently hitting against a wall where I want to use webpack aliases in a custom addon but cannot because the addon hook is executed before the manager\preview webpack hook is called to setup aliases.

This may be good for standalone presets, addons, etc. that are done in isolation but for an internal app where I'd like to reuse code (without unnecessary abstraction) and a centralized place for configuration of Storybook I cannot. Currently I am forced to use my custom preset to hook into the manager webpack config for a custom addon and the addons.js to register that addon.

@ndelangen
Copy link
Member Author

@ericberens I could show you how far along monoconfig currently is at and how it works, and what's left to do.

Send me a PM on discord if interested:
https://discord.gg/sMFvFsG

@jrwebdev
Copy link
Contributor

This sounds great - it looks like it would cover a need I have to share config between multiple projects. What would be the replacement for manager-head.html / preview-head.html with this approach?

@ndelangen
Copy link
Member Author

a simplified example of how to add stuff to the templates in monoconfig:

export managerTemplate = (template, config) => `
  ${template}

  my addition
`;

@sebastian-nowak
Copy link

It looks like an awesome, much needed update, thanks @ndelangen! Is there an estimate when it might land in 6.0-alpha so that we could play with it? ;)

@ndelangen
Copy link
Member Author

@sebastian-nowak We decided to slowly migrate to this over time instead of a big bang change.

Many things this introduced are now in a main.js file that is in the .storybook folder. Goal is to at some point change the many files into a single file storybook.config.js at the project root.

We'll get there eventually.

@shilman shilman modified the milestones: 6.0, 7.0 May 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants