├── .gitattributes ├── .eslintignore ├── .npmrc ├── .gitignore ├── .doclets.yml ├── .babelrc ├── CHANGELOG.md ├── src ├── index.js ├── index.test.js ├── remove-empty.test.js ├── prop-if.test.js ├── prop-if.js ├── remove-empty.js ├── get-if-utils.test.js └── get-if-utils.js ├── other ├── ROADMAP.md ├── EXAMPLES.md ├── manual-releases.md └── CODE_OF_CONDUCT.md ├── .travis.yml ├── LICENSE ├── package-scripts.js ├── package.json ├── CONTRIBUTING.md ├── .all-contributorsrc └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | coverage 4 | dist 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | save-exact=true 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | coverage 4 | dist 5 | .opt-in 6 | .opt-out -------------------------------------------------------------------------------- /.doclets.yml: -------------------------------------------------------------------------------- 1 | dir: src 2 | packageJson: package.json 3 | articles: 4 | - Overview: README.md 5 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "env": { 4 | "test": { 5 | "plugins": [ 6 | "istanbul" 7 | ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release). 4 | You can see it on the [releases page](../releases). 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * See documentation for the `combineLoaders` method [here](https://www.npmjs.com/webpack-combine-loaders) 3 | */ 4 | export {default as combineLoaders} from 'webpack-combine-loaders' 5 | 6 | export * from './prop-if' 7 | export * from './remove-empty' 8 | export * from './get-if-utils' 9 | -------------------------------------------------------------------------------- /other/ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Project Roadmap 2 | 3 | ## Want to do 4 | 5 | There are definitely more utilities we could make, but I can't think of any right now. Feel free to file an issue to 6 | discuss! 7 | 8 | ## Might do 9 | 10 | Similar to above 11 | 12 | ## Wont do 13 | 14 | - Augment the Webpack API. I don't want to write a DSL on top of webpack's DSL. 15 | -------------------------------------------------------------------------------- /src/index.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import {difference} from 'lodash' 3 | import * as utils from './' 4 | 5 | test('exports all the things we care about', t => { 6 | const allExports = Object.keys(utils) 7 | const expectedExports = [ 8 | 'combineLoaders', 9 | 'propIf', 'propIfNot', 10 | 'removeEmpty', 'getIfUtils', 11 | 'removeEmptyProperties', 12 | ] 13 | const diff = difference(allExports, expectedExports) 14 | t.deepEqual(diff, []) 15 | }) 16 | -------------------------------------------------------------------------------- /src/remove-empty.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import {removeEmpty} from './remove-empty' 3 | 4 | test('removeEmpty should remove undefined values out of an array', t => { 5 | t.deepEqual(removeEmpty([undefined, 0, 1, 2, undefined, 3, undefined, null]), [0, 1, 2, 3, null]) 6 | }) 7 | 8 | test('removeEmpty should remove keys whose values are `undefined` out of an object', t => { 9 | t.deepEqual( 10 | removeEmpty({a: 1, b: 'b', c: undefined, d: null}), 11 | {a: 1, b: 'b', d: null} 12 | ) 13 | }) 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | notifications: 7 | email: false 8 | node_js: 9 | - '6' 10 | - '4' 11 | before_install: 12 | - npm i -g npm@^3.0.0 13 | before_script: 14 | - npm prune 15 | script: 16 | - npm start validate 17 | after_success: 18 | - 'curl -Lo travis_after_all.py https://git.io/travis_after_all' 19 | - python travis_after_all.py 20 | - export $(cat .to_export_back) &> /dev/null 21 | - npm start report-coverage 22 | - npm start release 23 | branches: 24 | only: 25 | - master 26 | -------------------------------------------------------------------------------- /src/prop-if.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import {propIf, propIfNot} from './prop-if' 3 | 4 | test('propIf should use the given value when true', t => { 5 | t.is(propIf(true, 'value', 'alternate'), 'value') 6 | }) 7 | 8 | test('propIf should use the alternate when false', t => { 9 | t.is(propIf(false, 'value', 'alternate'), 'alternate') 10 | }) 11 | 12 | test('propIf should parse a string of "false" as `false`', t => { 13 | t.is(propIf('false', 'value', 'alternate'), 'alternate') 14 | }) 15 | 16 | test('propIfNot should use the given alternate when true', t => { 17 | t.is(propIfNot(true, 'value', 'alternate'), 'alternate') 18 | }) 19 | 20 | test('propIfNot should use the value when false', t => { 21 | t.is(propIfNot(false, 'value', 'alternate'), 'value') 22 | }) 23 | 24 | test('propIfNot should parse a string of "false" as `false`', t => { 25 | t.is(propIfNot('false', 'value', 'alternate'), 'value') 26 | }) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Kent C. Dodds 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/prop-if.js: -------------------------------------------------------------------------------- 1 | export {propIf, propIfNot} 2 | 3 | /** 4 | * A simple ternary in the form of a function. The `add` argument is evaluated via JSON.parse 5 | * (to turn "true" to `true`). And if the result is truthy, then `value` will be returned, otherwise `alternate` will be 6 | * returned. This powers the (arguably more useful) methods returned from `getIfUtils`. 7 | * @example 8 | * propIf(true, 'value', 'alternate') 9 | * // returns 'value' 10 | * @example 11 | * // Is falsy sensitive 12 | * propIf(0, 'value', 'alternate') 13 | * // returns 'alternate' 14 | * @param {*} add The value to evaluate 15 | * @param {*} value The value to return in a truthy case 16 | * @param {*} alternate The value to return in a falsy case 17 | * @return {*} The value based on whether `add` evaluates to truthy 18 | */ 19 | function propIf(add, value, alternate) { 20 | return getValue(add) ? value : alternate 21 | } 22 | 23 | /** 24 | * This does the opposite of `propIf`. In fact, it just calls into it and swaps the `value` and `alternate` arguments 25 | * 😄 26 | * @param {*} add The value to evaluate 27 | * @param {*} value The value to return in a falsy case 28 | * @param {*} alternate The value to return in a truthy case 29 | * @return {*} The value based on whether `add` evaluates to truthy 30 | */ 31 | function propIfNot(add, value, alternate) { 32 | return propIf(add, alternate, value) 33 | } 34 | 35 | /** 36 | * Parses the value as JSON. This way "true" evaluates to true and "23" evaluates to 23 37 | * @private 38 | * @param {*} val The value to parse 39 | * @return {*} the parsed value 40 | */ 41 | function getValue(val) { 42 | return JSON.parse(val) 43 | } 44 | -------------------------------------------------------------------------------- /other/EXAMPLES.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## React Hot Loader with `webpack-config-utils` 4 | 5 | ```js 6 | const path = require('path') 7 | const webpack = require('webpack') 8 | const {getIfUtils, removeEmpty} = require('webpack-config-utils') 9 | 10 | const {ifDevelopment, ifProduction} = getIfUtils(process.env.NODE_ENV) 11 | 12 | module.exports = { 13 | devtool: 'eval', 14 | entry: removeEmpty([ 15 | ...ifDevelopment([ 16 | 'webpack-dev-server/client?http://localhost:3000', 17 | 'webpack/hot/only-dev-server', 18 | 'react-hot-loader/patch', 19 | ]), 20 | './index' 21 | ]), 22 | output: { 23 | path: path.join(__dirname, 'dist'), 24 | filename: 'bundle.js', 25 | publicPath: '/static/' 26 | }, 27 | plugins: removeEmpty([ 28 | new webpack.optimize.OccurrenceOrderPlugin(), 29 | ...ifDevelopment([ 30 | new webpack.HotModuleReplacementPlugin(), 31 | new webpack.NoErrorsPlugin(), 32 | ]), 33 | ...ifProduction([ 34 | new webpack.DefinePlugin({ 35 | 'process.env': { 36 | 'NODE_ENV': '"production"', 37 | }, 38 | }), 39 | new webpack.optimize.UglifyJsPlugin({compressor: {warnings: false}}), 40 | ]), 41 | ]), 42 | module: { 43 | loaders: [{ 44 | test: /\.js$/, 45 | loaders: ['babel-loader'], 46 | exclude: /node_modules/, 47 | include: __dirname 48 | }, { 49 | test: /\.js$/, 50 | loaders: ['babel-loader'], 51 | include: path.join(__dirname, '..', '..', 'src') 52 | }, { 53 | test: /\.css?$/, 54 | loaders: ['style-loader', 'raw-loader'], 55 | include: __dirname 56 | }] 57 | } 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /other/manual-releases.md: -------------------------------------------------------------------------------- 1 | # manual-releases 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | This project has an automated release set up. So things are only released when 10 | there are useful changes in the code that justify a release. But sometimes 11 | things get messed up one way or another and we need to trigger the release 12 | ourselves. When this happens, simply bump the number below and commit that with 13 | the following commit message based on your needs: 14 | 15 | **Major** 16 | 17 | ``` 18 | fix(release): manually release a major version 19 | 20 | There was an issue with a major release, so this manual-releases.md 21 | change is to release a new major version. 22 | 23 | Reference: # 24 | 25 | BREAKING CHANGE: 26 | ``` 27 | 28 | **Minor** 29 | 30 | ``` 31 | feat(release): manually release a minor version 32 | 33 | There was an issue with a minor release, so this manual-releases.md 34 | change is to release a new minor version. 35 | 36 | Reference: # 37 | ``` 38 | 39 | **Patch** 40 | 41 | ``` 42 | fix(release): manually release a patch version 43 | 44 | There was an issue with a patch release, so this manual-releases.md 45 | change is to release a new patch version. 46 | 47 | Reference: # 48 | ``` 49 | 50 | The number of times we've had to do a manual release is: 2 51 | -------------------------------------------------------------------------------- /src/remove-empty.js: -------------------------------------------------------------------------------- 1 | export {removeEmpty} 2 | 3 | /** 4 | * Accepts an array or an object. In the case of an array, remove all undefined values (using `filter`). 5 | * In the case of an object, remove all keys whose values are undefined. 6 | * 7 | * @example 8 | * // Primary use case is in `plugins` and `entry` where `undefined` values can cause issues 9 | * module.exports = { 10 | * ... your config 11 | * entry: removeEmpty({ 12 | * app: ifProduction('./indexWithoutCSS', './indexWithCSS'), 13 | * css: ifNotProduction('./style.css') 14 | * }), 15 | * plugins: removeEmpty([ 16 | * ifProduction(new webpack.optimize.DedupePlugin()), 17 | * ifProduction(new webpack.LoaderOptionsPlugin({ 18 | * minimize: true, 19 | * quiet: true, 20 | * })), 21 | * ifProduction(new webpack.DefinePlugin({ 22 | * 'process.env': { 23 | * NODE_ENV: '"production"', 24 | * }, 25 | * })), 26 | * ifProduction(new webpack.optimize.UglifyJsPlugin({ 27 | * compress: { 28 | * screw_ie8: true, 29 | * warnings: false, 30 | * }, 31 | * })), 32 | * new HtmlWebpackPlugin({ 33 | * template: './index.html', 34 | * inject: 'head', 35 | * }), 36 | * ifProduction(new OfflinePlugin()), 37 | * ]), 38 | * } 39 | * 40 | * @param {object | Array} input The object to remove keys from or the array to remove values from 41 | * @returns {object | Array} The resulting object or array. 42 | */ 43 | 44 | function removeEmpty(input) { 45 | let output 46 | if (Array.isArray(input)) { 47 | output = input.filter(item => typeof item !== 'undefined') 48 | } else { 49 | output = {} 50 | Object.keys(input).forEach(key => { 51 | const value = input[key] 52 | if (typeof value !== 'undefined') { 53 | output[key] = value 54 | } 55 | }) 56 | } 57 | return output 58 | } 59 | -------------------------------------------------------------------------------- /package-scripts.js: -------------------------------------------------------------------------------- 1 | const concurrent = require('nps-utils').concurrent 2 | const rimraf = require('nps-utils').rimraf 3 | const series = require('nps-utils').series 4 | 5 | module.exports = { 6 | scripts: { 7 | commit: { 8 | description: 'This uses commitizen to help us generate well formatted commit messages', 9 | script: 'git-cz', 10 | }, 11 | test: { 12 | default: { 13 | script: 'cross-env NODE_ENV=test nyc ava', 14 | }, 15 | watch: { 16 | description: 'Run AVA in watch mode', 17 | script: 'ava -w --require babel-register', 18 | }, 19 | }, 20 | build: { 21 | description: 'delete the dist directory and run babel to build the files', 22 | script: series(rimraf('dist'), 'babel --copy-files --out-dir dist --ignore *.test.js src'), 23 | }, 24 | lint: { 25 | description: 'lint the entire project', 26 | script: 'eslint .', 27 | }, 28 | reportCoverage: { 29 | description: 'Report coverage stats to codecov. This should be run after the `test` script', 30 | script: 'codecov', 31 | }, 32 | release: { 33 | description: 'We automate releases with semantic-release. This should only be run on travis', 34 | script: series('semantic-release pre', 'npm publish', 'semantic-release post'), 35 | }, 36 | validate: { 37 | description: 'This runs several scripts to make sure things look good before committing or on clean install', 38 | script: concurrent.nps('lint', 'build', 'test'), 39 | }, 40 | addContributor: { 41 | description: 'When new people contribute to the project, run this', 42 | script: 'all-contributors add', 43 | }, 44 | generateContributors: { 45 | description: 'Update the badge and contributors table', 46 | script: 'all-contributors generate', 47 | }, 48 | }, 49 | options: { 50 | silent: false, 51 | }, 52 | } 53 | -------------------------------------------------------------------------------- /src/get-if-utils.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import {difference} from 'lodash' 3 | import {getIfUtils} from './get-if-utils' 4 | 5 | test('has if and ifNot methods for the expected environment values', t => { 6 | const expectedMethods = [ 7 | 'ifProduction', 'ifNotProduction', 8 | 'ifProd', 'ifNotProd', 9 | 'ifTest', 'ifNotTest', 10 | 'ifDevelopment', 'ifNotDevelopment', 11 | 'ifDev', 'ifNotDev', 12 | ] 13 | const utils = getIfUtils({}) 14 | const methods = Object.keys(utils) 15 | const diff = difference(methods, expectedMethods) 16 | t.deepEqual(diff, []) 17 | }) 18 | test('has ifXXX and ifNotXXX methods for the expected environment values', t => { 19 | const expectedMethods = [ 20 | 'ifFoo', 'ifNotFoo', 21 | 'ifBar', 'ifNotBar', 22 | ] 23 | const utils = getIfUtils({}, ['foo', 'bar']) 24 | const methods = Object.keys(utils) 25 | const diff = difference(methods, expectedMethods) 26 | t.deepEqual(diff, []) 27 | }) 28 | 29 | test('works with string values', t => { 30 | const {ifProduction} = getIfUtils('production') 31 | t.is(ifProduction('value', 'alternate'), 'value') 32 | }) 33 | 34 | test('works with an env object', t => { 35 | const {ifNotDev} = getIfUtils({dev: false}) 36 | t.is(ifNotDev('value', 'alternate'), 'value') 37 | }) 38 | 39 | test('throws an error when given something other than a string or object', t => { 40 | t.throws(() => getIfUtils(false), /webpack-config-utils:getIfUtils.*?string\/Object/) 41 | }) 42 | 43 | test('returns true/false when given no arguments', t => { 44 | const {ifTest, ifProd, ifNotDev} = getIfUtils('test') 45 | t.is(ifTest(), true) 46 | t.is(ifProd(), false) 47 | t.is(ifNotDev(), true) 48 | }) 49 | 50 | test('returns true/false for custom ifXXX when given no arguments', t => { 51 | const {ifWatch, ifProd, ifNotDev, ifTest} = getIfUtils('watch', ['prod', 'dev', 'watch']) 52 | t.is(ifWatch(), true) 53 | t.is(ifProd(), false) 54 | t.is(ifNotDev(), true) 55 | t.is(ifTest, undefined) 56 | }) 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-config-utils", 3 | "version": "0.0.0-semantically-released", 4 | "description": "Utilities to help your webpack config be easier to read", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "start": "nps", 8 | "test": "nps test" 9 | }, 10 | "files": [ 11 | "dist" 12 | ], 13 | "keywords": [], 14 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 15 | "license": "MIT", 16 | "dependencies": { 17 | "webpack-combine-loaders": "2.0.4" 18 | }, 19 | "bundledDependencies": [ 20 | "webpack-combine-loaders" 21 | ], 22 | "devDependencies": { 23 | "all-contributors-cli": "^3.0.0", 24 | "ava": "^0.15.2", 25 | "babel-cli": "^6.7.7", 26 | "babel-plugin-istanbul": "^1.0.3", 27 | "babel-preset-es2015": "^6.6.0", 28 | "babel-preset-stage-2": "^6.5.0", 29 | "babel-register": "^6.7.2", 30 | "codecov": "^1.0.1", 31 | "commitizen": "^2.8.1", 32 | "condition-node-version": "^1.3.0", 33 | "cross-env": "^2.0.0", 34 | "cz-conventional-changelog": "^1.1.6", 35 | "eslint": "^3.1.1", 36 | "eslint-config-kentcdodds": "^8.1.2", 37 | "ghooks": "^1.2.1", 38 | "lodash": "4.13.1", 39 | "nps": "^5.3.1", 40 | "nps-utils": "^1.2.0", 41 | "nyc": "^7.0.0", 42 | "opt-cli": "^1.4.2", 43 | "semantic-release": "^6.2.1", 44 | "validate-commit-msg": "^2.6.1" 45 | }, 46 | "eslintConfig": { 47 | "extends": [ 48 | "kentcdodds", 49 | "kentcdodds/ava" 50 | ], 51 | "rules": { 52 | "import/prefer-default-export": 0 53 | } 54 | }, 55 | "nyc": { 56 | "all": true, 57 | "check-coverage": true, 58 | "branches": 100, 59 | "function": 100, 60 | "lines": 100, 61 | "statements": 100, 62 | "reporter": [ 63 | "text", 64 | "lcov" 65 | ], 66 | "include": [ 67 | "src" 68 | ], 69 | "sourceMap": false, 70 | "instrument": false, 71 | "require": [ 72 | "babel-register" 73 | ] 74 | }, 75 | "release": { 76 | "verifyConditions": { 77 | "path": "condition-node-version", 78 | "node": "^6" 79 | } 80 | }, 81 | "config": { 82 | "ghooks": { 83 | "commit-msg": "opt --in commit-msg --exec \"validate-commit-msg\"", 84 | "pre-commit": "opt --in pre-commit --exec \"npm start validate\"" 85 | }, 86 | "commitizen": { 87 | "path": "node_modules/cz-conventional-changelog" 88 | } 89 | }, 90 | "repository": { 91 | "type": "git", 92 | "url": "https://github.com/kentcdodds/webpack-config-utils.git" 93 | }, 94 | "bugs": { 95 | "url": "https://github.com/kentcdodds/webpack-config-utils/issues" 96 | }, 97 | "homepage": "https://github.com/kentcdodds/webpack-config-utils#readme" 98 | } 99 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for being willing to contribute! 4 | 5 | **Working on your first Pull Request?** You can learn how from this *free* series 6 | [How to Contribute to an Open Source Project on GitHub][egghead] 7 | 8 | ## Project setup 9 | 10 | 1. Fork and clone the repo 11 | 2. `$ npm install` to install dependencies 12 | 3. `$ npm start validate` to validate you've got it working 13 | 4. Create a branch for your PR 14 | 15 | This project uses [`nps`][nps] and you can run `npm start` to see what scripts are available. 16 | 17 | ## Add yourself as a contributor 18 | 19 | This project follows the [all contributors][all-contributors] specification. To add yourself to the table of 20 | contributors on the README.md, please use the automated script as part of your PR: 21 | 22 | ```console 23 | npm start addContributor 24 | ``` 25 | 26 | Follow the prompt. If you've already added yourself to the list and are making a new type of contribution, you can run 27 | it again and select the added contribution type. 28 | 29 | ## Committing and Pushing changes 30 | 31 | This project uses [`semantic-release`][semantic-release] to do automatic releases and generate a changelog based on the 32 | commit history. So we follow [a convention][convention] for commit messages. Please follow this convention for your 33 | commit messages. 34 | 35 | You can use `commitizen` to help you to follow [the convention][convention] 36 | 37 | Once you are ready to commit the changes, please use the below commands 38 | 39 | 1. `git add ` 40 | 2. `$ npm start commit` 41 | 42 | ... and follow the instruction of the interactive prompt. 43 | 44 | ### opt into git hooks 45 | 46 | There are git hooks set up with this project that are automatically installed when you install dependencies. They're 47 | really handy, but are turned off by default (so as to not hinder new contributors). You can opt into these by creating 48 | a file called `.opt-in` at the root of the project and putting this inside: 49 | 50 | ``` 51 | commit-msg 52 | pre-commit 53 | ``` 54 | 55 | ## Help needed 56 | 57 | Please checkout the [ROADMAP.md](https://github.com/kentcdodds/webpack-config-utils/blob/master/other/ROADMAP.md) and raise an issue to discuss 58 | any of the items in the want to do or might do list. 59 | 60 | Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks! 61 | 62 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github 63 | [semantic-release]: https://npmjs.com/package/semantic-release 64 | [convention]: https://github.com/conventional-changelog/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md 65 | [all-contributors]: https://github.com/kentcdodds/all-contributors 66 | [ROADMAP]: ./ROADMAP.md 67 | [nps]: https://npmjs.com/package/nps 68 | -------------------------------------------------------------------------------- /src/get-if-utils.js: -------------------------------------------------------------------------------- 1 | import {propIf, propIfNot} from './prop-if' 2 | 3 | export {getIfUtils} 4 | 5 | /** 6 | * The object returned from getIfUtils 7 | * @typedef {Object} IfUtils 8 | * @property {function} ifProduction - a wrapper around `propIf` function that returns the given `value` if the 9 | * environment is `"production"` or the `alternate` if not 10 | * @property {function} ifNotProduction, - a wrapper around `propIf` function that returns the given `value` if the 11 | * environment is not `"production"` or the `alternate` if it is 12 | * @property {function} ifProd - a wrapper around `propIf` function that returns the given `value` if the environment is 13 | * `"prod"` or the `alternate` if not 14 | * @property {function} ifNotProd, - a wrapper around `propIf` function that returns the given `value` if the 15 | * environment is not `"prod"` or the `alternate` if it is 16 | * @property {function} ifTest - a wrapper around `propIf` function that returns the given `value` if the environment is 17 | * `"test"` or the `alternate` if not 18 | * @property {function} ifNotTest, - a wrapper around `propIf` function that returns the given `value` if the 19 | * environment is not `"test"` or the `alternate` if it is 20 | * @property {function} ifDevelopment - a wrapper around `propIf` function that returns the given `value` if the 21 | * environment is `"development"` or the `alternate` if not 22 | * @property {function} ifNotDevelopment, - a wrapper around `propIf` function that returns the given `value` if the 23 | * environment is not `"development"` or the `alternate` if it is 24 | * @property {function} ifDev - a wrapper around `propIf` function that returns the given `value` if the environment is 25 | * `"dev"` or the `alternate` if not 26 | * @property {function} ifNotDev, - a wrapper around `propIf` function that returns the given `value` if the 27 | * environment is not `"dev"` or the `alternate` if it is 28 | */ 29 | 30 | /** 31 | * This returns an object with methods to help you conditionally set values to your webpack configuration object. 32 | * @param {Object|string} env This would be either the `env` object from webpack (function config API) or the value of 33 | * `process.env.NODE_ENV`. 34 | * @param {Array} vars A list of valid environments if utils are generated for 35 | * @return {IfUtils} the IfUtils object for the given environment 36 | */ 37 | function getIfUtils(env, vars = ['production', 'prod', 'test', 'development', 'dev']) { 38 | env = typeof env === 'string' ? {[env]: true} : env 39 | if (typeof env !== 'object') { 40 | throw new Error( 41 | `webpack-config-utils:getIfUtils: env passed should be a string/Object. Was ${JSON.stringify(env)}` 42 | ) 43 | } 44 | return vars.reduce((utils, variable) => { 45 | const envValue = !!env[variable] 46 | const capitalVariable = capitalizeWord(variable) 47 | utils[`if${capitalVariable}`] = (value, alternate) => { 48 | return isUndefined(value) ? envValue : propIf(envValue, value, alternate) 49 | } 50 | utils[`ifNot${capitalVariable}`] = (value, alternate) => { 51 | return isUndefined(value) ? !envValue : propIfNot(envValue, value, alternate) 52 | } 53 | return utils 54 | }, {}) 55 | } 56 | 57 | // utilities 58 | 59 | function capitalizeWord(word) { 60 | return word.substring(0, 1).toUpperCase() + word.substring(1) 61 | } 62 | 63 | function isUndefined(val) { 64 | return typeof val === 'undefined' 65 | } 66 | -------------------------------------------------------------------------------- /other/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kent+coc@doddsfamily.us. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "webpack-config-utils", 3 | "projectOwner": "kentcdodds", 4 | "files": [ 5 | "README.md" 6 | ], 7 | "imageSize": 100, 8 | "commit": false, 9 | "contributors": [ 10 | { 11 | "login": "kentcdodds", 12 | "name": "Kent C. Dodds", 13 | "avatar_url": "https://avatars.githubusercontent.com/u/1500684?v=3", 14 | "profile": "https://kentcdodds.com", 15 | "contributions": [ 16 | "code", 17 | "doc", 18 | "example", 19 | "infra", 20 | "test" 21 | ] 22 | }, 23 | { 24 | "login": "brenoc", 25 | "name": "Breno Calazans", 26 | "avatar_url": "https://avatars.githubusercontent.com/u/284515?v=3", 27 | "profile": "https://twitter.com/breno_calazans", 28 | "contributions": [ 29 | "example" 30 | ] 31 | }, 32 | { 33 | "login": "tamouse", 34 | "name": "Tamara Temple", 35 | "avatar_url": "https://avatars.githubusercontent.com/u/363583?v=3", 36 | "profile": "http://tamouse.org", 37 | "contributions": [ 38 | "doc" 39 | ] 40 | }, 41 | { 42 | "login": "benhalverson", 43 | "name": "Ben Halverson", 44 | "avatar_url": "https://avatars.githubusercontent.com/u/7907232?v=3", 45 | "profile": "benhalverson.me", 46 | "contributions": [ 47 | "doc" 48 | ] 49 | }, 50 | { 51 | "login": "huy-nguyen", 52 | "name": "Huy Nguyen", 53 | "avatar_url": "https://avatars.githubusercontent.com/u/7352279?v=3", 54 | "profile": "http://www.huy-nguyen.com/", 55 | "contributions": [ 56 | "code", 57 | "doc", 58 | "example", 59 | "test" 60 | ] 61 | }, 62 | { 63 | "login": "ryandrewjohnson", 64 | "name": "Ryan Johnson", 65 | "avatar_url": "https://avatars.githubusercontent.com/u/3419547?v=3", 66 | "profile": "https://github.com/ryandrewjohnson", 67 | "contributions": [ 68 | "blog", 69 | "doc" 70 | ] 71 | }, 72 | { 73 | "login": "adamdicarlo", 74 | "name": "Adam DiCarlo", 75 | "avatar_url": "https://avatars1.githubusercontent.com/u/97462?v=3", 76 | "profile": "http://adamdicarlo.com", 77 | "contributions": [ 78 | "doc", 79 | "tool" 80 | ] 81 | }, 82 | { 83 | "login": "jezzay", 84 | "name": "Jeremy Y", 85 | "avatar_url": "https://avatars2.githubusercontent.com/u/5779101?v=4", 86 | "profile": "https://github.com/jezzay", 87 | "contributions": [ 88 | "doc" 89 | ] 90 | }, 91 | { 92 | "login": "seanyesmunt", 93 | "name": "Sean Yesmunt", 94 | "avatar_url": "https://avatars0.githubusercontent.com/u/16882830?v=4", 95 | "profile": "http://seanyesmunt.com", 96 | "contributions": [ 97 | "doc" 98 | ] 99 | }, 100 | { 101 | "login": "jdorfman", 102 | "name": "Justin Dorfman", 103 | "avatar_url": "https://avatars1.githubusercontent.com/u/398230?v=4", 104 | "profile": "https://stackshare.io/jdorfman/decisions", 105 | "contributions": [ 106 | "fundingFinding" 107 | ] 108 | }, 109 | { 110 | "login": "andrewmcodes", 111 | "name": "Andrew Mason", 112 | "avatar_url": "https://avatars1.githubusercontent.com/u/18423853?v=4", 113 | "profile": "https://www.andrewm.codes", 114 | "contributions": [ 115 | "doc" 116 | ] 117 | }, 118 | { 119 | "login": "gitKendra", 120 | "name": "Kendra Zolner", 121 | "avatar_url": "https://avatars1.githubusercontent.com/u/29556502?v=4", 122 | "profile": "https://github.com/gitKendra", 123 | "contributions": [ 124 | "doc" 125 | ] 126 | } 127 | ], 128 | "contributorsPerLine": 7, 129 | "repoType": "github", 130 | "repoHost": "https://github.com", 131 | "skipCi": false 132 | } 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webpack-config-utils 2 | 3 | Utilities to help your webpack config be easier to read 4 | 5 | [![Build Status][build-badge]][build] 6 | [![Code Coverage][coverage-badge]][coverage] 7 | [![Dependencies][dependencyci-badge]][dependencyci] 8 | [![version][version-badge]][package] 9 | [![downloads][downloads-badge]][npm-stat] 10 | [![MIT License][license-badge]][LICENSE] 11 | 12 | [![All Contributors](https://img.shields.io/badge/all_contributors-12-orange.svg?style=flat-square)](#contributors) 13 | [![PRs Welcome][prs-badge]][prs] 14 | [![Donate][donate-badge]][donate] 15 | [![Code of Conduct][coc-badge]][coc] 16 | [![Roadmap][roadmap-badge]][roadmap] 17 | [![Examples][examples-badge]][examples] 18 | 19 | ## The problem 20 | 21 | Webpack configuration is a JavaScript object which is awesomely declarative. However, the webpack config file 22 | can easily turn into an imperative mess in the process of creating the configuration object. 23 | 24 | ## This solution 25 | 26 | The goal of this project is to provide utilities to make it easier to compose your config object so it's easier for 27 | people to read. It has some custom methods and also comes bundled with some other projects to expose some helpful 28 | utility functions. 29 | 30 | ## Installation 31 | 32 | This module is distributed via [npm][npm] which is bundled with [node][node] and should 33 | be installed as one of your project's `devDependencies`: 34 | 35 | ``` 36 | npm install --save-dev webpack-config-utils 37 | ``` 38 | 39 | ## Usage 40 | 41 | It is expected that you use this in your `webpack.config.js` file. 42 | 43 | > Protip: You can name your config file `webpack.config.babel.js` and 44 | > it'll be automagically transpiled! (Make sure you have 45 | > `babel-register` installed.) So you could use ES6 module imports 46 | > rather than CommonJS requires. But this example will stick to 47 | > CommonJS because we love you ❤️ 48 | 49 | ```javascript 50 | const webpack = require('webpack') 51 | const {getIfUtils, removeEmpty} = require('webpack-config-utils') 52 | 53 | const {ifProduction, ifNotProduction} = getIfUtils(process.env.NODE_ENV) 54 | 55 | module.exports = { 56 | // ... your config 57 | mode: ifProduction('production', 'development'), 58 | entry: removeEmpty({ 59 | app: ifProduction('./indexWithoutCSS', './indexWithCSS'), 60 | css: ifProduction('./style.scss') 61 | }), 62 | output: { 63 | chunkFilename: ifProduction('js/[id].[contenthash].js', 'js/[name].js'), 64 | filename: ifProduction('js/[id].[contenthash].js', 'js/[name].js'), 65 | }, 66 | module: { 67 | rules: [ 68 | { 69 | test: /\.(sc|c)ss$/, 70 | exclude: /node_modules/, 71 | use: removeEmpty([ 72 | ifProduction(MiniCssExtractPlugin.loader), 73 | ifNotProduction({loader: 'style-loader', options: {sourceMap: true}}), 74 | {loader: 'css-loader', options: {sourceMap: true}}, 75 | {loader: 'postcss-loader', options: {sourceMap: true}}, 76 | {loader: 'sass-loader', options: {sourceMap: true}}, 77 | ]), 78 | }, 79 | }, 80 | plugins: removeEmpty([ 81 | ifProduction( 82 | new MiniCssExtractPlugin({ 83 | filename: 'css/[id].[contenthash].css', 84 | }) 85 | ), 86 | ifProduction(new webpack.DefinePlugin({ 87 | 'process.env': { 88 | NODE_ENV: '"production"', 89 | }, 90 | })), 91 | new HtmlWebpackPlugin({ 92 | template: './index.html', 93 | inject: 'head', 94 | }), 95 | ifProduction(new OfflinePlugin()), 96 | ]), 97 | } 98 | ``` 99 | 100 | Then you'd invoke the webpack config with [`cross-env`][cross-env] in your `package.json` scripts (or with 101 | [`nps`][nps]): 102 | 103 | ```js 104 | { 105 | // your package.json stuff 106 | scripts: { 107 | "build:dev": "cross-env NODE_ENV=development webpack", 108 | "build:prod": "cross-env NODE_ENV=production webpack" 109 | } 110 | } 111 | ``` 112 | 113 | --- 114 | 115 | Things get even better with webpack 2+ because you can write your webpack config as a function that accepts an `env` 116 | argument which you can set on the command line (which means you don't need `cross-env` 👍). 117 | 118 | ```javascript 119 | const webpack = require('webpack') 120 | const {resolve} = require('path') 121 | const {getIfUtils} = require('webpack-config-utils') 122 | 123 | module.exports = env => { 124 | const {ifDev} = getIfUtils(env) 125 | return { 126 | output: { 127 | // etc. 128 | pathinfo: ifDev(), 129 | path: resolve(__dirname, 'dist'), 130 | }, 131 | // etc. 132 | } 133 | } 134 | ``` 135 | 136 | ## API 137 | 138 | The API Documentation is still to come. In addition to custom utilities from this package, it comes bundled with 139 | another helpful utility: [`webpack-combine-loaders`](https://www.npmjs.com/package/webpack-combine-loaders) (exposed as `combineLoaders`). 140 | 141 | ## Articles 142 | 143 | * [One webpack config to rule them all — environments that is](https://medium.com/@ryandrewjohnson/one-webpack-config-to-rule-them-all-environments-that-is-277457769779#.34laieb5i) 144 | 145 | ## Contributors 146 | 147 | Thanks goes to these people ([emoji key][emojis]): 148 | 149 | 150 | | [
Kent C. Dodds](https://kentcdodds.com)
[💻](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) [📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) 💡 🚇 [⚠️](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) | [
Breno Calazans](https://twitter.com/breno_calazans)
💡 | [
Tamara Temple](http://tamouse.org)
[📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=tamouse) | [
Ben Halverson](benhalverson.me)
[📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=benhalverson) | [
Huy Nguyen](http://www.huy-nguyen.com/)
[💻](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) [📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) 💡 [⚠️](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) | [
Ryan Johnson](https://github.com/ryandrewjohnson)
📝 [📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=ryandrewjohnson) | [
Adam DiCarlo](http://adamdicarlo.com)
[📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=adamdicarlo) 🔧 | 151 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 152 | | [
Jeremy Y](https://github.com/jezzay)
[📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=jezzay) | [
Sean Yesmunt](http://seanyesmunt.com)
[📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=seanyesmunt) | [
Justin Dorfman](https://stackshare.io/jdorfman/decisions)
🔍 | [
Andrew Mason](https://www.andrewm.codes)
[📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=andrewmcodes) | [
Kendra Zolner](https://github.com/gitKendra)
[📖](https://github.com/kentcdodds/webpack-config-utils/commits?author=gitKendra) | 153 | 154 | 155 | This project follows the [all-contributors][all-contributors] specification. Contributions of any kind welcome! 156 | 157 | ## LICENSE 158 | 159 | MIT 160 | 161 | [npm]: https://www.npmjs.com/ 162 | [node]: https://nodejs.org 163 | [build-badge]: https://img.shields.io/travis/kentcdodds/webpack-config-utils.svg?style=flat-square 164 | [build]: https://travis-ci.org/kentcdodds/webpack-config-utils 165 | [coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/webpack-config-utils.svg?style=flat-square 166 | [coverage]: https://codecov.io/github/kentcdodds/webpack-config-utils 167 | [dependencyci-badge]: https://dependencyci.com/github/kentcdodds/webpack-config-utils/badge?style=flat-square 168 | [dependencyci]: https://dependencyci.com/github/kentcdodds/webpack-config-utils 169 | [version-badge]: https://img.shields.io/npm/v/webpack-config-utils.svg?style=flat-square 170 | [package]: https://www.npmjs.com/package/webpack-config-utils 171 | [downloads-badge]: https://img.shields.io/npm/dm/webpack-config-utils.svg?style=flat-square 172 | [npm-stat]: http://npm-stat.com/charts.html?package=webpack-config-utils&from=2016-04-01 173 | [license-badge]: https://img.shields.io/npm/l/webpack-config-utils.svg?style=flat-square 174 | [license]: https://github.com/kentcdodds/webpack-config-utils/blob/master/other/LICENSE 175 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 176 | [prs]: http://makeapullrequest.com 177 | [donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square 178 | [donate]: http://kcd.im/donate 179 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square 180 | [coc]: https://github.com/kentcdodds/webpack-config-utils/blob/master/other/CODE_OF_CONDUCT.md 181 | [roadmap-badge]: https://img.shields.io/badge/%F0%9F%93%94-roadmap-CD9523.svg?style=flat-square 182 | [roadmap]: https://github.com/kentcdodds/webpack-config-utils/blob/master/other/ROADMAP.md 183 | [examples-badge]: https://img.shields.io/badge/%F0%9F%92%A1-examples-8C8E93.svg?style=flat-square 184 | [examples]: https://github.com/kentcdodds/webpack-config-utils/blob/master/other/EXAMPLES.md 185 | [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key 186 | [all-contributors]: https://github.com/kentcdodds/all-contributors 187 | [cross-env]: https://www.npmjs.com/package/cross-env 188 | [nps]: https://www.npmjs.com/package/nps 189 | --------------------------------------------------------------------------------