diff --git a/app/react-native/package.json b/app/react-native/package.json index b91415c57c3..f581bf213b0 100644 --- a/app/react-native/package.json +++ b/app/react-native/package.json @@ -55,12 +55,12 @@ "global": "^4.3.2", "html-webpack-plugin": "^4.0.0-beta.1", "json5": "^2.1.0", + "lazy-universal-dotenv": "^1.9.1", "prop-types": "^15.6.2", "raw-loader": "^0.5.1", "react-dev-utils": "6.0.4", "react-native-swipe-gestures": "^1.0.2", "shelljs": "^0.8.2", - "universal-dotenv": "^1.9.1", "url-parse": "^1.4.3", "uuid": "^3.3.2", "webpack": "^4.20.2", diff --git a/app/react-native/src/server/config/utils.js b/app/react-native/src/server/config/utils.js index fee44f7b254..421e0032580 100644 --- a/app/react-native/src/server/config/utils.js +++ b/app/react-native/src/server/config/utils.js @@ -1,7 +1,33 @@ import path from 'path'; +import { getEnvironment } from 'lazy-universal-dotenv'; export const includePaths = [path.resolve('./')]; export const excludePaths = [path.resolve('./node_modules')]; export const nodeModulesPaths = path.resolve('./node_modules'); + +// Load environment variables starts with STORYBOOK_ to the client side. +export function loadEnv(options = {}) { + const defaultNodeEnv = options.production ? 'production' : 'development'; + const env = { + NODE_ENV: JSON.stringify(process.env.NODE_ENV || defaultNodeEnv), + // This is to support CRA's public folder feature. + // In production we set this to dot(.) to allow the browser to access these assests + // even when deployed inside a subpath. (like in GitHub pages) + // In development this is just empty as we always serves from the root. + PUBLIC_URL: JSON.stringify(options.production ? '.' : ''), + }; + + const { stringified } = getEnvironment(); + + Object.keys(process.env) + .filter(name => /^STORYBOOK_/.test(name)) + .forEach(name => { + env[name] = JSON.stringify(process.env[name]); + }); + + return { + 'process.env': Object.assign({}, env, stringified), + }; +} diff --git a/app/react-native/src/server/config/webpack.config.js b/app/react-native/src/server/config/webpack.config.js index ec9c6ee8dc4..2369fae2819 100644 --- a/app/react-native/src/server/config/webpack.config.js +++ b/app/react-native/src/server/config/webpack.config.js @@ -1,6 +1,5 @@ import path from 'path'; import webpack from 'webpack'; -import { getEnvironment } from 'universal-dotenv'; import Dotenv from 'dotenv-webpack'; import WatchMissingNodeModulesPlugin from 'react-dev-utils/WatchMissingNodeModulesPlugin'; import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'; @@ -8,10 +7,11 @@ import HtmlWebpackPlugin from 'html-webpack-plugin'; import { getManagerHeadHtml } from '@storybook/core/server'; -import { includePaths, excludePaths, nodeModulesPaths } from './utils'; +import { includePaths, excludePaths, nodeModulesPaths, loadEnv } from './utils'; import { version } from '../../../package.json'; const getConfig = options => { + const environment = loadEnv(); const entriesMeta = { manager: { headHtmlSnippet: getManagerHeadHtml(options.configDir, process.env), @@ -44,14 +44,14 @@ const getConfig = options => { }), template: require.resolve(`@storybook/core/src/server/templates/index.ejs`), }), - new webpack.HotModuleReplacementPlugin(), - new CaseSensitivePathsPlugin(), - new WatchMissingNodeModulesPlugin(nodeModulesPaths), - new webpack.DefinePlugin(getEnvironment().webpack), - new Dotenv({ silent: true }), new webpack.DefinePlugin({ storybookOptions: JSON.stringify(options), }), + new webpack.HotModuleReplacementPlugin(), + new CaseSensitivePathsPlugin(), + new WatchMissingNodeModulesPlugin(nodeModulesPaths), + new webpack.DefinePlugin(environment), + new Dotenv({ silent: true }), ], module: { rules: [ 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 6c6db323bde..09720b78ff2 100644 --- a/app/react-native/src/server/config/webpack.config.prod.js +++ b/app/react-native/src/server/config/webpack.config.prod.js @@ -1,14 +1,14 @@ import path from 'path'; import webpack from 'webpack'; -import { getEnvironment } from 'universal-dotenv'; import Dotenv from 'dotenv-webpack'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import { getManagerHeadHtml } from '@storybook/core/dist/server/utils'; import { version } from '../../../package.json'; -import { includePaths, excludePaths } from './utils'; +import { includePaths, excludePaths, loadEnv } from './utils'; const getConfig = options => { + const environment = loadEnv({ production: true }); const entriesMeta = { manager: { headHtmlSnippet: getManagerHeadHtml(options.configDir, process.env), @@ -48,11 +48,10 @@ const getConfig = options => { template: require.resolve(`@storybook/core/src/server/templates/index.ejs`), }), new webpack.DefinePlugin({ - 'process.env.NODE_ENV': '"production"', storybookOptions: JSON.stringify(options), }), new webpack.optimize.DedupePlugin(), - new webpack.DefinePlugin(getEnvironment().webpack), + new webpack.DefinePlugin(environment), new Dotenv({ silent: true }), ], module: { diff --git a/examples/official-storybook/.env b/examples/official-storybook/.env index f088c261657..aa608211b26 100644 --- a/examples/official-storybook/.env +++ b/examples/official-storybook/.env @@ -1,2 +1,2 @@ -DISPLAY_WARNING=none +DOTENV_DISPLAY_WARNING=none STORYBOOK_EXAMPLE_APP=true diff --git a/examples/official-storybook/.env.development b/examples/official-storybook/.env.development new file mode 100644 index 00000000000..3746c482ab5 --- /dev/null +++ b/examples/official-storybook/.env.development @@ -0,0 +1 @@ +DOTENV_DEVELOPMENT_DISPLAY_WARNING=true diff --git a/examples/official-storybook/config.js b/examples/official-storybook/config.js index 520d9f5b50a..9b48d7835fb 100644 --- a/examples/official-storybook/config.js +++ b/examples/official-storybook/config.js @@ -9,6 +9,20 @@ import 'react-chromatic/storybook-addon'; import addHeadWarning from './head-warning'; import extraViewports from './extra-viewports.json'; +if (process.env.NODE_ENV === 'development') { + if (!process.env.DOTENV_DEVELOPMENT_DISPLAY_WARNING) { + addHeadWarning('Dotenv development file not loaded'); + } + + if (!process.env.STORYBOOK_DISPLAY_WARNING) { + addHeadWarning('Global storybook env var not loaded'); + } + + if (process.env.DISPLAY_WARNING) { + addHeadWarning('Global non-storybook env var loaded'); + } +} + addHeadWarning('Preview head not loaded', 'preview-head-not-loaded'); addHeadWarning('Dotenv file not loaded', 'dotenv-file-not-loaded'); diff --git a/examples/official-storybook/head-warning.js b/examples/official-storybook/head-warning.js index 6d6f1552732..1f25db16fd0 100644 --- a/examples/official-storybook/head-warning.js +++ b/examples/official-storybook/head-warning.js @@ -1,6 +1,6 @@ import { document } from 'global'; -export default function addHeadWarning(text, className) { +export default function addHeadWarning(text, className = '') { const warning = document.createElement('h1'); warning.textContent = text; warning.className = className; diff --git a/examples/official-storybook/package.json b/examples/official-storybook/package.json index e5243716bd3..cba48ea505a 100644 --- a/examples/official-storybook/package.json +++ b/examples/official-storybook/package.json @@ -9,7 +9,7 @@ "generate-addon-jest-testresults": "jest --config=tests/addon-jest.config.json --json --outputFile=stories/addon-jest.testresults.json", "graphql": "node ./graphql-server/index.js", "image-snapshots": "yarn run build-storybook && yarn run do-image-snapshots", - "storybook": "start-storybook -p 9011 -c ./ -s built-storybooks" + "storybook": "STORYBOOK_DISPLAY_WARNING=true DISPLAY_WARNING=true start-storybook -p 9011 -c ./ -s built-storybooks" }, "devDependencies": { "@emotion/core": "^0.13.1", diff --git a/examples/official-storybook/preview-head.html b/examples/official-storybook/preview-head.html index 4b10c3cde53..cdaf1026b73 100644 --- a/examples/official-storybook/preview-head.html +++ b/examples/official-storybook/preview-head.html @@ -4,6 +4,6 @@ } .dotenv-file-not-loaded { - display: %DISPLAY_WARNING%; + display: %DOTENV_DISPLAY_WARNING%; } diff --git a/lib/core/env.js b/lib/core/env.js deleted file mode 100644 index 0e6c77e4a58..00000000000 --- a/lib/core/env.js +++ /dev/null @@ -1 +0,0 @@ -require('./dist/server/config/env'); diff --git a/lib/core/package.json b/lib/core/package.json index efbb94a1fcb..60c54e8a4a8 100644 --- a/lib/core/package.json +++ b/lib/core/package.json @@ -60,6 +60,7 @@ "interpret": "^1.1.0", "ip": "^1.1.5", "json5": "^2.1.0", + "lazy-universal-dotenv": "^1.9.1", "node-fetch": "^2.2.0", "opn": "^5.4.0", "postcss-flexbugs-fixes": "^4.1.0", @@ -76,7 +77,6 @@ "style-loader": "^0.23.1", "svg-url-loader": "^2.3.2", "tty-table": "^2.6.9", - "universal-dotenv": "^1.9.1", "url-loader": "^1.1.2", "webpack": "^4.20.2", "webpack-dev-middleware": "^3.4.0", diff --git a/lib/core/src/server/build-dev.js b/lib/core/src/server/build-dev.js index 6fe8d1ea96d..708677d12f3 100644 --- a/lib/core/src/server/build-dev.js +++ b/lib/core/src/server/build-dev.js @@ -16,7 +16,6 @@ import Table from 'tty-table'; import storybook, { webpackValid } from './middleware'; import { getDevCli } from './cli'; -import './config/env'; const defaultFavIcon = require.resolve('./public/favicon.ico'); diff --git a/lib/core/src/server/build-static.js b/lib/core/src/server/build-static.js index 0a1c7c56252..b83ee3090e0 100644 --- a/lib/core/src/server/build-static.js +++ b/lib/core/src/server/build-static.js @@ -4,7 +4,6 @@ import fs from 'fs'; import shelljs from 'shelljs'; import { logger } from '@storybook/node-logger'; import { getProdCli } from './cli'; -import './config/env'; import loadConfig from './config'; const defaultFavIcon = require.resolve('./public/favicon.ico'); diff --git a/lib/core/src/server/config/env.js b/lib/core/src/server/config/env.js deleted file mode 100644 index 3b39d07c38d..00000000000 --- a/lib/core/src/server/config/env.js +++ /dev/null @@ -1 +0,0 @@ -import 'universal-dotenv'; diff --git a/lib/core/src/server/config/utils.js b/lib/core/src/server/config/utils.js index 99eb89b8755..c04c7b32256 100644 --- a/lib/core/src/server/config/utils.js +++ b/lib/core/src/server/config/utils.js @@ -1,4 +1,5 @@ import path from 'path'; +import { getEnvironment } from 'lazy-universal-dotenv'; export const includePaths = [path.resolve('./')]; @@ -29,8 +30,10 @@ export function loadEnv(options = {}) { env[name] = JSON.stringify(process.env[name]); }); + const { stringified } = getEnvironment({ nodeEnv: JSON.parse(env.NODE_ENV) }); + return { - 'process.env': env, + 'process.env': Object.assign({}, env, stringified), }; } diff --git a/lib/core/src/server/config/webpack.config.dev.js b/lib/core/src/server/config/webpack.config.dev.js index d70f33fd8e5..97a94ba3d6a 100644 --- a/lib/core/src/server/config/webpack.config.dev.js +++ b/lib/core/src/server/config/webpack.config.dev.js @@ -1,6 +1,5 @@ import path from 'path'; import webpack from 'webpack'; -import { getEnvironment } from 'universal-dotenv'; import Dotenv from 'dotenv-webpack'; import WatchMissingNodeModulesPlugin from 'react-dev-utils/WatchMissingNodeModulesPlugin'; import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'; @@ -18,6 +17,7 @@ import { getPreviewHeadHtml, getManagerHeadHtml, getPreviewBodyHtml } from '../u import { version } from '../../../package.json'; export default ({ configDir, quiet, babelOptions, entries }) => { + const environment = loadEnv(); const entriesMeta = { iframe: { headHtmlSnippet: getPreviewHeadHtml(configDir, process.env), @@ -61,12 +61,11 @@ export default ({ configDir, quiet, babelOptions, entries }) => { template: require.resolve(`../templates/index.ejs`), }) ), - new webpack.DefinePlugin(loadEnv()), + new webpack.DefinePlugin(environment), new webpack.HotModuleReplacementPlugin(), new CaseSensitivePathsPlugin(), new WatchMissingNodeModulesPlugin(nodeModulesPaths), quiet ? null : new webpack.ProgressPlugin(), - new webpack.DefinePlugin(getEnvironment().webpack), new Dotenv({ silent: true }), ].filter(Boolean), module: { diff --git a/lib/core/src/server/config/webpack.config.prod.js b/lib/core/src/server/config/webpack.config.prod.js index a2bf9bd9419..eee8e5aa179 100644 --- a/lib/core/src/server/config/webpack.config.prod.js +++ b/lib/core/src/server/config/webpack.config.prod.js @@ -1,5 +1,4 @@ import webpack from 'webpack'; -import { getEnvironment } from 'universal-dotenv'; import Dotenv from 'dotenv-webpack'; import HtmlWebpackPlugin from 'html-webpack-plugin'; @@ -8,6 +7,7 @@ import { includePaths, excludePaths, loadEnv, nodePaths, getBabelRuntimePath } f import { getPreviewHeadHtml, getManagerHeadHtml, getPreviewBodyHtml } from '../utils'; export default ({ configDir, babelOptions, entries }) => { + const environment = loadEnv({ production: true }); const entriesMeta = { iframe: { headHtmlSnippet: getPreviewHeadHtml(configDir, process.env), @@ -51,8 +51,7 @@ export default ({ configDir, babelOptions, entries }) => { template: require.resolve(`../templates/index.ejs`), }) ), - new webpack.DefinePlugin(loadEnv({ production: true })), - new webpack.DefinePlugin(getEnvironment().webpack), + new webpack.DefinePlugin(environment), new Dotenv({ silent: true }), ], module: { diff --git a/yarn.lock b/yarn.lock index 7e37a94bd34..408cae3fa25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13336,6 +13336,17 @@ lazy-cache@^1.0.3: resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= +lazy-universal-dotenv@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/lazy-universal-dotenv/-/lazy-universal-dotenv-1.9.1.tgz#d3fe9d5dd30309fc84eea1dab2a03585491474f9" + integrity sha512-Xd8XLG1S1TgoOcuPJZcxzAadRhXG5HLzOHUMPlon9VV/ewhAc56HGKPWbES99owbBoextbG8nb/AQA3c0S7TIQ== + dependencies: + "@babel/runtime" "^7.0.0" + app-root-dir "^1.0.2" + core-js "^2.5.7" + dotenv "^6.0.0" + dotenv-expand "^4.2.0" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -22098,17 +22109,6 @@ unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.1.1: dependencies: unist-util-visit-parents "^2.0.0" -universal-dotenv@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/universal-dotenv/-/universal-dotenv-1.9.1.tgz#b19730a2d8a83b8c5b72e5db09ef44dc30539b83" - integrity sha512-38GhLyN0rAz7040sltVEDSkO/yTcShKJFBIX5/4Hi/c//wUS9UgvnP1GDTLVs9CR+XARLsihPBciSS9y+USyKA== - dependencies: - "@babel/runtime" "^7.0.0" - app-root-dir "^1.0.2" - core-js "^2.5.7" - dotenv "^6.0.0" - dotenv-expand "^4.2.0" - universal-user-agent@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-2.0.1.tgz#18e591ca52b1cb804f6b9cbc4c336cf8191f80e1"