diff --git a/app/react-native/package.json b/app/react-native/package.json index d2d78b903fb1..5716932f1b31 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 ddd38a20d004..0e2f5c2ce09e 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 841459f0c83f..9602f322a9a3 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,67 @@ 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()); -} + // 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 000000000000..84b46693e476 --- /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 e3bc622c4e2f..000000000000 --- 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 10ec0c03c14a..3aa404d17d24 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 3ced6fa641bb..001da2406dda 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 4126c6c8233c..996107a55711 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 000000000000..a2daea72816d --- /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 f7cbac74a0b4..416bb092edd9 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.