导出故事书包中的单个组件

时间:2018-02-01 07:20:57

标签: webpack storybook

因此,在开发人员辞职后,我继承了一个小故事书库,而我的网络包/故事书并不是我的强项。

问题是将Storybook构建配置为输出可导出组件是多么容易。我想将我的Storybook组件(不是组件的故事,而是底层组件本身)转换为私有的npm包,这样我就可以将它们导出到其他项目中。

我的故事书是@storybook的反应3.2.11运行打字稿/ scss 我文件的结构看起来像这样...

src
    components
        Button
             Button.tsx
             button.scss
        Dropdown
        ..etc
    stories
        components
            Buttonstory
                 ButtonStory.tsx
        index.tsx

我的config.js看起来像这样......

import { configure } from '@storybook/react';

function loadStories() {
  require('../src/stories/index.tsx');
  // You can require as many stories as you need.
}

configure(loadStories, module);

和我的.storybook webpack.config.js看起来像这样。

const path = require("path");
const autoprefixer = require("autoprefixer");
const genDefaultConfig = 
require('@storybook/react/dist/server/config/defaults/webpack.config.js');
const stylelint = require('stylelint');
const stylelintPlugin = require("stylelint-webpack-plugin");

module.exports = (baseConfig, env) => {
    const config = genDefaultConfig(baseConfig, env);

    // Reset rules
    config.module.rules = [];

    /**
     * Typescript handling
     * Allows webpack to process ts and tsx files using the typescript compiler.
     */
    config.module.rules.push({
        test: /\.(tsx?)$/,
        enforce: "pre",
        loader: require.resolve("tslint-loader")
    });

    config.module.rules.push({
        test: /\.(tsx?)$/,
        loader: require.resolve('awesome-typescript-loader')
    });
    config.resolve.extensions.push('.ts', '.tsx');

    /**
     * Style handling
     * 
     * There are four loaders for handling the styling import process their hierachy is as follows:
     * 
     * sass-loader -> postcss -> css-loader -> style-loader
     */
    config.module.rules.push({
        test: /\.(scss)$/,
        use: [
            require.resolve('style-loader'),
            {
                loader: require.resolve('css-loader'),
                options: {
                    importLoaders: 2
                }
            },
            {
                loader: require.resolve('postcss-loader'),
                options: {
                    ident: 'postcss',
                    sourceMap: true,
                    plugins: function plugins() {
                        return [
                            require('postcss-flexbugs-fixes'),
                            autoprefixer({
                                browsers: ['>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9'],
                                flexbox: 'no-2009'
                            })
                        ]
                    }
                }
            },
            {
                loader: require.resolve('sass-loader'),
                options: {
                    sourceMap: true
                }
            },
        ]
    })

    /**
     * Begin storybook defaults
     * These are the storybook defaults that we have not written over. Pretty much everything except
     * styling loader and imports.
     */
    config.module.rules = config.module.rules.concat([
        {
            test: /\.json$/,
            loader: require.resolve('json-loader')
        }, 
        {
            test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
            loader: require.resolve('file-loader'),
            query: {
                name: 'static/media/[name].[hash:8].[ext]'
            }
        }, 
        {
            test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
            loader: require.resolve('url-loader'),
            query: {
                limit: 10000,
                name: 'static/media/[name].[hash:8].[ext]'
            }
        }
    ]);

    /**
     * Add stylelint as a plugin, doing this because you need this to run before postCSS and as of the
     * of this writing I couldn't be bothered reconfiguring postCSS to process the SCSS.
     */
    config.plugins.push(new stylelintPlugin({
        files: "**/*.scss",
        emitErrors: false,
        failOnError: false
    }));

    return config;
};

想知道是否有人设置了类似的东西

1 个答案:

答案 0 :(得分:2)

根据要求的评论,我在不久前解决了这个问题,不得不学习一些webpack,但基本上最后的结果是我现在有一个 故事书 - &gt; private npm repository - &gt;其他项目管道。 这是我如何解决它。

.storybook .webpack.config.js就是这个。

const genDefaultConfig = require('@storybook/react/dist/server/config/defaults/webpack.config.js');
const productionBuild = require("./webpack.prod");
const developmentBuild = require("./webpack.dev");

module.exports = (baseConfig, env) => {
    const config = genDefaultConfig(baseConfig, env);
    console.log(env);
    config.module.rules = [];

    if(env === "PRODUCTION") return productionBuild(config);
    if(env === "DEVELOPMENT") return developmentBuild(config);

    return config;
};

这将检查开发或生产版本,然后通过适当的文件运行它。 (我只是展示制作)

const ExtractTextPlugin = require("extract-text-webpack-plugin");
// const DashboardPlugin = require('webpack-dashboard/plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const autoprefixer = require("autoprefixer");
const { TsConfigPathsPlugin } = require('awesome-typescript-loader');

module.exports = function(config) {
    config.entry = {};   // remove this line if you want storybook js files in output.
    config.entry["Components"] = ['./src/main.tsx'];

    config.output = {
        filename: 'dist/[name].bundle.js',
        publicPath: '',
        libraryTarget: "commonjs"
    };
+
    config.module.rules.push({
        test: /\.(tsx?)$/,
        enforce: "pre",
        loader: require.resolve("tslint-loader")
    });

    config.plugins.push(new TsConfigPathsPlugin())

    config.module.rules.push({
        test: /\.(tsx?)$/,
        loader: require.resolve('awesome-typescript-loader'),
        options: {
            configFileName: 'tsconfig.prod.json'
        }
    });

    config.module.rules.push({
        test: /\.(scss)$/,
        use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: [
                {
                    loader: 'css-loader',
                    options: {
                        url: false,
                        minimize: true,
                        sourceMap: false
                    }
                },
                {
                    loader: require.resolve('postcss-loader'),
                    options: {
                        ident: 'postcss',
                        sourceMap: false,
                        plugins: function plugins() {
                            return [
                                require('postcss-flexbugs-fixes'),
                                autoprefixer({
                                    browsers: ['>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9'],
                                    flexbox: 'no-2009'
                                })
                            ]
                        }
                    }
                },
                {
                    loader: require.resolve('sass-loader'),
                    options: {
                        sourceMap: false
                    }
                },
            ]
        })
    });

    config.module.rules = config.module.rules.concat([
        {
            test: /\.json$/,
            loader: require.resolve('json-loader')
        },
        {
            test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
            loader: require.resolve('file-loader'),
            query: {
                name: 'static/media/[name].[hash:8].[ext]'
            }
        },
        {
            test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
            loader: require.resolve('url-loader'),
            query: {
                limit: 10000,
                name: 'static/media/[name].[hash:8].[ext]'
            }
        }
    ]);

    config.resolve.extensions.push('.ts', '.tsx');

    config.externals = {
        react: {
            root: 'React',
            commonjs2: 'react',
            commonjs: 'react',
            amd: 'react'
        },
        'react-dom': {
            root: 'ReactDOM',
            commonjs2: 'react-dom',
            commonjs: 'react-dom',
            amd: 'react-dom'
        }
    };

    config.plugins.push(new UglifyJSPlugin());

    config.plugins.push(new ExtractTextPlugin({
        filename: '[name].css',
        disable: false,
        allChunks: true
    }));


    /* unneccessary logic below is to stop webpack dashboard stalling the production build  */
    // config.plugins.push({
    //      apply: function() {
    //          const dashboard = new DashboardPlugin({color: "cyan"});
    //          dashboard.apply.apply(dashboard, arguments)
    //      }
    //  }
    // );

    return config;
};

^上面的代码是.storybook - &gt; webpack.prod.js 基本上它所做的是覆盖故事书使用的webpack的入口点作为默认值,如果你想要故事书资产,我建议你推送到config.entry而不是覆盖它。

webpack的作用是遍历依赖关系树,因此Main.tsx是一个单独的文件,只包含我想要导出的组件,所以我的包没有任何臃肿。

最后,有一些小的优化措施可以减少捆绑包的大小 (1) - Tree Shaking删除不可用的导出 (2) - ExtractTextPlugin获取单独的CSS文件 (3) - config.externals React和ReactDOM这意味着输出包不包含React或ReactDOM,而是在IMPORT INTO&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; (重要)

以下是Main.tsx(webpack入口点)

export { BEMHelper } from "./utils/bem-helper/bem-helper";
export { Button } from "./components/Button/Button";
export { Checkbox } from "./components/Checkbox/Checkbox";
...etc etc etc repeat for every component you want to export in your bundle
import "./styles/bootstrap.scss";

最后如果你在你的打字稿配置中使用commonJS,这将是你的index.js(你的NPM包需要的)

export const BEMHelper = require("./storybook-static/dist/Components.bundle").BEMHelper;
export const Button = require('./storybook-static/dist/Components.bundle').Button;
..... etc etc etc. (repeat)