From df1fa10a02c848c8ccb90e2edeeac69e7ddf253e Mon Sep 17 00:00:00 2001
From: Thomas Bertet
Date: Thu, 31 Aug 2017 08:40:35 +0200
Subject: [PATCH 1/8] load chunks after the preview bundle
---
app/react/src/server/iframe.html.js | 5 ++-
app/react/src/server/iframe.html.test.js | 39 +++++++++++++++++++++++-
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/app/react/src/server/iframe.html.js b/app/react/src/server/iframe.html.js
index 9b9fdc020f5..2c8d390a590 100644
--- a/app/react/src/server/iframe.html.js
+++ b/app/react/src/server/iframe.html.js
@@ -2,6 +2,8 @@ import url from 'url';
const getExtensionForFilename = filename => /.+\.(\w+)$/.exec(filename)[1];
+export const isPreviewAsset = filename => filename.indexOf('preview.bundle.js') >= 0;
+
// assets.preview will be:
// - undefined
// - string e.g. 'static/preview.9adbb5ef965106be1cc3.bundle.js'
@@ -39,7 +41,8 @@ export const urlsFromAssets = assets => {
return isSupportedExtension && !isMap;
})
.forEach(assetUrl => {
- urls[getExtensionForFilename(assetUrl)].push(assetUrl);
+ const method = isPreviewAsset(assetUrl) ? 'unshift' : 'push';
+ urls[getExtensionForFilename(assetUrl)][method](assetUrl);
});
});
diff --git a/app/react/src/server/iframe.html.test.js b/app/react/src/server/iframe.html.test.js
index b57e2b9bba0..66a097d6620 100644
--- a/app/react/src/server/iframe.html.test.js
+++ b/app/react/src/server/iframe.html.test.js
@@ -1,4 +1,4 @@
-import { urlsFromAssets } from './iframe.html';
+import { urlsFromAssets, isPreviewAsset } from './iframe.html';
describe('server.urlsFromAssets', () => {
it('should return the default when there are no assets', () => {
@@ -28,4 +28,41 @@ describe('server.urlsFromAssets', () => {
css: [],
});
});
+
+ it('should put the JS preview bundle first, before other chunks', () => {
+ const fixture = {
+ manager: 'static/manager.a.bundle.js',
+ preview: [
+ 'static/0.bundle.js',
+ 'static/2.bundle.js',
+ 'static/3.bundle.js',
+ 'static/4.bundle.js',
+ 'static/preview.bundle.js',
+ 'static/5.bundle.js',
+ 'static/6.bundle.js',
+ ],
+ };
+ expect(urlsFromAssets(fixture)).toEqual({
+ js: [
+ 'static/preview.bundle.js',
+ 'static/0.bundle.js',
+ 'static/2.bundle.js',
+ 'static/3.bundle.js',
+ 'static/4.bundle.js',
+ 'static/5.bundle.js',
+ 'static/6.bundle.js',
+ ],
+ css: [],
+ });
+ });
+});
+
+describe('server.isPreviewAsset', () => {
+ it('should return true when this is the preview bundle', () => {
+ expect(isPreviewAsset('static/preview.bundle.js')).toBe(true);
+ });
+
+ it('should return false when this is NOT the preview bundle', () => {
+ expect(isPreviewAsset('static/some.other.bundle.js')).toBe(false);
+ });
});
From f42248157b3cf9b49de98ea9bca1f88495d6d58a Mon Sep 17 00:00:00 2001
From: Thomas Bertet
Date: Fri, 1 Sep 2017 18:29:11 +0200
Subject: [PATCH 2/8] use HtmlWebpackPlugin & custom webpack.config.js in
cra-kitchen-sink
---
app/react/package.json | 1 +
app/react/src/server/build.js | 19 +----
app/react/src/server/config/utils.js | 10 ++-
app/react/src/server/config/webpack.config.js | 30 ++++++-
.../src/server/config/webpack.config.prod.js | 22 ++++-
app/react/src/server/iframe.html.ejs | 19 +++++
app/react/src/server/iframe.html.js | 85 -------------------
app/react/src/server/index.html.ejs | 44 ++++++++++
app/react/src/server/index.html.js | 79 -----------------
app/react/src/server/middleware.js | 18 ++--
.../.storybook/webpack.config.js | 29 +++++++
11 files changed, 157 insertions(+), 199 deletions(-)
create mode 100644 app/react/src/server/iframe.html.ejs
delete mode 100644 app/react/src/server/iframe.html.js
create mode 100644 app/react/src/server/index.html.ejs
delete mode 100644 app/react/src/server/index.html.js
create mode 100644 examples/cra-kitchen-sink/.storybook/webpack.config.js
diff --git a/app/react/package.json b/app/react/package.json
index 390ef2fef27..f3d93e24cee 100644
--- a/app/react/package.json
+++ b/app/react/package.json
@@ -50,6 +50,7 @@
"glamor": "^2.20.40",
"glamorous": "^4.1.2",
"global": "^4.3.2",
+ "html-webpack-plugin": "^2.30.1",
"json-loader": "^0.5.4",
"json-stringify-safe": "^5.0.1",
"json5": "^0.5.1",
diff --git a/app/react/src/server/build.js b/app/react/src/server/build.js
index 884b9a7188a..99df39a8f57 100755
--- a/app/react/src/server/build.js
+++ b/app/react/src/server/build.js
@@ -9,9 +9,7 @@ import shelljs from 'shelljs';
import packageJson from '../../package.json';
import getBaseConfig from './config/webpack.config.prod';
import loadConfig from './config';
-import getIndexHtml from './index.html';
-import getIframeHtml from './iframe.html';
-import { getPreviewHeadHtml, getManagerHeadHtml, parseList, getEnvConfig } from './utils';
+import { parseList, getEnvConfig } from './utils';
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
@@ -86,19 +84,4 @@ webpack(config).run((err, stats) => {
stats.hasErrors() && stats.toJson().errors.forEach(e => logger.error(e));
process.exit(1);
}
-
- const data = {
- publicPath: config.output.publicPath,
- assets: stats.toJson().assetsByChunkName,
- };
-
- // Write both the storybook UI and IFRAME HTML files to destination path.
- fs.writeFileSync(
- path.resolve(outputDir, 'index.html'),
- getIndexHtml({ ...data, headHtml: getManagerHeadHtml(configDir) })
- );
- fs.writeFileSync(
- path.resolve(outputDir, 'iframe.html'),
- getIframeHtml({ ...data, headHtml: getPreviewHeadHtml(configDir) })
- );
});
diff --git a/app/react/src/server/config/utils.js b/app/react/src/server/config/utils.js
index 8cb15c640d9..0236481efd7 100644
--- a/app/react/src/server/config/utils.js
+++ b/app/react/src/server/config/utils.js
@@ -23,11 +23,15 @@ export function loadEnv(options = {}) {
PUBLIC_URL: JSON.stringify(options.production ? '.' : ''),
};
- Object.keys(process.env).filter(name => /^STORYBOOK_/.test(name)).forEach(name => {
- env[name] = JSON.stringify(process.env[name]);
- });
+ Object.keys(process.env)
+ .filter(name => /^STORYBOOK_/.test(name))
+ .forEach(name => {
+ env[name] = JSON.stringify(process.env[name]);
+ });
return {
'process.env': env,
};
}
+
+export const getConfigDir = () => process.env.SBCONFIG_CONFIG_DIR || './.storybook';
diff --git a/app/react/src/server/config/webpack.config.js b/app/react/src/server/config/webpack.config.js
index 24eacd0bf24..d90af4f9c91 100644
--- a/app/react/src/server/config/webpack.config.js
+++ b/app/react/src/server/config/webpack.config.js
@@ -1,9 +1,20 @@
import path from 'path';
import webpack from 'webpack';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
import WatchMissingNodeModulesPlugin from './WatchMissingNodeModulesPlugin';
-import { includePaths, excludePaths, nodeModulesPaths, loadEnv, nodePaths } from './utils';
+
+import {
+ getConfigDir,
+ includePaths,
+ excludePaths,
+ nodeModulesPaths,
+ loadEnv,
+ nodePaths,
+} from './utils';
import babelLoaderConfig from './babel';
+import { getPreviewHeadHtml, getManagerHeadHtml } from '../utils';
+import { version } from '../../../package.json';
export default function() {
const config = {
@@ -22,6 +33,23 @@ export default function() {
publicPath: '/',
},
plugins: [
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ chunks: ['manager'],
+ data: {
+ managerHead: getManagerHeadHtml(getConfigDir()),
+ version,
+ },
+ template: require.resolve('../index.html.ejs'),
+ }),
+ new HtmlWebpackPlugin({
+ filename: 'iframe.html',
+ excludeChunks: ['manager'],
+ data: {
+ previewHead: getPreviewHeadHtml(getConfigDir()),
+ },
+ template: require.resolve('../iframe.html.ejs'),
+ }),
new webpack.DefinePlugin(loadEnv()),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
diff --git a/app/react/src/server/config/webpack.config.prod.js b/app/react/src/server/config/webpack.config.prod.js
index 989b1adca7a..12a2793137f 100644
--- a/app/react/src/server/config/webpack.config.prod.js
+++ b/app/react/src/server/config/webpack.config.prod.js
@@ -1,7 +1,10 @@
import path from 'path';
import webpack from 'webpack';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
import babelLoaderConfig from './babel.prod';
-import { includePaths, excludePaths, loadEnv, nodePaths } from './utils';
+import { getConfigDir, includePaths, excludePaths, loadEnv, nodePaths } from './utils';
+import { getPreviewHeadHtml, getManagerHeadHtml } from '../utils';
+import { version } from '../../../package.json';
export default function() {
const entries = {
@@ -23,6 +26,23 @@ export default function() {
publicPath: '',
},
plugins: [
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ chunks: ['manager'],
+ data: {
+ managerHead: getManagerHeadHtml(getConfigDir()),
+ version,
+ },
+ template: require.resolve('../index.html.ejs'),
+ }),
+ new HtmlWebpackPlugin({
+ filename: 'iframe.html',
+ excludeChunks: ['manager'],
+ data: {
+ previewHead: getPreviewHeadHtml(getConfigDir()),
+ },
+ template: require.resolve('../iframe.html.ejs'),
+ }),
new webpack.DefinePlugin(loadEnv({ production: true })),
new webpack.optimize.UglifyJsPlugin({
compress: {
diff --git a/app/react/src/server/iframe.html.ejs b/app/react/src/server/iframe.html.ejs
new file mode 100644
index 00000000000..55723c849e3
--- /dev/null
+++ b/app/react/src/server/iframe.html.ejs
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Storybook
+ <%= htmlWebpackPlugin.options.data.previewHead %>
+
+
+
+
+
+
diff --git a/app/react/src/server/iframe.html.js b/app/react/src/server/iframe.html.js
deleted file mode 100644
index 2c8d390a590..00000000000
--- a/app/react/src/server/iframe.html.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import url from 'url';
-
-const getExtensionForFilename = filename => /.+\.(\w+)$/.exec(filename)[1];
-
-export const isPreviewAsset = filename => filename.indexOf('preview.bundle.js') >= 0;
-
-// assets.preview will be:
-// - undefined
-// - string e.g. 'static/preview.9adbb5ef965106be1cc3.bundle.js'
-// - array of strings e.g.
-// [ 'static/preview.9adbb5ef965106be1cc3.bundle.js',
-// 'preview.0d2d3d845f78399fd6d5e859daa152a9.css',
-// 'static/preview.9adbb5ef965106be1cc3.bundle.js.map',
-// 'preview.0d2d3d845f78399fd6d5e859daa152a9.css.map' ]
-export const urlsFromAssets = assets => {
- if (!assets) {
- return {
- js: ['static/preview.bundle.js'],
- css: [],
- };
- }
-
- const urls = {
- js: [],
- css: [],
- };
-
- Object.keys(assets)
- // Don't load the manager script in the iframe
- .filter(key => key !== 'manager')
- .forEach(key => {
- let assetList = assets[key];
- if (!Array.isArray(assetList)) {
- assetList = [assetList];
- }
- assetList
- .filter(assetUrl => {
- const extension = getExtensionForFilename(assetUrl);
- const isMap = extension === 'map';
- const isSupportedExtension = Boolean(urls[extension]);
- return isSupportedExtension && !isMap;
- })
- .forEach(assetUrl => {
- const method = isPreviewAsset(assetUrl) ? 'unshift' : 'push';
- urls[getExtensionForFilename(assetUrl)][method](assetUrl);
- });
- });
-
- return urls;
-};
-
-export default function({ assets, publicPath, headHtml }) {
- const urls = urlsFromAssets(assets);
-
- const cssTags = urls.css
- .map(u => ``)
- .join('\n');
- const scriptTags = urls.js
- .map(u => ``)
- .join('\n');
-
- return `
-
-
-
-
-
-
-
- Storybook
- ${headHtml}
- ${cssTags}
-
-
-
-
- ${scriptTags}
-
-
- `;
-}
diff --git a/app/react/src/server/index.html.ejs b/app/react/src/server/index.html.ejs
new file mode 100644
index 00000000000..397aaf41d52
--- /dev/null
+++ b/app/react/src/server/index.html.ejs
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+ Storybook
+
+ <%= htmlWebpackPlugin.options.data.managerHead %>
+
+
+
+
+
+
diff --git a/app/react/src/server/index.html.js b/app/react/src/server/index.html.js
deleted file mode 100644
index dc5b0afc42b..00000000000
--- a/app/react/src/server/index.html.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import url from 'url';
-import { version } from '../../package.json';
-
-// assets.manager will be:
-// - undefined
-// - string e.g. 'static/manager.9adbb5ef965106be1cc3.bundle.js'
-// - array of strings e.g.
-// assets.manager will be something like:
-// [ 'static/manager.c6e6350b6eb01fff8bad.bundle.js',
-// 'static/manager.c6e6350b6eb01fff8bad.bundle.js.map' ]
-const managerUrlsFromAssets = assets => {
- if (!assets || !assets.manager) {
- return {
- js: 'static/manager.bundle.js',
- };
- }
-
- if (typeof assets.manager === 'string') {
- return {
- js: assets.manager,
- };
- }
-
- return {
- js: assets.manager.find(filename => filename.match(/\.js$/)),
- css: assets.manager.find(filename => filename.match(/\.css$/)),
- };
-};
-
-export default function({ assets, publicPath, headHtml }) {
- const managerUrls = managerUrlsFromAssets(assets);
-
- return `
-
-
-
-
-
-
-
- Storybook
-
- ${headHtml}
-
-
-
-
-
-
- `;
-}
diff --git a/app/react/src/server/middleware.js b/app/react/src/server/middleware.js
index 7ebeb18be47..e5b0ed9b8e4 100644
--- a/app/react/src/server/middleware.js
+++ b/app/react/src/server/middleware.js
@@ -1,12 +1,11 @@
+import path from 'path';
import { Router } from 'express';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import getBaseConfig from './config/webpack.config';
import loadConfig from './config';
-import getIndexHtml from './index.html';
-import getIframeHtml from './iframe.html';
-import { getPreviewHeadHtml, getManagerHeadHtml, getMiddleware } from './utils';
+import { getMiddleware } from './utils';
let webpackResolve = () => {};
let webpackReject = () => {};
@@ -44,19 +43,14 @@ export default function(configDir) {
middlewareFn(router);
webpackDevMiddlewareInstance.waitUntilValid(stats => {
- const data = {
- publicPath: config.output.publicPath,
- assets: stats.toJson().assetsByChunkName,
- };
-
router.get('/', (req, res) => {
- const headHtml = getManagerHeadHtml(configDir);
- res.send(getIndexHtml({ publicPath, headHtml }));
+ res.set('Content-Type', 'text/html');
+ res.sendFile(path.join(`${__dirname}/public/index.html`));
});
router.get('/iframe.html', (req, res) => {
- const headHtml = getPreviewHeadHtml(configDir);
- res.send(getIframeHtml({ ...data, headHtml, publicPath }));
+ res.set('Content-Type', 'text/html');
+ res.sendFile(path.join(`${__dirname}/public/iframe.html`));
});
if (stats.toJson().errors.length) {
diff --git a/examples/cra-kitchen-sink/.storybook/webpack.config.js b/examples/cra-kitchen-sink/.storybook/webpack.config.js
new file mode 100644
index 00000000000..3ced6fa641b
--- /dev/null
+++ b/examples/cra-kitchen-sink/.storybook/webpack.config.js
@@ -0,0 +1,29 @@
+const path = require('path');
+const webpack = require('webpack');
+
+// load the default config generator.
+const genDefaultConfig = require('@storybook/react/dist/server/config/defaults/webpack.config.js');
+
+// Export a function. Accept the base config as the only param.
+module.exports = (storybookBaseConfig, configType) => {
+ // configType has a value of 'DEVELOPMENT' or 'PRODUCTION'
+ // You can change the configuration based on that.
+ // 'PRODUCTION' is used when building the static version of storybook.
+
+ const config = genDefaultConfig(storybookBaseConfig, configType);
+
+ // Make whatever fine-grained changes you need
+ config.plugins.push(
+ new webpack.optimize.CommonsChunkPlugin({
+ name: "vendor",
+ chunks: ['preview'],
+ minChunks: function (module) {
+ // this assumes your vendor imports exist in the node_modules directory
+ return module.context && module.context.indexOf("node_modules") !== -1;
+ },
+ })
+ );
+
+ // Return the altered config
+ return config;
+};
From 174db1aa0bb2965e7d268e6e85eb8ff323f55a52 Mon Sep 17 00:00:00 2001
From: Thomas Bertet
Date: Fri, 1 Sep 2017 18:35:05 +0200
Subject: [PATCH 3/8] remove useless test file since HtmlWebpackPlugin is
taking over
---
app/react/src/server/iframe.html.test.js | 68 ------------------------
1 file changed, 68 deletions(-)
delete mode 100644 app/react/src/server/iframe.html.test.js
diff --git a/app/react/src/server/iframe.html.test.js b/app/react/src/server/iframe.html.test.js
deleted file mode 100644
index 66a097d6620..00000000000
--- a/app/react/src/server/iframe.html.test.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { urlsFromAssets, isPreviewAsset } from './iframe.html';
-
-describe('server.urlsFromAssets', () => {
- it('should return the default when there are no assets', () => {
- expect(urlsFromAssets()).toEqual({
- js: ['static/preview.bundle.js'],
- css: [],
- });
- });
-
- it('should return multiple assets', () => {
- const fixture = {
- manager: 'static/manager.a.bundle.js',
- preview: ['static/preview.x.bundle.js', 'static/preview.y.css', 'static/preview.y.css.map'],
- };
- expect(urlsFromAssets(fixture)).toEqual({
- js: ['static/preview.x.bundle.js'],
- css: ['static/preview.y.css'],
- });
- });
-
- it('should not return non-js or non-css assets', () => {
- const fixture = {
- 'some-thing.svg': 'some-thing.svg',
- };
- expect(urlsFromAssets(fixture)).toEqual({
- js: [],
- css: [],
- });
- });
-
- it('should put the JS preview bundle first, before other chunks', () => {
- const fixture = {
- manager: 'static/manager.a.bundle.js',
- preview: [
- 'static/0.bundle.js',
- 'static/2.bundle.js',
- 'static/3.bundle.js',
- 'static/4.bundle.js',
- 'static/preview.bundle.js',
- 'static/5.bundle.js',
- 'static/6.bundle.js',
- ],
- };
- expect(urlsFromAssets(fixture)).toEqual({
- js: [
- 'static/preview.bundle.js',
- 'static/0.bundle.js',
- 'static/2.bundle.js',
- 'static/3.bundle.js',
- 'static/4.bundle.js',
- 'static/5.bundle.js',
- 'static/6.bundle.js',
- ],
- css: [],
- });
- });
-});
-
-describe('server.isPreviewAsset', () => {
- it('should return true when this is the preview bundle', () => {
- expect(isPreviewAsset('static/preview.bundle.js')).toBe(true);
- });
-
- it('should return false when this is NOT the preview bundle', () => {
- expect(isPreviewAsset('static/some.other.bundle.js')).toBe(false);
- });
-});
From e3020ce291fdc8263b47546d2b55211e0341d274 Mon Sep 17 00:00:00 2001
From: Thomas Bertet
Date: Mon, 4 Sep 2017 09:01:28 +0200
Subject: [PATCH 4/8] use HtmlWebpackPlugin for vue
---
app/vue/package.json | 1 +
app/vue/src/server/build.js | 26 ++----
app/vue/src/server/config/utils.js | 10 ++-
app/vue/src/server/config/webpack.config.js | 29 ++++++-
.../src/server/config/webpack.config.prod.js | 22 ++++-
app/vue/src/server/iframe.html.ejs | 20 +++++
app/vue/src/server/iframe.html.js | 87 -------------------
app/vue/src/server/index.html.ejs | 44 ++++++++++
app/vue/src/server/index.html.js | 79 -----------------
app/vue/src/server/middleware.js | 18 ++--
10 files changed, 133 insertions(+), 203 deletions(-)
create mode 100644 app/vue/src/server/iframe.html.ejs
delete mode 100644 app/vue/src/server/iframe.html.js
create mode 100644 app/vue/src/server/index.html.ejs
delete mode 100644 app/vue/src/server/index.html.js
diff --git a/app/vue/package.json b/app/vue/package.json
index 3bec1d4a754..d82d06d0779 100644
--- a/app/vue/package.json
+++ b/app/vue/package.json
@@ -48,6 +48,7 @@
"file-loader": "^0.11.1",
"find-cache-dir": "^1.0.0",
"global": "^4.3.2",
+ "html-webpack-plugin": "^2.30.1",
"json-loader": "^0.5.4",
"json-stringify-safe": "^5.0.1",
"json5": "^0.5.1",
diff --git a/app/vue/src/server/build.js b/app/vue/src/server/build.js
index b2a598b3e80..99df39a8f57 100755
--- a/app/vue/src/server/build.js
+++ b/app/vue/src/server/build.js
@@ -9,9 +9,7 @@ import shelljs from 'shelljs';
import packageJson from '../../package.json';
import getBaseConfig from './config/webpack.config.prod';
import loadConfig from './config';
-import getIndexHtml from './index.html';
-import getIframeHtml from './iframe.html';
-import { getPreviewHeadHtml, getManagerHeadHtml, parseList, getEnvConfig } from './utils';
+import { parseList, getEnvConfig } from './utils';
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
@@ -78,24 +76,12 @@ if (program.staticDir) {
// compile all resources with webpack and write them to the disk.
logger.log('Building storybook ...');
webpack(config).run((err, stats) => {
- if (err) {
+ if (err || stats.hasErrors()) {
logger.error('Failed to build the storybook');
- logger.error(err.message);
+ // eslint-disable-next-line no-unused-expressions
+ err && logger.error(err.message);
+ // eslint-disable-next-line no-unused-expressions
+ stats.hasErrors() && stats.toJson().errors.forEach(e => logger.error(e));
process.exit(1);
}
-
- const data = {
- publicPath: config.output.publicPath,
- assets: stats.toJson().assetsByChunkName,
- };
-
- // Write both the storybook UI and IFRAME HTML files to destination path.
- fs.writeFileSync(
- path.resolve(outputDir, 'index.html'),
- getIndexHtml({ ...data, headHtml: getManagerHeadHtml(configDir) })
- );
- fs.writeFileSync(
- path.resolve(outputDir, 'iframe.html'),
- getIframeHtml({ ...data, headHtml: getPreviewHeadHtml(configDir) })
- );
});
diff --git a/app/vue/src/server/config/utils.js b/app/vue/src/server/config/utils.js
index 8cb15c640d9..0236481efd7 100644
--- a/app/vue/src/server/config/utils.js
+++ b/app/vue/src/server/config/utils.js
@@ -23,11 +23,15 @@ export function loadEnv(options = {}) {
PUBLIC_URL: JSON.stringify(options.production ? '.' : ''),
};
- Object.keys(process.env).filter(name => /^STORYBOOK_/.test(name)).forEach(name => {
- env[name] = JSON.stringify(process.env[name]);
- });
+ Object.keys(process.env)
+ .filter(name => /^STORYBOOK_/.test(name))
+ .forEach(name => {
+ env[name] = JSON.stringify(process.env[name]);
+ });
return {
'process.env': env,
};
}
+
+export const getConfigDir = () => process.env.SBCONFIG_CONFIG_DIR || './.storybook';
diff --git a/app/vue/src/server/config/webpack.config.js b/app/vue/src/server/config/webpack.config.js
index 10d1a6c9c1b..c5fe4cdd631 100644
--- a/app/vue/src/server/config/webpack.config.js
+++ b/app/vue/src/server/config/webpack.config.js
@@ -1,9 +1,19 @@
import path from 'path';
import webpack from 'webpack';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
import WatchMissingNodeModulesPlugin from './WatchMissingNodeModulesPlugin';
-import { includePaths, excludePaths, nodeModulesPaths, loadEnv, nodePaths } from './utils';
+import {
+ getConfigDir,
+ includePaths,
+ excludePaths,
+ nodeModulesPaths,
+ loadEnv,
+ nodePaths,
+} from './utils';
+import { getPreviewHeadHtml, getManagerHeadHtml } from '../utils';
import babelLoaderConfig from './babel';
+import { version } from '../../../package.json';
export default function() {
const config = {
@@ -22,6 +32,23 @@ export default function() {
publicPath: '/',
},
plugins: [
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ chunks: ['manager'],
+ data: {
+ managerHead: getManagerHeadHtml(getConfigDir()),
+ version,
+ },
+ template: require.resolve('../index.html.ejs'),
+ }),
+ new HtmlWebpackPlugin({
+ filename: 'iframe.html',
+ excludeChunks: ['manager'],
+ data: {
+ previewHead: getPreviewHeadHtml(getConfigDir()),
+ },
+ template: require.resolve('../iframe.html.ejs'),
+ }),
new webpack.DefinePlugin(loadEnv()),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
diff --git a/app/vue/src/server/config/webpack.config.prod.js b/app/vue/src/server/config/webpack.config.prod.js
index 1260b9de6dc..f4fca7240d7 100644
--- a/app/vue/src/server/config/webpack.config.prod.js
+++ b/app/vue/src/server/config/webpack.config.prod.js
@@ -1,7 +1,10 @@
import path from 'path';
import webpack from 'webpack';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
import babelLoaderConfig from './babel.prod';
-import { includePaths, excludePaths, loadEnv, nodePaths } from './utils';
+import { getConfigDir, includePaths, excludePaths, loadEnv, nodePaths } from './utils';
+import { getPreviewHeadHtml, getManagerHeadHtml } from '../utils';
+import { version } from '../../../package.json';
export default function() {
const entries = {
@@ -23,6 +26,23 @@ export default function() {
publicPath: '',
},
plugins: [
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ chunks: ['manager'],
+ data: {
+ managerHead: getManagerHeadHtml(getConfigDir()),
+ version,
+ },
+ template: require.resolve('../index.html.ejs'),
+ }),
+ new HtmlWebpackPlugin({
+ filename: 'iframe.html',
+ excludeChunks: ['manager'],
+ data: {
+ previewHead: getPreviewHeadHtml(getConfigDir()),
+ },
+ template: require.resolve('../iframe.html.ejs'),
+ }),
new webpack.DefinePlugin(loadEnv({ production: true })),
new webpack.optimize.UglifyJsPlugin({
compress: {
diff --git a/app/vue/src/server/iframe.html.ejs b/app/vue/src/server/iframe.html.ejs
new file mode 100644
index 00000000000..32318e29e95
--- /dev/null
+++ b/app/vue/src/server/iframe.html.ejs
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ Storybook
+ <%= htmlWebpackPlugin.options.data.previewHead %>
+
+
+
+
+
+
diff --git a/app/vue/src/server/iframe.html.js b/app/vue/src/server/iframe.html.js
deleted file mode 100644
index bfcc2db58ee..00000000000
--- a/app/vue/src/server/iframe.html.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import url from 'url';
-
-const getExtensionForFilename = filename => /.+\.(\w+)$/.exec(filename)[1];
-
-// assets.preview will be:
-// - undefined
-// - string e.g. 'static/preview.9adbb5ef965106be1cc3.bundle.js'
-// - array of strings e.g.
-// [ 'static/preview.9adbb5ef965106be1cc3.bundle.js',
-// 'preview.0d2d3d845f78399fd6d5e859daa152a9.css',
-// 'static/preview.9adbb5ef965106be1cc3.bundle.js.map',
-// 'preview.0d2d3d845f78399fd6d5e859daa152a9.css.map' ]
-const urlsFromAssets = assets => {
- if (!assets) {
- return {
- js: ['static/preview.bundle.js'],
- css: [],
- };
- }
-
- const urls = {
- js: [],
- css: [],
- };
-
- Object.keys(assets)
- // Don't load the manager script in the iframe
- .filter(key => key !== 'manager')
- .forEach(key => {
- let asset = assets[key];
- if (typeof asset === 'string') {
- urls[getExtensionForFilename(asset)].push(asset);
- } else {
- if (!Array.isArray(asset)) {
- asset = [asset];
- }
- asset
- .filter(assetUrl => {
- const extension = getExtensionForFilename(assetUrl);
- const isMap = extension === 'map';
- const isSupportedExtension = Boolean(urls[extension]);
- return isSupportedExtension && !isMap;
- })
- .forEach(assetUrl => {
- urls[getExtensionForFilename(assetUrl)].push(assetUrl);
- });
- }
- });
-
- return urls;
-};
-
-export default function({ assets, publicPath, headHtml }) {
- const urls = urlsFromAssets(assets);
-
- const cssTags = urls.css
- .map(u => ``)
- .join('\n');
- const scriptTags = urls.js
- .map(u => ``)
- .join('\n');
-
- return `
-
-
-
-
-
-
-
- Storybook
- ${headHtml}
- ${cssTags}
-
-
-
-
- ${scriptTags}
-
-
- `;
-}
diff --git a/app/vue/src/server/index.html.ejs b/app/vue/src/server/index.html.ejs
new file mode 100644
index 00000000000..397aaf41d52
--- /dev/null
+++ b/app/vue/src/server/index.html.ejs
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+ Storybook
+
+ <%= htmlWebpackPlugin.options.data.managerHead %>
+
+
+
+
+
+
diff --git a/app/vue/src/server/index.html.js b/app/vue/src/server/index.html.js
deleted file mode 100644
index dc5b0afc42b..00000000000
--- a/app/vue/src/server/index.html.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import url from 'url';
-import { version } from '../../package.json';
-
-// assets.manager will be:
-// - undefined
-// - string e.g. 'static/manager.9adbb5ef965106be1cc3.bundle.js'
-// - array of strings e.g.
-// assets.manager will be something like:
-// [ 'static/manager.c6e6350b6eb01fff8bad.bundle.js',
-// 'static/manager.c6e6350b6eb01fff8bad.bundle.js.map' ]
-const managerUrlsFromAssets = assets => {
- if (!assets || !assets.manager) {
- return {
- js: 'static/manager.bundle.js',
- };
- }
-
- if (typeof assets.manager === 'string') {
- return {
- js: assets.manager,
- };
- }
-
- return {
- js: assets.manager.find(filename => filename.match(/\.js$/)),
- css: assets.manager.find(filename => filename.match(/\.css$/)),
- };
-};
-
-export default function({ assets, publicPath, headHtml }) {
- const managerUrls = managerUrlsFromAssets(assets);
-
- return `
-
-
-
-
-
-
-
- Storybook
-
- ${headHtml}
-
-
-
-
-
-
- `;
-}
diff --git a/app/vue/src/server/middleware.js b/app/vue/src/server/middleware.js
index 7ebeb18be47..cf03d060a59 100644
--- a/app/vue/src/server/middleware.js
+++ b/app/vue/src/server/middleware.js
@@ -1,12 +1,11 @@
import { Router } from 'express';
import webpack from 'webpack';
+import path from 'path';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import getBaseConfig from './config/webpack.config';
import loadConfig from './config';
-import getIndexHtml from './index.html';
-import getIframeHtml from './iframe.html';
-import { getPreviewHeadHtml, getManagerHeadHtml, getMiddleware } from './utils';
+import { getMiddleware } from './utils';
let webpackResolve = () => {};
let webpackReject = () => {};
@@ -44,19 +43,14 @@ export default function(configDir) {
middlewareFn(router);
webpackDevMiddlewareInstance.waitUntilValid(stats => {
- const data = {
- publicPath: config.output.publicPath,
- assets: stats.toJson().assetsByChunkName,
- };
-
router.get('/', (req, res) => {
- const headHtml = getManagerHeadHtml(configDir);
- res.send(getIndexHtml({ publicPath, headHtml }));
+ res.set('Content-Type', 'text/html');
+ res.sendFile(path.join(`${__dirname}/public/index.html`));
});
router.get('/iframe.html', (req, res) => {
- const headHtml = getPreviewHeadHtml(configDir);
- res.send(getIframeHtml({ ...data, headHtml, publicPath }));
+ res.set('Content-Type', 'text/html');
+ res.sendFile(path.join(`${__dirname}/public/iframe.html`));
});
if (stats.toJson().errors.length) {
From 5438db41863805066b2f14585d58f3237c0aad03 Mon Sep 17 00:00:00 2001
From: Norbert de Langen
Date: Mon, 4 Sep 2017 23:52:22 +0200
Subject: [PATCH 5/8] ADD custom webpack config for vue kitchen sink as with
cra-kitchen-sink
---
.../.storybook/webpack.config.js | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 examples/vue-kitchen-sink/.storybook/webpack.config.js
diff --git a/examples/vue-kitchen-sink/.storybook/webpack.config.js b/examples/vue-kitchen-sink/.storybook/webpack.config.js
new file mode 100644
index 00000000000..f7cbac74a0b
--- /dev/null
+++ b/examples/vue-kitchen-sink/.storybook/webpack.config.js
@@ -0,0 +1,29 @@
+const path = require('path');
+const webpack = require('webpack');
+
+// load the default config generator.
+const genDefaultConfig = require('@storybook/vue/dist/server/config/defaults/webpack.config.js');
+
+// Export a function. Accept the base config as the only param.
+module.exports = (storybookBaseConfig, configType) => {
+ // configType has a value of 'DEVELOPMENT' or 'PRODUCTION'
+ // You can change the configuration based on that.
+ // 'PRODUCTION' is used when building the static version of storybook.
+
+ const config = genDefaultConfig(storybookBaseConfig, configType);
+
+ // Make whatever fine-grained changes you need
+ config.plugins.push(
+ new webpack.optimize.CommonsChunkPlugin({
+ name: "vendor",
+ chunks: ['preview'],
+ minChunks: function (module) {
+ // this assumes your vendor imports exist in the node_modules directory
+ return module.context && module.context.indexOf("node_modules") !== -1;
+ },
+ })
+ );
+
+ // Return the altered config
+ return config;
+};
From 2b0644c1e7b72d6242f803322740b30b8f95b71d Mon Sep 17 00:00:00 2001
From: Norbert de Langen
Date: Mon, 4 Sep 2017 23:58:06 +0200
Subject: [PATCH 6/8] Fix for lint-staged see:
https://github.com/okonet/lint-staged/issues/225#issuecomment-327032465
---
package.json | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/package.json b/package.json
index 7fe0a0cc6ae..088d3c4821b 100644
--- a/package.json
+++ b/package.json
@@ -101,8 +101,7 @@
"git add"
]
},
- "verbose": true,
- "concurrent": false
+ "verbose": true
},
"pr-log": {
"skipLabels": [
From faa4803ca23a1c2ec40631555e410ca34a76d770 Mon Sep 17 00:00:00 2001
From: Norbert de Langen
Date: Wed, 6 Sep 2017 08:41:47 +0200
Subject: [PATCH 7/8] FIX snapshots
---
.../src/stories/__snapshots__/index.storyshot | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/examples/cra-kitchen-sink/src/stories/__snapshots__/index.storyshot b/examples/cra-kitchen-sink/src/stories/__snapshots__/index.storyshot
index a862be90e2b..76bd3f3d9be 100644
--- a/examples/cra-kitchen-sink/src/stories/__snapshots__/index.storyshot
+++ b/examples/cra-kitchen-sink/src/stories/__snapshots__/index.storyshot
@@ -150,7 +150,7 @@ exports[`Storyshots AddonInfo.DocgenButton DocgenButton 1`] = `
}
}
>
- Button with PropTypes and doc comments
+ Some Description
@@ -618,7 +618,7 @@ exports[`Storyshots AddonInfo.FlowTypeButton FlowTypeButton 1`] = `
}
}
>
- Button with Flow type documentation comments
+ Some Description
@@ -3118,9 +3118,9 @@ exports[`Storyshots WithEvents Logger 1`] = `
Object {
"color": "rgb(51, 51, 51)",
"fontFamily": "
- -apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", \\"Roboto\\",
- \\"Segoe UI\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", sans-serif
- ",
+ -apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", \\"Roboto\\",
+ \\"Segoe UI\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", sans-serif
+ ",
"padding": 20,
}
}
From bd6d8a64e62c60f34a632a83b5050e93e2a228f6 Mon Sep 17 00:00:00 2001
From: Norbert de Langen
Date: Wed, 6 Sep 2017 08:41:47 +0200
Subject: [PATCH 8/8] use HtmlWebpackPlugin for RN & CRNA
---
app/react-native/package.json | 1 +
.../src/server/config/webpack.config.js | 14 ++-
.../src/server/config/webpack.config.prod.js | 112 ++++++++++--------
app/react-native/src/server/index.html.ejs | 34 ++++++
app/react-native/src/server/index.html.js | 41 -------
app/react-native/src/server/middleware.js | 11 +-
.../.storybook/webpack.config.js | 1 -
examples/crna-kitchen-sink/package.json | 3 +-
.../storybook/webpack.config.js | 27 +++++
.../.storybook/webpack.config.js | 1 -
10 files changed, 140 insertions(+), 105 deletions(-)
create mode 100644 app/react-native/src/server/index.html.ejs
delete mode 100644 app/react-native/src/server/index.html.js
create mode 100644 examples/crna-kitchen-sink/storybook/webpack.config.js
diff --git a/app/react-native/package.json b/app/react-native/package.json
index d2d78b903fb..5716932f1b3 100644
--- a/app/react-native/package.json
+++ b/app/react-native/package.json
@@ -53,6 +53,7 @@
"file-loader": "^0.11.1",
"find-cache-dir": "^1.0.0",
"global": "^4.3.2",
+ "html-webpack-plugin": "^2.30.1",
"json-loader": "^0.5.4",
"json5": "^0.5.1",
"postcss-loader": "^2.0.5",
diff --git a/app/react-native/src/server/config/webpack.config.js b/app/react-native/src/server/config/webpack.config.js
index ddd38a20d00..0e2f5c2ce09 100644
--- a/app/react-native/src/server/config/webpack.config.js
+++ b/app/react-native/src/server/config/webpack.config.js
@@ -1,9 +1,10 @@
import path from 'path';
import webpack from 'webpack';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
import { OccurenceOrderPlugin, includePaths, excludePaths } from './utils';
-const config = {
+const getConfig = options => ({
devtool: '#cheap-module-eval-source-map',
entry: {
manager: [require.resolve('../../manager')],
@@ -14,6 +15,13 @@ const config = {
publicPath: '/',
},
plugins: [
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ data: {
+ options: JSON.stringify(options),
+ },
+ template: require.resolve('../index.html.ejs'),
+ }),
new OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
@@ -29,6 +37,6 @@ const config = {
},
],
},
-};
+});
-export default config;
+export default getConfig;
diff --git a/app/react-native/src/server/config/webpack.config.prod.js b/app/react-native/src/server/config/webpack.config.prod.js
index 841459f0c83..b7c15794ef1 100644
--- a/app/react-native/src/server/config/webpack.config.prod.js
+++ b/app/react-native/src/server/config/webpack.config.prod.js
@@ -1,57 +1,69 @@
import path from 'path';
import webpack from 'webpack';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
import { OccurenceOrderPlugin, includePaths, excludePaths } from './utils';
-const config = {
- bail: true,
- devtool: '#cheap-module-source-map',
- entry: {
- manager: [path.resolve(__dirname, '../../manager')],
- },
- output: {
- path: path.join(__dirname, 'dist'),
- filename: 'static/[name].bundle.js',
- // Here we set the publicPath to ''.
- // This allows us to deploy storybook into subpaths like GitHub pages.
- // This works with css and image loaders too.
- // This is working for storybook since, we don't use pushState urls and
- // relative URLs works always.
- publicPath: '/',
- },
- plugins: [
- new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
- new webpack.optimize.DedupePlugin(),
- new webpack.optimize.UglifyJsPlugin({
- compress: {
- screw_ie8: true,
- warnings: false,
- },
- mangle: {
- screw_ie8: true,
- },
- output: {
- comments: false,
- screw_ie8: true,
- },
- }),
- ],
- module: {
- loaders: [
- {
- test: /\.jsx?$/,
- loader: require.resolve('babel-loader'),
- query: require('./babel.prod.js'), // eslint-disable-line
- include: includePaths,
- exclude: excludePaths,
- },
+const getConfig = options => {
+ const config = {
+ bail: true,
+ devtool: '#cheap-module-source-map',
+ entry: {
+ manager: [path.resolve(__dirname, '../../manager')],
+ },
+ output: {
+ path: path.join(__dirname, 'dist'),
+ filename: 'static/[name].bundle.js',
+ // Here we set the publicPath to ''.
+ // This allows us to deploy storybook into subpaths like GitHub pages.
+ // This works with css and image loaders too.
+ // This is working for storybook since, we don't use pushState urls and
+ // relative URLs works always.
+ publicPath: '/',
+ },
+ plugins: [
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ data: {
+ options: JSON.stringify(options),
+ },
+ template: require.resolve('../index.html.ejs'),
+ }),
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
+ new webpack.optimize.DedupePlugin(),
+ new webpack.optimize.UglifyJsPlugin({
+ compress: {
+ screw_ie8: true,
+ warnings: false,
+ },
+ mangle: {
+ screw_ie8: true,
+ },
+ output: {
+ comments: false,
+ screw_ie8: true,
+ },
+ }),
],
- },
+ module: {
+ loaders: [
+ {
+ test: /\.jsx?$/,
+ loader: require.resolve('babel-loader'),
+ query: require('./babel.prod.js'), // eslint-disable-line
+ include: includePaths,
+ exclude: excludePaths,
+ },
+ ],
+ },
+ };
+
+ // Webpack 2 doesn't have a OccurenceOrderPlugin plugin in the production mode.
+ // But webpack 1 has it. That's why we do this.
+ if (OccurenceOrderPlugin) {
+ config.plugins.unshift(new OccurenceOrderPlugin());
+ }
+
+ return config;
};
-// Webpack 2 doesn't have a OccurenceOrderPlugin plugin in the production mode.
-// But webpack 1 has it. That's why we do this.
-if (OccurenceOrderPlugin) {
- config.plugins.unshift(new OccurenceOrderPlugin());
-}
-
-export default config;
+export default getConfig;
diff --git a/app/react-native/src/server/index.html.ejs b/app/react-native/src/server/index.html.ejs
new file mode 100644
index 00000000000..84b46693e47
--- /dev/null
+++ b/app/react-native/src/server/index.html.ejs
@@ -0,0 +1,34 @@
+
+
+
+
+
+ Storybook for React
+
+
+
+
+
+
+
diff --git a/app/react-native/src/server/index.html.js b/app/react-native/src/server/index.html.js
deleted file mode 100644
index e3bc622c4e2..00000000000
--- a/app/react-native/src/server/index.html.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import url from 'url';
-
-export default function(publicPath, options) {
- return `
-
-
-
-
-
- Storybook for React
-
-
-
-
-
-
-
-
- `;
-}
diff --git a/app/react-native/src/server/middleware.js b/app/react-native/src/server/middleware.js
index 10ec0c03c14..3aa404d17d2 100644
--- a/app/react-native/src/server/middleware.js
+++ b/app/react-native/src/server/middleware.js
@@ -7,7 +7,6 @@ import webpackHotMiddleware from 'webpack-hot-middleware';
import baseConfig from './config/webpack.config';
import baseProductionConfig from './config/webpack.config.prod';
import loadConfig from './config';
-import getIndexHtml from './index.html';
function getMiddleware(configDir) {
const middlewarePath = path.resolve(configDir, 'middleware.js');
@@ -26,7 +25,7 @@ export default function({ projectDir, configDir, ...options }) {
// custom `.babelrc` file and `webpack.config.js` files
const environment = options.environment || 'DEVELOPMENT';
const isProd = environment === 'PRODUCTION';
- const currentWebpackConfig = isProd ? baseProductionConfig : baseConfig;
+ const currentWebpackConfig = isProd ? baseProductionConfig(options) : baseConfig(options);
const config = loadConfig(environment, currentWebpackConfig, projectDir, configDir);
// remove the leading '/'
@@ -53,12 +52,8 @@ export default function({ projectDir, configDir, ...options }) {
}
router.get('/', (req, res) => {
- res.send(
- getIndexHtml(publicPath, {
- manualId: options.manualId,
- secured: options.secured,
- })
- );
+ res.set('Content-Type', 'text/html');
+ res.sendFile(path.join(`${__dirname}/public/index.html`));
});
return router;
diff --git a/examples/cra-kitchen-sink/.storybook/webpack.config.js b/examples/cra-kitchen-sink/.storybook/webpack.config.js
index 3ced6fa641b..001da2406dd 100644
--- a/examples/cra-kitchen-sink/.storybook/webpack.config.js
+++ b/examples/cra-kitchen-sink/.storybook/webpack.config.js
@@ -1,4 +1,3 @@
-const path = require('path');
const webpack = require('webpack');
// load the default config generator.
diff --git a/examples/crna-kitchen-sink/package.json b/examples/crna-kitchen-sink/package.json
index 4126c6c8233..996107a5571 100644
--- a/examples/crna-kitchen-sink/package.json
+++ b/examples/crna-kitchen-sink/package.json
@@ -34,6 +34,7 @@
"expo": "19.0.0",
"prop-types": "15.5.10",
"react": "16.0.0-alpha.12",
- "react-native": "0.46.1"
+ "react-native": "0.46.1",
+ "webpack": "^2.5.1 || ^3.0.0"
}
}
diff --git a/examples/crna-kitchen-sink/storybook/webpack.config.js b/examples/crna-kitchen-sink/storybook/webpack.config.js
new file mode 100644
index 00000000000..a2daea72816
--- /dev/null
+++ b/examples/crna-kitchen-sink/storybook/webpack.config.js
@@ -0,0 +1,27 @@
+const webpack = require('webpack');
+
+// load the default config generator.
+const genDefaultConfig = require('@storybook/react-native/dist/server/config/defaults/webpack.config.js');
+
+// Export a function. Accept the base config as the only param.
+module.exports = (storybookBaseConfig, configType) => {
+ // configType has a value of 'DEVELOPMENT' or 'PRODUCTION'
+ // You can change the configuration based on that.
+ // 'PRODUCTION' is used when building the static version of storybook.
+
+ const config = genDefaultConfig(storybookBaseConfig, configType);
+
+ // Make whatever fine-grained changes you need
+ config.plugins.push(
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'vendor',
+ minChunks(module) {
+ // this assumes your vendor imports exist in the node_modules directory
+ return module.context && module.context.indexOf('node_modules') !== -1;
+ },
+ })
+ );
+
+ // Return the altered config
+ return config;
+};
diff --git a/examples/vue-kitchen-sink/.storybook/webpack.config.js b/examples/vue-kitchen-sink/.storybook/webpack.config.js
index f7cbac74a0b..416bb092edd 100644
--- a/examples/vue-kitchen-sink/.storybook/webpack.config.js
+++ b/examples/vue-kitchen-sink/.storybook/webpack.config.js
@@ -1,4 +1,3 @@
-const path = require('path');
const webpack = require('webpack');
// load the default config generator.