diff --git a/app/react/src/server/config/webpack.config.js b/app/react/src/server/config/webpack.config.js index 72de95c5e61e..24eacd0bf24b 100644 --- a/app/react/src/server/config/webpack.config.js +++ b/app/react/src/server/config/webpack.config.js @@ -26,6 +26,7 @@ export default function() { new webpack.HotModuleReplacementPlugin(), new CaseSensitivePathsPlugin(), new WatchMissingNodeModulesPlugin(nodeModulesPaths), + new webpack.ProgressPlugin(), ], module: { rules: [ diff --git a/app/react/src/server/iframe.html.js b/app/react/src/server/iframe.html.js index 240b2be5774e..580b48b6dec7 100644 --- a/app/react/src/server/iframe.html.js +++ b/app/react/src/server/iframe.html.js @@ -8,37 +8,47 @@ import url from 'url'; // 'preview.0d2d3d845f78399fd6d5e859daa152a9.css', // 'static/preview.9adbb5ef965106be1cc3.bundle.js.map', // 'preview.0d2d3d845f78399fd6d5e859daa152a9.css.map' ] -const previewUrlsFromAssets = assets => { +const urlsFromAssets = assets => { if (!assets) { return { - js: 'static/preview.bundle.js', + js: ['static/preview.bundle.js'], + css: [], }; } - if (typeof assets.preview === 'string') { - return { - js: assets.preview, - }; - } - - return { - js: assets.preview.find(filename => filename.match(/\.js$/)), - css: assets.preview.find(filename => filename.match(/\.css$/)), + const urls = { + js: [], + css: [], }; + + const re = /.+\.(\w+)$/; + Object.keys(assets) + // Don't load the manager script in the iframe + .filter(key => key !== 'manager') + .forEach(key => { + const asset = assets[key]; + if (typeof asset === 'string') { + urls[re.exec(asset)[1]].push(asset); + } else { + const assetUrl = asset.find(u => re.exec(u)[1] !== 'map'); + urls[re.exec(assetUrl)[1]].push(assetUrl); + } + }); + + return urls; }; export default function(data) { const { assets, headHtml, publicPath } = data; - const previewUrls = previewUrlsFromAssets(assets); + const urls = urlsFromAssets(assets); - let previewCssTag = ''; - if (previewUrls.css) { - previewCssTag = ``; - } + const cssTags = urls.css + .map(u => ``) + .join('\n'); + const scriptTags = urls.js + .map(u => ``) + .join('\n'); return ` @@ -53,12 +63,12 @@ export default function(data) { Storybook ${headHtml} - ${previewCssTag} + ${cssTags}
- + ${scriptTags} `; diff --git a/app/react/src/server/index.js b/app/react/src/server/index.js index d086222ebcbc..d23a75fb50f1 100755 --- a/app/react/src/server/index.js +++ b/app/react/src/server/index.js @@ -8,7 +8,7 @@ import path from 'path'; import fs from 'fs'; import chalk from 'chalk'; import shelljs from 'shelljs'; -import storybook from './middleware'; +import storybook, { webpackValid } from './middleware'; import packageJson from '../../package.json'; import { parseList, getEnvConfig } from './utils'; @@ -135,11 +135,23 @@ process.env.STORYBOOK_GIT_BRANCH = // `getBaseConfig` function which is called inside the middleware app.use(storybook(configDir)); +let serverResolve = () => {}; +let serverReject = () => {}; +const serverListening = new Promise((resolve, reject) => { + serverResolve = resolve; + serverReject = reject; +}); server.listen(...listenAddr, error => { if (error) { - throw error; + serverReject(error); } else { - const address = `http://${program.host || 'localhost'}:${program.port}/`; - logger.info(`Storybook started on => ${chalk.cyan(address)}\n`); + serverResolve(); } }); + +Promise.all([webpackValid, serverListening]) + .then(() => { + const address = `http://${program.host || 'localhost'}:${program.port}/`; + logger.info(`Storybook started on => ${chalk.cyan(address)}\n`); + }) + .catch(error => logger.error(error)); diff --git a/app/react/src/server/middleware.js b/app/react/src/server/middleware.js index e2ca8d2ace87..112740698259 100644 --- a/app/react/src/server/middleware.js +++ b/app/react/src/server/middleware.js @@ -8,6 +8,13 @@ import getIndexHtml from './index.html'; import getIframeHtml from './iframe.html'; import { getHeadHtml, getMiddleware } from './utils'; +let webpackResolve = () => {}; +let webpackReject = () => {}; +export const webpackValid = new Promise((resolve, reject) => { + webpackResolve = resolve; + webpackReject = reject; +}); + export default function(configDir) { // Build the webpack configuration using the `getBaseConfig` // custom `.babelrc` file and `webpack.config.js` files @@ -29,19 +36,33 @@ export default function(configDir) { }; const router = new Router(); - router.use(webpackDevMiddleware(compiler, devMiddlewareOptions)); + const webpackDevMiddlewareInstance = webpackDevMiddleware(compiler, devMiddlewareOptions); + router.use(webpackDevMiddlewareInstance); router.use(webpackHotMiddleware(compiler)); // custom middleware middlewareFn(router); - router.get('/', (req, res) => { - res.send(getIndexHtml({ publicPath })); - }); + webpackDevMiddlewareInstance.waitUntilValid(stats => { + const data = { + publicPath: config.output.publicPath, + assets: stats.toJson().assetsByChunkName, + }; + + router.get('/', (req, res) => { + res.send(getIndexHtml({ publicPath })); + }); + + router.get('/iframe.html', (req, res) => { + const headHtml = getHeadHtml(configDir); + res.send(getIframeHtml({ ...data, headHtml, publicPath })); + }); - router.get('/iframe.html', (req, res) => { - const headHtml = getHeadHtml(configDir); - res.send(getIframeHtml({ headHtml, publicPath })); + if (stats.toJson().errors.length) { + webpackReject(stats); + } else { + webpackResolve(stats); + } }); return router; diff --git a/examples/cra-storybook/package.json b/examples/cra-storybook/package.json index 3cd54c700771..29ba51e8cbf3 100644 --- a/examples/cra-storybook/package.json +++ b/examples/cra-storybook/package.json @@ -19,11 +19,11 @@ "uuid": "^3.0.1" }, "devDependencies": { - "@storybook/addon-actions": "3.0.0", - "@storybook/addon-links": "3.0.0", - "@storybook/addon-events": "3.0.1", - "@storybook/addons": "3.0.0", - "@storybook/react": "3.0.0", + "@storybook/addon-actions": "^3.0.0", + "@storybook/addon-links": "^3.0.0", + "@storybook/addon-events": "^3.0.0", + "@storybook/addons": "^3.0.0", + "@storybook/react": "^3.0.0", "react-scripts": "1.0.1" }, "private": true