diff --git a/packages/docz-core/package.json b/packages/docz-core/package.json index 9c8fd14c9..00c5b20af 100644 --- a/packages/docz-core/package.json +++ b/packages/docz-core/package.json @@ -42,6 +42,7 @@ "cpy": "^7.0.1", "deepmerge": "^2.1.1", "detect-port": "^1.2.3", + "dotenv": "^6.0.0", "env-dot-prop": "^1.0.2", "express": "^4.16.3", "fast-glob": "^2.2.2", diff --git a/packages/docz-core/src/bundlers/webpack/config.ts b/packages/docz-core/src/bundlers/webpack/config.ts index b88d821bd..47d8dd042 100644 --- a/packages/docz-core/src/bundlers/webpack/config.ts +++ b/packages/docz-core/src/bundlers/webpack/config.ts @@ -10,6 +10,7 @@ import UglifyJs from 'uglifyjs-webpack-plugin' import { Config as Args, Env } from '../../commands/args' import { BabelRC } from '../../utils/babel-config' import * as paths from '../../config/paths' +import { getClientEnvironment } from '../../config/dotenv' import * as loaders from './loaders' const uglify = new UglifyJs({ @@ -235,6 +236,7 @@ export const createConfig = (args: Args, env: Env) => ( config.plugin('injections').use(require('webpack/lib/DefinePlugin'), [ { + ...getClientEnvironment(base).stringified, BASE_URL: JSON.stringify(base), NODE_ENV: JSON.stringify(env), 'process.env': { diff --git a/packages/docz-core/src/config/dotenv.ts b/packages/docz-core/src/config/dotenv.ts new file mode 100644 index 000000000..4e88d7384 --- /dev/null +++ b/packages/docz-core/src/config/dotenv.ts @@ -0,0 +1,81 @@ +import fs from 'fs' +import path from 'path' + +const appDir = fs.realpathSync(process.cwd()) +const resolveApp = (relativePath: any) => path.resolve(appDir, relativePath) + +const dotenv = resolveApp('.env') + +const NODE_ENV = process.env.NODE_ENV + +// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use +const dotenvFiles = [ + `${dotenv}.${NODE_ENV}.local`, + `${dotenv}.${NODE_ENV}`, + // Don't include `.env.local` for `test` environment + // since normally you expect tests to produce the same + // results for everyone + NODE_ENV !== 'test' && `$dotenv}.local`, + dotenv, +].filter(Boolean) + +// Load environment variables from .env* files. Suppress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. Variable expansion is supported in .env files. +// https://github.com/motdotla/dotenv +dotenvFiles.forEach(dotenvFile => { + require('dotenv').config({ + path: dotenvFile, + }) +}) + +// We support resolving modules according to `NODE_PATH`. +// It works similar to `NODE_PATH` in Node itself: +// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders +// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. +// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. +// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 +// We also resolve them to make sure all tools using them work consistently. +const appDirectory = fs.realpathSync(process.cwd()) +process.env.NODE_PATH = (process.env.NODE_PATH || '') + .split(path.delimiter) + .filter(folder => folder && !path.isAbsolute(folder)) + .map(folder => path.resolve(appDirectory, folder)) + .join(path.delimiter) + +// Grab NODE_ENV and REACT_APP_ or ANGULAR_APP_ or VUE_APP_* environment variables +// and prepare them to be +// injected into the application via DefinePlugin in Webpack configuration. +const APP_TEST = /^(REACT_APP_)|(ANGULAR_APP_)|(VUE_APP_)/i + +interface RT { + [name: string]: any +} +export const getClientEnvironment = (publicUrl: string) => { + const raw: RT = Object.keys(process.env) + .filter(key => APP_TEST.test(key)) + .reduce( + (env: RT, key) => { + env[key] = process.env[key] + return env + }, + { + // Useful for determining whether we’re running in production mode. Most + // importantly, it switches React into the correct mode. + NODE_ENV: process.env.NODE_ENV || 'development', + // Useful for resolving the correct path to static assets in `public`. For + // example, . This should + // only be used as an escape hatch. Normally you would put images into the `src` + // and `import` them in code to get their + PUBLIC_URL: publicUrl, + } + ) + const stringified = { + 'process.env': Object.keys(raw).reduce((env: RT, key) => { + env[key] = JSON.stringify(raw[key]) + return env + }, {}), + } + + return { raw, stringified } +} diff --git a/yarn.lock b/yarn.lock index 1ac093064..27007a196 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5093,6 +5093,10 @@ dot-prop@^4.1.0, dot-prop@^4.1.1, dot-prop@^4.2.0: dependencies: is-obj "^1.0.0" +dotenv@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.0.0.tgz#24e37c041741c5f4b25324958ebbc34bca965935" + duplexer2@^0.1.4, duplexer2@~0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"