├── CHANGELOG.md ├── webpack-settings └── example.settings.js ├── package.json ├── webpack-configs └── example.config.js ├── LICENSE ├── .gitignore ├── index.js └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # get-webpack-config Changelog 2 | 3 | ## 1.0.0 - 2021.02.02 4 | ### Added 5 | * Initial release 6 | -------------------------------------------------------------------------------- /webpack-settings/example.settings.js: -------------------------------------------------------------------------------- 1 | // example.settings.js 2 | 3 | // node modules 4 | require('dotenv').config(); 5 | 6 | // settings 7 | module.exports = { 8 | foo: 'bar', 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-webpack-config", 3 | "version": "1.0.0", 4 | "description": "Utilities to help you modularize your webpack configs", 5 | "main": "index.js", 6 | "dependencies": { 7 | "webpack-merge": "^5.7.0" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/nystudio107/get-webpack-config.git" 15 | }, 16 | "keywords": [ 17 | "webpack", 18 | "modular", 19 | "config" 20 | ], 21 | "author": "nystudio107", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/nystudio107/get-webpack-config/issues" 25 | }, 26 | "homepage": "https://github.com/nystudio107/get-webpack-config#readme" 27 | } 28 | -------------------------------------------------------------------------------- /webpack-configs/example.config.js: -------------------------------------------------------------------------------- 1 | // example.config.js 2 | // returns a webpack config object for the example config 3 | 4 | // return a webpack config 5 | module.exports = (type = 'modern', settings) => { 6 | // common config 7 | const common = () => ({ 8 | }); 9 | // configs 10 | const configs = { 11 | // development configs 12 | development: { 13 | // legacy development config 14 | legacy: { 15 | }, 16 | // modern development config 17 | modern: { 18 | ...common(), 19 | }, 20 | }, 21 | // production configs 22 | production: { 23 | // legacy production config 24 | legacy: { 25 | ...common(), 26 | }, 27 | // modern production config 28 | modern: { 29 | ...common(), 30 | }, 31 | } 32 | }; 33 | 34 | return configs[process.env.NODE_ENV][type]; 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nystudio107 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # MISC FILES 107 | .cache 108 | .DS_Store 109 | .idea 110 | .project 111 | .settings 112 | *.esproj 113 | *.sublime-workspace 114 | *.sublime-project 115 | *.tmproj 116 | *.tmproject 117 | .vscode/* 118 | !.vscode/settings.json 119 | !.vscode/tasks.json 120 | !.vscode/launch.json 121 | !.vscode/extensions.json 122 | config.codekit3 123 | prepros-6.config 124 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // node modules 2 | const path = require('path'); 3 | const { merge } = require('webpack-merge'); 4 | 5 | const getBaseConfigName = () => process.env.GET_WEBPACK_CONFIG_BASE_CONFIG_NAME || 'app'; 6 | const getSettingsPath = () => process.env.GET_WEBPACK_CONFIG_SETTINGS_PATH || path.resolve(process.cwd(), 'webpack-settings'); 7 | const getConfigsPath = () => process.env.GET_WEBPACK_CONFIG_CONFIGS_PATH || path.resolve(process.cwd(), 'webpack-configs'); 8 | 9 | /** 10 | * return a webpack settings file 11 | * @param name string 12 | * @returns {{}} 13 | */ 14 | const getWebpackSettings = (name) => { 15 | let settings = {}; 16 | try { 17 | settings = require(path.join(getSettingsPath(), `${name}.settings.js`)); 18 | } catch (e) { 19 | // that's okay 20 | } 21 | 22 | return settings; 23 | }; 24 | 25 | /** 26 | * return a webpack settings file combined with the 'app' settings 27 | * @param name string 28 | * @returns {{}} 29 | */ 30 | const getCombinedWebpackSettings = (name) => ({ 31 | ...getWebpackSettings(getBaseConfigName()), 32 | ...getWebpackSettings(name), 33 | }); 34 | 35 | /** 36 | * return a legacy webpack config file 37 | * @param name 38 | * @returns {{}} 39 | */ 40 | const getLegacyWebpackConfig = (name) => require(path.join(getConfigsPath(), `${name}.config.js`))('legacy', getCombinedWebpackSettings(name)); 41 | 42 | /** 43 | * return a modern webpack config file 44 | * @param name 45 | * @returns {{}} 46 | */ 47 | const getModernWebpackConfig = (name) => require(path.join(getConfigsPath(), `${name}.config.js`))('modern', getCombinedWebpackSettings(name)); 48 | 49 | /** 50 | * return an array of webpack configs using the function provided in getWebpackConfig 51 | * @param names string[] 52 | * @param getWebpackConfig function 53 | * @returns {{}} 54 | */ 55 | const webpackConfigs = (names, getWebpackConfig) => { 56 | let config = {}; 57 | names.forEach((name) => config = merge(config, getWebpackConfig(name))); 58 | 59 | return config; 60 | }; 61 | 62 | /** 63 | * return an array of build webpack configs 64 | * @param names string 65 | * @returns {{}} 66 | */ 67 | const buildWebpackConfigs = (...names) => webpackConfigs(names, getModernWebpackConfig); 68 | 69 | /** 70 | * return an array of legacy webpack configs 71 | * @param names string 72 | * @returns {{}} 73 | */ 74 | const legacyWebpackConfigs = (...names) => webpackConfigs(names, getLegacyWebpackConfig); 75 | 76 | /** 77 | * return an array of modern webpack configs 78 | * @param names string 79 | * @returns {{}} 80 | */ 81 | const modernWebpackConfigs = (...names) => webpackConfigs(names, getModernWebpackConfig); 82 | 83 | // module exports 84 | module.exports = { 85 | getWebpackSettings, 86 | getLegacyWebpackConfig, 87 | getModernWebpackConfig, 88 | buildWebpackConfigs, 89 | legacyWebpackConfigs, 90 | modernWebpackConfigs, 91 | }; 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # get-webpack-config 2 | 3 | Utilities to help you modularize your webpack configs 4 | 5 | ## About get-webpack-config 6 | `get-webpack-config` is a set of lightweight utilities to help you modularize your webpack configs. 7 | 8 | You can break up your webpack configs into small modular configs that are easier to understand & maintain, and then merge them together for webpack. 9 | 10 | It also allows you to have optional settings that can be passed in to each config, allowing you to update the configs independently of the settings. 11 | 12 | The other key feature is that because the configs are completely modularized, you can add or remove them easily, without fear of it affecting the rest of your webpack config. 13 | 14 | ## get-webpack-config example 15 | 16 | Here's an example of how you might use `get-webpack-config`, this is a complete webpack config file for a real-world `webpack.dev.js` config: 17 | 18 | ```js 19 | // webpack.dev.js 20 | // developmental build config 21 | 22 | // environment 23 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'; 24 | 25 | // webpack config file helpers 26 | const { modernWebpackConfigs } = require('get-webpack-config'); 27 | 28 | // development module exports 29 | module.exports = modernWebpackConfigs( 30 | 'app', 31 | 'dev-server', 32 | 'manifest', 33 | 'babel-loader', 34 | 'image-loader', 35 | 'font-loader', 36 | 'postcss-loader', 37 | 'typescript-loader', 38 | 'vue-loader', 39 | ); 40 | ``` 41 | 42 | You'd run it just as you'd run any webpack build, e.g.: 43 | 44 | ``` 45 | webpack serve --config webpack.dev.js 46 | ``` 47 | 48 | This would look for webpack configs in the default `webpack-configs/` directory given names, with `.config.js` appended to them. 49 | 50 | It will also look for corresponding settings in the default `webpack-settings/` directory, and pass them in to each config. 51 | 52 | Then using webpack-merge, it merges the configs together, and passes them back to webpack. 53 | 54 | Here's an example of what the directory structure would look like, with each `.config.js` and optional `.settings.js` file: 55 | 56 | ``` 57 | ├── package.json 58 | ├── package-lock.json 59 | ├── webpack.dev.js 60 | ├── webpack-configs 61 | │   ├── app.config.js 62 | │   ├── babel-loader.config.js 63 | │   ├── dev-server.config.js 64 | │   ├── font-loader.config.js 65 | │   ├── image-loader.config.js 66 | │   ├── manifest.config.js 67 | │   ├── postcss-loader.config.js 68 | │   ├── typescript-loader.config.js 69 | │   └── vue-loader.config.js 70 | └── webpack-settings 71 | ├── app.settings.js 72 | ├── babel-loader.settings.js 73 | ├── dev-server.settings.js 74 | ├── manifest.settings.js 75 | └── typescript-loader.settings.js 76 | ``` 77 | 78 | ## webpack-configs 79 | 80 | A webpack config for `get-webpack-config` is exactly the same as any webpack config: it's a `.js` file that returns a [webpack configuration object](https://webpack.js.org/configuration/). 81 | 82 | The webpack config is passed in the `type` of build (either `modern` or `legacy`), and a `settings` object. Modern builds are intended for modern browsers, and legacy builds are intended for legacy browsers, using the [module/nomodule pattern](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) 83 | 84 | Here's an example of an empty example config: 85 | 86 | ```js 87 | // example.config.js 88 | // returns a webpack config object for the example config 89 | 90 | // return a webpack config 91 | module.exports = (type = 'modern', settings) => { 92 | // common config 93 | const common = () => ({ 94 | }); 95 | // configs 96 | const configs = { 97 | // development configs 98 | development: { 99 | // legacy development config 100 | legacy: { 101 | }, 102 | // modern development config 103 | modern: { 104 | ...common(), 105 | }, 106 | }, 107 | // production configs 108 | production: { 109 | // legacy production config 110 | legacy: { 111 | ...common(), 112 | }, 113 | // modern production config 114 | modern: { 115 | ...common(), 116 | }, 117 | } 118 | }; 119 | 120 | return configs[process.env.NODE_ENV][type]; 121 | } 122 | ``` 123 | 124 | Note that the above is just a _convention_, all the default export needs to do is return a webpack config object. 125 | 126 | Following this convention, though, allows you to easily create webpack configs that are different depending on their `type` and whether it's a development or production build. 127 | 128 | The `common()` function contains a common webpack config that's spread into the actual configs, then each specific config can add in whatever makes it unique. 129 | 130 | **N.B.:** The `development`/`legacy` config combo is empty, because it's rare to want to use legacy builds in a HMR development environment. 131 | 132 | ## webpack-settings 133 | 134 | The `.settings.js` files are just JavaScript modules that export an object of config settings. 135 | 136 | Here's an example: 137 | 138 | ```js 139 | // example.settings.js 140 | 141 | // node modules 142 | require('dotenv').config(); 143 | 144 | // settings 145 | module.exports = { 146 | foo: 'bar', 147 | }; 148 | 149 | ``` 150 | 151 | We're `require()`'ing the optional [dotenv package](https://www.npmjs.com/package/dotenv) because it's common to want to override settings from `.env` files. 152 | 153 | All you actually need to do is ensure that the default export returns an object of settings that your config can use. 154 | 155 | The settings are kept separate from the config files so that webpack configs can be updated independent of the settings that affect them. 156 | 157 | There is a special settings file `app.settings.js` that is merged with all other settings files, so you can put global settings in there. 158 | 159 | ## Exported functions 160 | 161 | For all of the functions below, whether to return a production or development config is based on `process.env.NODE_ENV`. 162 | 163 | ### `modernWebpackConfigs(configName, ...)` 164 | 165 | This function takes an arbitrary number of config names as arguments, and returns a merged webpack config from them all. 166 | 167 | It ends up calling `getModernWebpackConfig()` on each argument, to return the modern version of the config. 168 | 169 | ### `legacyWebpackConfigs(configName, ...)` 170 | 171 | This function takes an arbitrary number of config names as arguments, and returns a merged webpack config from them all. 172 | 173 | It ends up calling `getLegacyWebpackConfig()` on each argument, to return the legacy version of the config. 174 | 175 | ### `buildWebpackConfigs(configName, ...)` 176 | 177 | This function takes an arbitrary number of config names as arguments, and returns a merged webpack config from them all. 178 | 179 | It ends up calling `getModernWebpackConfig()` on each argument, to return the modern version of the config. 180 | 181 | This function exists just as a way to logically separate out any webpack tasks that are not related to either the modern or legacy builds, such as "clean" or "copy" tasks. 182 | 183 | ### `getModernWebpackConfig(configName)` 184 | 185 | This function returns a single webpack config, by passing in the corresponding `.settings.js` to it (if any) and returning the results. 186 | 187 | It appends `.config.js` to `configName` to determine the name of the config to load. 188 | 189 | It returns the modern version of the config. 190 | 191 | ### `getLegacyWebpackConfig(configName)` 192 | 193 | This function returns a single webpack config, by passing in the corresponding `.settings.js` to it (if any) and returning the results. 194 | 195 | It appends `.config.js` to `configName` to determine the name of the config to load. 196 | 197 | It returns the legacy version of the config. 198 | 199 | ### `getWebpackSettings(settingsName)` 200 | 201 | This function returns a settings object for the passed in `settingsName`. 202 | 203 | It appends `.settings.js` to `configName` to determine the name of the settings to load. 204 | 205 | ## Environment variables 206 | 207 | The following environment variables allow you to override defaults in `get-webpack-config`: 208 | 209 | - `GET_WEBPACK_CONFIG_BASE_CONFIG_NAME` - the name of the global base settings file that should be merged into any webpack config specific settings. Default: `app` 210 | 211 | - `GET_WEBPACK_CONFIG_SETTINGS_PATH` - The fully qualified path to the directory where the webpack configs are. Default: `./webpack-configs` 212 | 213 | - `GET_WEBPACK_CONFIG_CONFIGS_PATH` - The fully qualified path to the directory where the webpack settings are. Default: `./webpack-settings` 214 | 215 | ## webpack Multi-compiler example 216 | 217 | Any webpack config file can return an object, which is a single webpack compiler. Or it can return an array of webpack objects, which is called a [multi-compiler](https://webpack.js.org/api/node/#multicompiler). 218 | 219 | We leverage this to break up our builds into separate configs for `modern`, `legacy`, and `build` webpack compiles. 220 | 221 | Here's an example of a complete real-world `webpack.prod.js` config file that uses the `get-webpack-config` functions for a nice clean, semantically readable config: 222 | 223 | ```js 224 | // webpack.prod.js 225 | // production build config 226 | 227 | // environment 228 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'; 229 | 230 | // webpack config file helpers 231 | const { buildWebpackConfigs, legacyWebpackConfigs, modernWebpackConfigs } = require('get-webpack-config'); 232 | 233 | // production multi-compiler module exports 234 | module.exports = [ 235 | legacyWebpackConfigs( 236 | 'app', 237 | 'production', 238 | 'critical', 239 | 'manifest', 240 | 'babel-loader', 241 | 'image-loader', 242 | 'font-loader', 243 | 'postcss-loader', 244 | 'typescript-loader', 245 | 'vue-loader', 246 | 'banner', 247 | 'compression', 248 | 'bundle-analyzer', 249 | ), 250 | modernWebpackConfigs( 251 | 'app', 252 | 'production', 253 | 'manifest', 254 | 'babel-loader', 255 | 'image-loader', 256 | 'imagemin-webp', 257 | 'font-loader', 258 | 'postcss-loader', 259 | 'typescript-loader', 260 | 'vue-loader', 261 | 'banner', 262 | 'compression', 263 | 'bundle-analyzer', 264 | 'workbox', 265 | ), 266 | buildWebpackConfigs( 267 | 'build', 268 | 'clean', 269 | 'copy', 270 | 'favicons', 271 | 'create-symlink', 272 | 'save-remote-file', 273 | ), 274 | ]; 275 | ``` 276 | 277 | You may have simpler needs than this, but it's easy to just remove the bits you don't need by just deleting a single line. 278 | --------------------------------------------------------------------------------