├── .circleci └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── funding.yml ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── codecov.yml ├── commitlint.config.js ├── lib ├── client.js ├── index.js ├── middleware.js └── validate.js ├── package-lock.json ├── package.json └── test ├── fixtures ├── input.js └── webpack.config.js └── test.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | dependency_cache: 4 | docker: 5 | - image: rollupcabal/circleci-node-base:latest 6 | steps: 7 | - checkout 8 | - restore_cache: 9 | key: dependency-cache-{{ checksum "package-lock.json" }} 10 | - run: 11 | name: Install Dependencies 12 | command: npm install 13 | - save_cache: 14 | key: dependency-cache-{{ checksum "package-lock.json" }} 15 | paths: 16 | - ./node_modules 17 | node-v10-latest: 18 | docker: 19 | - image: rollupcabal/circleci-node-v10:latest 20 | steps: 21 | - checkout 22 | - restore_cache: 23 | key: dependency-cache-{{ checksum "package-lock.json" }} 24 | - run: 25 | name: NPM Rebuild 26 | command: npm install 27 | - run: 28 | name: Run unit tests. 29 | command: npm run ci:test 30 | node-v12-latest: 31 | docker: 32 | - image: rollupcabal/circleci-node-v12:latest 33 | steps: 34 | - checkout 35 | - restore_cache: 36 | key: dependency-cache-{{ checksum "package-lock.json" }} 37 | - run: 38 | name: NPM Rebuild 39 | command: npm install 40 | - run: 41 | name: Run unit tests. 42 | command: npm run ci:coverage 43 | - run: 44 | name: Submit coverage data to codecov. 45 | command: bash <(curl -s https://codecov.io/bash) 46 | when: on_success 47 | analysis: 48 | docker: 49 | - image: rollupcabal/circleci-node-base:latest 50 | steps: 51 | - checkout 52 | - restore_cache: 53 | key: dependency-cache-{{ checksum "package-lock.json" }} 54 | - run: 55 | name: NPM Rebuild 56 | command: npm install 57 | - run: 58 | name: Run linting. 59 | command: npm run lint 60 | - run: 61 | name: Run Security Check 62 | command: npm run security 63 | - run: 64 | name: Validate Commit Messages 65 | command: npm run ci:lint:commits 66 | workflows: 67 | version: 2 68 | validate: 69 | jobs: 70 | - dependency_cache 71 | - analysis: 72 | requires: 73 | - dependency_cache 74 | filters: 75 | tags: 76 | only: /.*/ 77 | - node-v10-latest: 78 | requires: 79 | - analysis 80 | filters: 81 | tags: 82 | only: /.*/ 83 | - node-v12-latest: 84 | requires: 85 | - analysis 86 | filters: 87 | tags: 88 | only: /.*/ 89 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | insert_final_newline = false 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | index-old.js 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "shellscape", 3 | "globals": { 4 | "document": true, 5 | "WebSocket": true, 6 | "window": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json -diff 2 | * text=auto 3 | bin/* eol=lf -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome! 2 | > Thanks ahead for taking the time to contribute to this project 3 | 4 | ## What We Use 5 | 6 | - Testing: [Ava](https://github.com/avajs/ava) 7 | - Linting: [ESLint](http://eslint.org/) 8 | - Building: [Webpack](https://webpack.github.io/) 9 | 10 | ## Pull Requests 11 | 12 | Please don't commit `package-lock.json`. 13 | 14 | Please don't change variable or parameter names to match your 15 | personal prefrences, unless the change is part of a refactor 16 | or significant modification of the codebase (which is very rare). 17 | 18 | Please remember to thoroughly explain your Pull Request if it 19 | doesn't have an associated issue. If you're changing code 20 | significantly, please remember to add inline or block comments 21 | in the code as appropriate. 22 | 23 | ## Thanks 24 | 25 | For your interest, understanding, and for following this simple guide. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > The Issues page for this repository is not a support forum, it is for reporting potential bugs in this module, which 2 | [composes](https://github.com/koajs/compose) webpack-hot-middleware and webpack-dev-middle for use with koa2. 3 | If you have arrived here because you cannot get [webpack-hot-middleware](https://github.com/glenjamin/webpack-hot-middlewar) 4 | or [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware/) working, please review the documentation 5 | for the respective middleware you are experiencing a problem with. If you proceed with this form, please fill out *_all_* fields, or your issue may be closed as "invalid." Please remove this header to acknowledge this message. 6 | 7 | * Node Version: 8 | * NPM Version: 9 | * koa Version: 10 | * koa-wepback Version: 11 | 12 | If you have a large amount of code to share which demonstrates the problem you're experiencing, please provide a link to your 13 | repository rather than pasting code. Otherwise, please paste relevant short snippets below. 14 | 15 | ```js 16 | // webpack.config.js 17 | ``` 18 | 19 | ```js 20 | // app.js 21 | ``` 22 | 23 | ### Expected Behavior 24 | 25 | ### Actual Behavior 26 | 27 | ### How can we reproduce the behavior? 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | This PR contains a: 12 | 13 | - [ ] **bugfix** 14 | - [ ] new **feature** 15 | - [ ] **code refactor** 16 | - [ ] **test update** 17 | - [ ] **typo fix** 18 | - [ ] **metadata update** 19 | 20 | ### Motivation / Use-Case 21 | 22 | 27 | 28 | ### Breaking Changes 29 | 30 | 34 | 35 | ### Additional Info -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | patreon: shellscape 2 | custom: https://paypal.me/shellscape 3 | liberapay: shellscape 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | coverage 4 | node_modules 5 | 6 | .nyc_output 7 | coverage.lcov 8 | 9 | .eslintcache 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "arrowParens": "always" 5 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Andrew Powell 2 | 3 | Mozilla Public License Version 2.0 4 | ================================== 5 | 6 | 1. Definitions 7 | -------------- 8 | 9 | 1.1. "Contributor" 10 | means each individual or legal entity that creates, contributes to 11 | the creation of, or owns Covered Software. 12 | 13 | 1.2. "Contributor Version" 14 | means the combination of the Contributions of others (if any) used 15 | by a Contributor and that particular Contributor's Contribution. 16 | 17 | 1.3. "Contribution" 18 | means Covered Software of a particular Contributor. 19 | 20 | 1.4. "Covered Software" 21 | means Source Code Form to which the initial Contributor has attached 22 | the notice in Exhibit A, the Executable Form of such Source Code 23 | Form, and Modifications of such Source Code Form, in each case 24 | including portions thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | (a) that the initial Contributor has attached the notice described 30 | in Exhibit B to the Covered Software; or 31 | 32 | (b) that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the 34 | terms of a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | means any form of the work other than Source Code Form. 38 | 39 | 1.7. "Larger Work" 40 | means a work that combines Covered Software with other material, in 41 | a separate file or files, that is not Covered Software. 42 | 43 | 1.8. "License" 44 | means this document. 45 | 46 | 1.9. "Licensable" 47 | means having the right to grant, to the maximum extent possible, 48 | whether at the time of the initial grant or subsequently, any and 49 | all of the rights conveyed by this License. 50 | 51 | 1.10. "Modifications" 52 | means any of the following: 53 | 54 | (a) any file in Source Code Form that results from an addition to, 55 | deletion from, or modification of the contents of Covered 56 | Software; or 57 | 58 | (b) any new file in Source Code Form that contains any Covered 59 | Software. 60 | 61 | 1.11. "Patent Claims" of a Contributor 62 | means any patent claim(s), including without limitation, method, 63 | process, and apparatus claims, in any patent Licensable by such 64 | Contributor that would be infringed, but for the grant of the 65 | License, by the making, using, selling, offering for sale, having 66 | made, import, or transfer of either its Contributions or its 67 | Contributor Version. 68 | 69 | 1.12. "Secondary License" 70 | means either the GNU General Public License, Version 2.0, the GNU 71 | Lesser General Public License, Version 2.1, the GNU Affero General 72 | Public License, Version 3.0, or any later versions of those 73 | licenses. 74 | 75 | 1.13. "Source Code Form" 76 | means the form of the work preferred for making modifications. 77 | 78 | 1.14. "You" (or "Your") 79 | means an individual or a legal entity exercising rights under this 80 | License. For legal entities, "You" includes any entity that 81 | controls, is controlled by, or is under common control with You. For 82 | purposes of this definition, "control" means (a) the power, direct 83 | or indirect, to cause the direction or management of such entity, 84 | whether by contract or otherwise, or (b) ownership of more than 85 | fifty percent (50%) of the outstanding shares or beneficial 86 | ownership of such entity. 87 | 88 | 2. License Grants and Conditions 89 | -------------------------------- 90 | 91 | 2.1. Grants 92 | 93 | Each Contributor hereby grants You a world-wide, royalty-free, 94 | non-exclusive license: 95 | 96 | (a) under intellectual property rights (other than patent or trademark) 97 | Licensable by such Contributor to use, reproduce, make available, 98 | modify, display, perform, distribute, and otherwise exploit its 99 | Contributions, either on an unmodified basis, with Modifications, or 100 | as part of a Larger Work; and 101 | 102 | (b) under Patent Claims of such Contributor to make, use, sell, offer 103 | for sale, have made, import, and otherwise transfer either its 104 | Contributions or its Contributor Version. 105 | 106 | 2.2. Effective Date 107 | 108 | The licenses granted in Section 2.1 with respect to any Contribution 109 | become effective for each Contribution on the date the Contributor first 110 | distributes such Contribution. 111 | 112 | 2.3. Limitations on Grant Scope 113 | 114 | The licenses granted in this Section 2 are the only rights granted under 115 | this License. No additional rights or licenses will be implied from the 116 | distribution or licensing of Covered Software under this License. 117 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 118 | Contributor: 119 | 120 | (a) for any code that a Contributor has removed from Covered Software; 121 | or 122 | 123 | (b) for infringements caused by: (i) Your and any other third party's 124 | modifications of Covered Software, or (ii) the combination of its 125 | Contributions with other software (except as part of its Contributor 126 | Version); or 127 | 128 | (c) under Patent Claims infringed by Covered Software in the absence of 129 | its Contributions. 130 | 131 | This License does not grant any rights in the trademarks, service marks, 132 | or logos of any Contributor (except as may be necessary to comply with 133 | the notice requirements in Section 3.4). 134 | 135 | 2.4. Subsequent Licenses 136 | 137 | No Contributor makes additional grants as a result of Your choice to 138 | distribute the Covered Software under a subsequent version of this 139 | License (see Section 10.2) or under the terms of a Secondary License (if 140 | permitted under the terms of Section 3.3). 141 | 142 | 2.5. Representation 143 | 144 | Each Contributor represents that the Contributor believes its 145 | Contributions are its original creation(s) or it has sufficient rights 146 | to grant the rights to its Contributions conveyed by this License. 147 | 148 | 2.6. Fair Use 149 | 150 | This License is not intended to limit any rights You have under 151 | applicable copyright doctrines of fair use, fair dealing, or other 152 | equivalents. 153 | 154 | 2.7. Conditions 155 | 156 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 157 | in Section 2.1. 158 | 159 | 3. Responsibilities 160 | ------------------- 161 | 162 | 3.1. Distribution of Source Form 163 | 164 | All distribution of Covered Software in Source Code Form, including any 165 | Modifications that You create or to which You contribute, must be under 166 | the terms of this License. You must inform recipients that the Source 167 | Code Form of the Covered Software is governed by the terms of this 168 | License, and how they can obtain a copy of this License. You may not 169 | attempt to alter or restrict the recipients' rights in the Source Code 170 | Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | (a) such Covered Software must also be made available in Source Code 177 | Form, as described in Section 3.1, and You must inform recipients of 178 | the Executable Form how they can obtain a copy of such Source Code 179 | Form by reasonable means in a timely manner, at a charge no more 180 | than the cost of distribution to the recipient; and 181 | 182 | (b) You may distribute such Executable Form under the terms of this 183 | License, or sublicense it under different terms, provided that the 184 | license for the Executable Form does not attempt to limit or alter 185 | the recipients' rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for 191 | the Covered Software. If the Larger Work is a combination of Covered 192 | Software with a work governed by one or more Secondary Licenses, and the 193 | Covered Software is not Incompatible With Secondary Licenses, this 194 | License permits You to additionally distribute such Covered Software 195 | under the terms of such Secondary License(s), so that the recipient of 196 | the Larger Work may, at their option, further distribute the Covered 197 | Software under the terms of either this License or such Secondary 198 | License(s). 199 | 200 | 3.4. Notices 201 | 202 | You may not remove or alter the substance of any license notices 203 | (including copyright notices, patent notices, disclaimers of warranty, 204 | or limitations of liability) contained within the Source Code Form of 205 | the Covered Software, except that You may alter any license notices to 206 | the extent required to remedy known factual inaccuracies. 207 | 208 | 3.5. Application of Additional Terms 209 | 210 | You may choose to offer, and to charge a fee for, warranty, support, 211 | indemnity or liability obligations to one or more recipients of Covered 212 | Software. However, You may do so only on Your own behalf, and not on 213 | behalf of any Contributor. You must make it absolutely clear that any 214 | such warranty, support, indemnity, or liability obligation is offered by 215 | You alone, and You hereby agree to indemnify every Contributor for any 216 | liability incurred by such Contributor as a result of warranty, support, 217 | indemnity or liability terms You offer. You may include additional 218 | disclaimers of warranty and limitations of liability specific to any 219 | jurisdiction. 220 | 221 | 4. Inability to Comply Due to Statute or Regulation 222 | --------------------------------------------------- 223 | 224 | If it is impossible for You to comply with any of the terms of this 225 | License with respect to some or all of the Covered Software due to 226 | statute, judicial order, or regulation then You must: (a) comply with 227 | the terms of this License to the maximum extent possible; and (b) 228 | describe the limitations and the code they affect. Such description must 229 | be placed in a text file included with all distributions of the Covered 230 | Software under this License. Except to the extent prohibited by statute 231 | or regulation, such description must be sufficiently detailed for a 232 | recipient of ordinary skill to be able to understand it. 233 | 234 | 5. Termination 235 | -------------- 236 | 237 | 5.1. The rights granted under this License will terminate automatically 238 | if You fail to comply with any of its terms. However, if You become 239 | compliant, then the rights granted under this License from a particular 240 | Contributor are reinstated (a) provisionally, unless and until such 241 | Contributor explicitly and finally terminates Your grants, and (b) on an 242 | ongoing basis, if such Contributor fails to notify You of the 243 | non-compliance by some reasonable means prior to 60 days after You have 244 | come back into compliance. Moreover, Your grants from a particular 245 | Contributor are reinstated on an ongoing basis if such Contributor 246 | notifies You of the non-compliance by some reasonable means, this is the 247 | first time You have received notice of non-compliance with this License 248 | from such Contributor, and You become compliant prior to 30 days after 249 | Your receipt of the notice. 250 | 251 | 5.2. If You initiate litigation against any entity by asserting a patent 252 | infringement claim (excluding declaratory judgment actions, 253 | counter-claims, and cross-claims) alleging that a Contributor Version 254 | directly or indirectly infringes any patent, then the rights granted to 255 | You by any and all Contributors for the Covered Software under Section 256 | 2.1 of this License shall terminate. 257 | 258 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 259 | end user license agreements (excluding distributors and resellers) which 260 | have been validly granted by You or Your distributors under this License 261 | prior to termination shall survive termination. 262 | 263 | ************************************************************************ 264 | * * 265 | * 6. Disclaimer of Warranty * 266 | * ------------------------- * 267 | * * 268 | * Covered Software is provided under this License on an "as is" * 269 | * basis, without warranty of any kind, either expressed, implied, or * 270 | * statutory, including, without limitation, warranties that the * 271 | * Covered Software is free of defects, merchantable, fit for a * 272 | * particular purpose or non-infringing. The entire risk as to the * 273 | * quality and performance of the Covered Software is with You. * 274 | * Should any Covered Software prove defective in any respect, You * 275 | * (not any Contributor) assume the cost of any necessary servicing, * 276 | * repair, or correction. This disclaimer of warranty constitutes an * 277 | * essential part of this License. No use of any Covered Software is * 278 | * authorized under this License except under this disclaimer. * 279 | * * 280 | ************************************************************************ 281 | 282 | ************************************************************************ 283 | * * 284 | * 7. Limitation of Liability * 285 | * -------------------------- * 286 | * * 287 | * Under no circumstances and under no legal theory, whether tort * 288 | * (including negligence), contract, or otherwise, shall any * 289 | * Contributor, or anyone who distributes Covered Software as * 290 | * permitted above, be liable to You for any direct, indirect, * 291 | * special, incidental, or consequential damages of any character * 292 | * including, without limitation, damages for lost profits, loss of * 293 | * goodwill, work stoppage, computer failure or malfunction, or any * 294 | * and all other commercial damages or losses, even if such party * 295 | * shall have been informed of the possibility of such damages. This * 296 | * limitation of liability shall not apply to liability for death or * 297 | * personal injury resulting from such party's negligence to the * 298 | * extent applicable law prohibits such limitation. Some * 299 | * jurisdictions do not allow the exclusion or limitation of * 300 | * incidental or consequential damages, so this exclusion and * 301 | * limitation may not apply to You. * 302 | * * 303 | ************************************************************************ 304 | 305 | 8. Litigation 306 | ------------- 307 | 308 | Any litigation relating to this License may be brought only in the 309 | courts of a jurisdiction where the defendant maintains its principal 310 | place of business and such litigation shall be governed by laws of that 311 | jurisdiction, without reference to its conflict-of-law provisions. 312 | Nothing in this Section shall prevent a party's ability to bring 313 | cross-claims or counter-claims. 314 | 315 | 9. Miscellaneous 316 | ---------------- 317 | 318 | This License represents the complete agreement concerning the subject 319 | matter hereof. If any provision of this License is held to be 320 | unenforceable, such provision shall be reformed only to the extent 321 | necessary to make it enforceable. Any law or regulation which provides 322 | that the language of a contract shall be construed against the drafter 323 | shall not be used to construe this License against a Contributor. 324 | 325 | 10. Versions of the License 326 | --------------------------- 327 | 328 | 10.1. New Versions 329 | 330 | Mozilla Foundation is the license steward. Except as provided in Section 331 | 10.3, no one other than the license steward has the right to modify or 332 | publish new versions of this License. Each version will be given a 333 | distinguishing version number. 334 | 335 | 10.2. Effect of New Versions 336 | 337 | You may distribute the Covered Software under the terms of the version 338 | of the License under which You originally received the Covered Software, 339 | or under the terms of any subsequent version published by the license 340 | steward. 341 | 342 | 10.3. Modified Versions 343 | 344 | If you create software not governed by this License, and you want to 345 | create a new license for such software, you may create and use a 346 | modified version of this License if you rename the license and remove 347 | any references to the name of the license steward (except to note that 348 | such modified license differs from this License). 349 | 350 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 351 | Licenses 352 | 353 | If You choose to distribute Source Code Form that is Incompatible With 354 | Secondary Licenses under the terms of this version of the License, the 355 | notice described in Exhibit B of this License must be attached. 356 | 357 | Exhibit A - Source Code Form License Notice 358 | ------------------------------------------- 359 | 360 | This Source Code Form is subject to the terms of the Mozilla Public 361 | License, v. 2.0. If a copy of the MPL was not distributed with this 362 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 363 | 364 | If it is not possible or desirable to put the notice in a particular 365 | file, then You may include the notice in a location (such as a LICENSE 366 | file in a relevant directory) where a recipient would be likely to look 367 | for such a notice. 368 | 369 | You may add additional accurate notices of copyright ownership. 370 | 371 | Exhibit B - "Incompatible With Secondary Licenses" Notice 372 | --------------------------------------------------------- 373 | 374 | This Source Code Form is "Incompatible With Secondary Licenses", as 375 | defined by the Mozilla Public License, v. 2.0. 376 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [tests]: https://img.shields.io/circleci/project/github/shellscape/koa-webpack.svg 2 | [tests-url]: https://circleci.com/gh/shellscape/koa-webpack 3 | 4 | [cover]: https://codecov.io/gh/shellscape/koa-webpack/branch/master/graph/badge.svg 5 | [cover-url]: https://codecov.io/gh/shellscape/koa-webpack 6 | 7 | [size]: https://packagephobia.now.sh/badge?p=koa-webpack 8 | [size-url]: https://packagephobia.now.sh/result?p=koa-webpack 9 | 10 | # koa-webpack 11 | 12 | [![tests][tests]][tests-url] 13 | [![cover][cover]][cover-url] 14 | [![size][size]][size-url] 15 | 16 | Development and Hot Module Reload Middleware for **Koa2**, in a single 17 | middleware module. 18 | 19 | This module wraps and composes 20 | [`webpack-dev-middleware`](https://github.com/webpack/webpack-dev-middleware) and 21 | [`webpack-hot-client`](https://github.com/shellscape/webpack-hot-client) 22 | into a single middleware module, allowing for quick and concise implementation. 23 | 24 | As an added bonus, it'll also use the installed `webpack` module from your project, 25 | and the `webpack.config.js` file in the root of your project, automagically, should 26 | you choose to let it. This negates the need for all of the repetitive setup and 27 | config that you get with `koa-webpack-middleware`. 28 | 29 | ## Install 30 | 31 | Using npm: 32 | 33 | ```console 34 | npm install koa-webpack --save-dev 35 | ``` 36 | 37 | 38 | 39 | 40 | 41 | ## Requirements 42 | 43 | `koa-webpack` is an evergreen module. 🌲 This module requires an [Active LTS](https://github.com/nodejs/Release) Node version (v8.0.0+ or v10.0.0+), and Webpack v4.0.0+. 44 | 45 | ## Usage 46 | 47 | ```js 48 | const Koa = require('koa'); 49 | const koaWebpack = require('koa-webpack'); 50 | 51 | const app = new Koa(); 52 | const options = { .. }; 53 | const middleware = await koaWebpack(options); 54 | 55 | app.use(middleware); 56 | ``` 57 | 58 | ## API 59 | 60 | ### koaWebpack([options]) 61 | 62 | Returns a `Promise` which resolves the server `middleware` containing the 63 | following additional properties: 64 | 65 | - `close(callback)` *(Function)* - Closes both the instance of `webpack-dev-middleware` 66 | and `webpack-hot-client`. Accepts a single `Function` callback parameter that is 67 | executed when complete. 68 | - `hotClient` *(Object)* - An instance of `webpack-hot-client`. 69 | - `devMiddleware` *(Object)* - An instance of `webpack-dev-middleware` 70 | 71 | ## Options 72 | 73 | The middleware accepts an `options` Object, which can contain options for the 74 | `webpack-dev-middleware` and `webpack-hot-client` bundled with this module. 75 | The following is a property reference for the Object: 76 | 77 | ### compiler 78 | 79 | Type: `Object` 80 | `optional` 81 | 82 | Should you rather that the middleware use an instance of `webpack` that you've 83 | already init'd [with webpack config], you can pass it to the middleware using 84 | this option. 85 | 86 | Example: 87 | 88 | ```js 89 | const webpack = require('webpack'); 90 | const config = require('./webpack.config.js'); 91 | const koaWebpack = require('koa-webpack'); 92 | 93 | const compiler = webpack(config); 94 | const middleware = await koaWebpack({ compiler }); 95 | 96 | app.use(middleware); 97 | ``` 98 | 99 | ### config 100 | 101 | Type: `Object` 102 | 103 | Should you rather that the middleware use an instance of webpack configuration 104 | that you've already required/imported, you can pass it to the middleware using 105 | this option. 106 | 107 | Example: 108 | 109 | ```js 110 | const koaWebpack = require('koa-webpack'); 111 | const config = require('./webpack.config.js'); 112 | 113 | const middleware = await koaWebpack({ config }); 114 | 115 | app.use(middleware); 116 | ``` 117 | 118 | ### configPath 119 | 120 | Type: `String` 121 | 122 | Allows you to specify the absolute path to the Webpack config file to be used. 123 | 124 | Example: 125 | 126 | ```js 127 | const path = require('path'); 128 | const koaWebpack = require('koa-webpack'); 129 | 130 | // The Webpack config file would be at "./client/webpack.config.js". 131 | const middleware = await koaWebpack({ 132 | configPath: path.join(__dirname, 'client', 'webpack.config.js') 133 | }); 134 | 135 | app.use(middleware); 136 | ``` 137 | 138 | ### devMiddleware 139 | 140 | Type: `Object` 141 | 142 | The `devMiddleware` property should contain options for `webpack-dev-middleware`, a list of 143 | which is available at [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware). 144 | Omitting this property will result in `webpack-dev-middleware` using its default 145 | options. 146 | 147 | ### hotClient 148 | 149 | Type: `Object|Boolean` 150 | 151 | The `hotClient` property should contain options for `webpack-hot-client`, a list of 152 | which is available at [webpack-hot-client](https://github.com/webpack-contrib/webpack-hot-client). 153 | Omitting this property will result in `webpack-hot-client` using its default 154 | options. 155 | 156 | As of `v3.0.1` setting this to `false` will completely disable `webpack-hot-client` 157 | and all automatic Hot Module Replacement functionality. 158 | 159 | ## Using with koa-compress 160 | 161 | When using `koa-webpack` with [koa-compress](https://github.com/koajs/compress), 162 | you may experience issues with saving files and hot module reload. Please review 163 | [this issue](https://github.com/shellscape/koa-webpack/issues/36#issuecomment-289565573) 164 | for more information and a workaround. 165 | 166 | ## Server-Side-Rendering 167 | 168 | When `serverSideRender` is set to true in `config.devMiddleware`, `webpackStats` is 169 | accessible from `ctx.state.webpackStats`. 170 | 171 | ```js 172 | app.use(async (ctx, next) => { 173 | const assetsByChunkName = ctx.state.webpackStats.toJson().assetsByChunkName; 174 | // do something with assetsByChunkName 175 | }) 176 | ``` 177 | 178 | For more details please refer to: 179 | [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware#server-side-rendering) 180 | 181 | 182 | ## Using with html-webpack-plugin 183 | 184 | When using with html-webpack-plugin, you can access dev-middleware in-memory filesystem to serve index.html file: 185 | 186 | ```js 187 | const middleware = await koaWebpack({ config }); 188 | 189 | app.use(middleware); 190 | 191 | app.use(async (ctx) => { 192 | const filename = path.resolve(webpackConfig.output.path, 'index.html') 193 | ctx.response.type = 'html' 194 | ctx.response.body = middleware.devMiddleware.fileSystem.createReadStream(filename) 195 | }); 196 | ``` 197 | 198 | ## Contributing 199 | 200 | Please take a moment to read our contributing guidelines if you haven't yet done so. 201 | 202 | #### [CONTRIBUTING](./.github/CONTRIBUTING.md) 203 | 204 | ## Attribution 205 | 206 | This module started as a fork of 207 | [`koa-webpack-middleware`](https://github.com/leecade/koa-webpack-middleware) 208 | 209 | ## License 210 | 211 | #### [MPL](./LICENSE) 212 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: master 3 | coverage: 4 | precision: 2 5 | round: down 6 | range: 70...100 7 | status: 8 | project: 'no' 9 | patch: 'yes' 10 | comment: 'off' 11 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const Configuration = { 3 | rules: { 4 | 'body-leading-blank': [1, 'always'], 5 | 'footer-leading-blank': [1, 'always'], 6 | 'header-max-length': [2, 'always', 72], 7 | 'scope-case': [2, 'always', 'lower-case'], 8 | 'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']], 9 | 'subject-empty': [2, 'never'], 10 | 'subject-full-stop': [2, 'never', '.'], 11 | 'type-case': [2, 'always', 'lower-case'], 12 | 'type-empty': [2, 'never'], 13 | 'type-enum': [2, 'always', [ 14 | 'build', 15 | 'chore', 16 | 'ci', 17 | 'docs', 18 | 'feat', 19 | 'fix', 20 | 'perf', 21 | 'refactor', 22 | 'revert', 23 | 'style', 24 | 'test', 25 | ], 26 | ], 27 | }, 28 | }; 29 | 30 | module.exports = Configuration; 31 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2016 Andrew Powell 3 | 4 | This Source Code Form is subject to the terms of the Mozilla Public 5 | License, v. 2.0. If a copy of the MPL was not distributed with this 6 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | 8 | The above copyright notice and this permission notice shall be 9 | included in all copies or substantial portions of this Source Code Form. 10 | */ 11 | const webpackHotClient = require('webpack-hot-client'); 12 | 13 | module.exports = { 14 | getClient(compiler, options) { 15 | if (!options.hotClient) { 16 | return Promise.resolve(null); 17 | } 18 | 19 | return new Promise((resolve) => { 20 | const client = webpackHotClient(compiler, options.hotClient); 21 | const { server } = client; 22 | 23 | server.on('listening', () => resolve(client)); 24 | }); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2016 Andrew Powell 3 | 4 | This Source Code Form is subject to the terms of the Mozilla Public 5 | License, v. 2.0. If a copy of the MPL was not distributed with this 6 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | 8 | The above copyright notice and this permission notice shall be 9 | included in all copies or substantial portions of this Source Code Form. 10 | */ 11 | const { join } = require('path'); 12 | 13 | const root = require('app-root-path'); 14 | const chalk = require('chalk'); 15 | const webpack = require('webpack'); 16 | const webpackDevMiddleware = require('webpack-dev-middleware'); 17 | 18 | const { getClient } = require('./client'); 19 | const { getMiddleware } = require('./middleware'); 20 | const { validate } = require('./validate'); 21 | 22 | const defaults = { devMiddleware: {}, hotClient: {} }; 23 | 24 | module.exports = async (opts) => { 25 | const valid = validate(opts); 26 | 27 | if (valid.error) { 28 | const { error } = console; 29 | error(chalk.red('⬢ koa-webpack:'), 'An option was passed to koa-webpack that is not valid'); 30 | throw valid.error; 31 | } 32 | 33 | const options = Object.assign({}, defaults, opts); 34 | 35 | let { compiler, config } = options; 36 | 37 | if (!compiler) { 38 | if (!config) { 39 | // eslint-disable-next-line import/no-dynamic-require, global-require 40 | config = require(options.configPath || join(root.path, 'webpack.config.js')); 41 | } 42 | 43 | compiler = webpack(config); 44 | } 45 | 46 | if (!options.devMiddleware.publicPath) { 47 | const { publicPath } = compiler.options.output; 48 | 49 | if (!publicPath) { 50 | throw new Error( 51 | "koa-webpack: publicPath must be set on `dev` options, or in a compiler's `output` configuration." 52 | ); 53 | } 54 | 55 | options.devMiddleware.publicPath = publicPath; 56 | } 57 | 58 | const hotClient = await getClient(compiler, options); 59 | const devMiddleware = webpackDevMiddleware(compiler, options.devMiddleware); 60 | const middleware = getMiddleware(compiler, devMiddleware); 61 | const close = (callback) => { 62 | const next = hotClient ? () => hotClient.close(callback) : callback; 63 | devMiddleware.close(next); 64 | }; 65 | 66 | return Object.assign(middleware, { 67 | hotClient, 68 | devMiddleware, 69 | close 70 | }); 71 | }; 72 | -------------------------------------------------------------------------------- /lib/middleware.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2016 Andrew Powell 3 | 4 | This Source Code Form is subject to the terms of the Mozilla Public 5 | License, v. 2.0. If a copy of the MPL was not distributed with this 6 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | 8 | The above copyright notice and this permission notice shall be 9 | included in all copies or substantial portions of this Source Code Form. 10 | */ 11 | module.exports = { 12 | getMiddleware(compiler, devMiddleware) { 13 | return (context, next) => { 14 | // wait for webpack-dev-middleware to signal that the build is ready 15 | const ready = new Promise((resolve, reject) => { 16 | for (const comp of [].concat(compiler.compilers || compiler)) { 17 | comp.hooks.failed.tap('KoaWebpack', (error) => { 18 | reject(error); 19 | }); 20 | } 21 | 22 | devMiddleware.waitUntilValid(() => { 23 | resolve(true); 24 | }); 25 | }); 26 | // tell webpack-dev-middleware to handle the request 27 | const init = new Promise((resolve) => { 28 | devMiddleware( 29 | context.req, 30 | { 31 | end: (content) => { 32 | // eslint-disable-next-line no-param-reassign 33 | context.body = content; 34 | resolve(); 35 | }, 36 | getHeader: context.get.bind(context), 37 | setHeader: context.set.bind(context), 38 | locals: context.state 39 | }, 40 | () => resolve(next()) 41 | ); 42 | }); 43 | 44 | return Promise.all([ready, init]); 45 | }; 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /lib/validate.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2016 Andrew Powell 3 | 4 | This Source Code Form is subject to the terms of the Mozilla Public 5 | License, v. 2.0. If a copy of the MPL was not distributed with this 6 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | 8 | The above copyright notice and this permission notice shall be 9 | included in all copies or substantial portions of this Source Code Form. 10 | */ 11 | const Joi = require('joi'); 12 | 13 | module.exports = { 14 | validate(options) { 15 | const keys = { 16 | compiler: [Joi.object().allow(null)], 17 | config: [Joi.object().allow(null)], 18 | configPath: [Joi.string().allow(null)], 19 | devMiddleware: [Joi.object()], 20 | hotClient: [Joi.boolean(), Joi.object().allow(null)] 21 | }; 22 | const schema = Joi.object().keys(keys); 23 | const results = schema.validate(options); 24 | 25 | return results; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-webpack", 3 | "version": "6.0.0", 4 | "description": "Development and Hot Reload Middleware for Koa2", 5 | "license": "MPL-2.0", 6 | "repository": "shellscape/koa-webpack", 7 | "author": "Andrew Powell ", 8 | "homepage": "https://github.com/shellscape/koa-webpack", 9 | "bugs": "https://github.com/shellscape/koa-webpack/issues", 10 | "main": "lib/index.js", 11 | "engines": { 12 | "node": ">= 10.0.0" 13 | }, 14 | "scripts": { 15 | "ci:coverage": "nyc npm run ci:test && nyc report --reporter=text-lcov > coverage.lcov", 16 | "ci:lint": "npm run lint && npm run security", 17 | "ci:lint:commits": "commitlint --from=${CIRCLE_BRANCH} --to=${CIRCLE_SHA1}", 18 | "ci:test": "npm run test -- --verbose", 19 | "lint": "eslint --fix --cache lib test", 20 | "lint-staged": "lint-staged", 21 | "security": "npm audit --audit-level=high", 22 | "test": "ava --timeout=2500" 23 | }, 24 | "files": [ 25 | "lib/", 26 | "LICENSE", 27 | "README.md" 28 | ], 29 | "peerDependencies": { 30 | "webpack": "^4.28.0" 31 | }, 32 | "dependencies": { 33 | "app-root-path": "^3.0.0", 34 | "chalk": "^4.1.0", 35 | "joi": "^17.2.0", 36 | "merge-options": "^2.0.0", 37 | "webpack-dev-middleware": "^3.7.2", 38 | "webpack-hot-client": "^4.1.2" 39 | }, 40 | "devDependencies": { 41 | "@commitlint/cli": "^9.1.2", 42 | "ava": "^3.11.1", 43 | "eslint": "^6.0.1", 44 | "eslint-config-shellscape": "^2.1.0", 45 | "husky": "^4.2.5", 46 | "killable": "^1.0.1", 47 | "koa": "^2.13.0", 48 | "koa-compose": "^4.1.0", 49 | "lint-staged": "^10.2.11", 50 | "nyc": "^15.1.0", 51 | "pre-commit": "^1.2.2", 52 | "prettier": "^1.13.4", 53 | "supertest": "^4.0.2", 54 | "webpack": "^4.44.1" 55 | }, 56 | "keywords": [ 57 | "koa", 58 | "middleware", 59 | "webpack" 60 | ], 61 | "ava": { 62 | "files": [ 63 | "!**/fixtures/**", 64 | "!**/helpers/**" 65 | ] 66 | }, 67 | "lint-staged": { 68 | "*.js": [ 69 | "eslint --fix" 70 | ] 71 | }, 72 | "husky": { 73 | "hooks": { 74 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 75 | } 76 | }, 77 | "pre-commit": "lint-staged" 78 | } 79 | -------------------------------------------------------------------------------- /test/fixtures/input.js: -------------------------------------------------------------------------------- 1 | console.log('Hello World'); // eslint-disable-line 2 | -------------------------------------------------------------------------------- /test/fixtures/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | entry: [resolve(__dirname, 'input.js')], 6 | output: { 7 | path: __dirname, 8 | filename: 'output.js' 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | 3 | const test = require('ava'); 4 | const killable = require('killable'); 5 | const merge = require('merge-options'); 6 | const Koa = require('koa'); 7 | const compose = require('koa-compose'); 8 | const request = require('supertest'); 9 | const webpack = require('webpack'); 10 | 11 | const koaWebpack = require('../lib'); 12 | 13 | const config = require('./fixtures/webpack.config'); 14 | 15 | const defaults = { 16 | config, 17 | devMiddleware: { 18 | publicPath: '/', 19 | logLevel: 'silent' 20 | }, 21 | hotClient: { 22 | logLevel: 'silent' 23 | } 24 | }; 25 | 26 | function buildOptions(opts) { 27 | const options = merge({}, defaults, opts); 28 | return merge(options, { 29 | config: null, 30 | ...(opts.configPath ? {} : { compiler: webpack(options.config) }) 31 | }); 32 | } 33 | 34 | function defaultApp(middleware) { 35 | return middleware; 36 | } 37 | 38 | async function setup(opts, setupMiddleware = defaultApp) { 39 | const app = new Koa(); 40 | const options = buildOptions(opts); 41 | const middleware = await koaWebpack(options); 42 | 43 | app.use(setupMiddleware(middleware)); 44 | 45 | const server = app.listen(); 46 | const req = request(server); 47 | 48 | killable(server); 49 | 50 | return { middleware, req, server }; 51 | } 52 | 53 | function close(server, middleware) { 54 | return new Promise((r) => server.kill(middleware.close(r))); 55 | } 56 | 57 | test('should provide access to middleware and client', async (t) => { 58 | const { middleware, req, server } = await setup({ 59 | devMiddleware: { lazy: false } 60 | }); 61 | 62 | await req.get('/output.js'); 63 | 64 | const { devMiddleware, hotClient } = middleware; 65 | 66 | t.truthy(devMiddleware); 67 | t.truthy(devMiddleware.close); 68 | t.truthy(hotClient); 69 | t.truthy(hotClient.close); 70 | 71 | return close(server, middleware); 72 | }); 73 | 74 | test('should disable hot-client', async (t) => { 75 | const { middleware, req, server } = await setup({ 76 | devMiddleware: { lazy: false }, 77 | hotClient: false 78 | }); 79 | 80 | await req.get('/output.js'); 81 | 82 | t.truthy(middleware.devMiddleware); 83 | t.is(middleware.hotClient, null); 84 | 85 | return close(server, middleware); 86 | }); 87 | 88 | test('sends the result in watch mode', async (t) => { 89 | const { middleware, req, server } = await setup({ 90 | devMiddleware: { lazy: false } 91 | }); 92 | 93 | const response = await req.get('/output.js').expect(200); 94 | 95 | t.regex(response.text, /Hello World/); 96 | 97 | return close(server, middleware); 98 | }); 99 | 100 | test('sends the result to a MultiCompiler in watch mode', async (t) => { 101 | const { middleware, req, server } = await setup({ 102 | devMiddleware: { lazy: false }, 103 | config: [ 104 | { 105 | entry: [resolve(__dirname, 'fixtures', 'input.js')], 106 | output: { 107 | path: resolve(__dirname, 'fixtures'), 108 | filename: 'output.js' 109 | } 110 | }, 111 | { 112 | entry: [resolve(__dirname, 'fixtures', 'input.js')], 113 | output: { 114 | path: resolve(__dirname, 'fixtures'), 115 | filename: 'output2.js' 116 | } 117 | } 118 | ] 119 | }); 120 | 121 | let response = await req.get('/output.js').expect(200); 122 | 123 | t.regex(response.text, /Hello World/); 124 | 125 | response = await req.get('/output2.js').expect(200); 126 | 127 | t.regex(response.text, /Hello World/); 128 | 129 | return close(server, middleware); 130 | }); 131 | 132 | test('builds and sends the result in lazy mode', async (t) => { 133 | const { middleware, req, server } = await setup({ 134 | devMiddleware: { lazy: true } 135 | }); 136 | 137 | const response = await req.get('/output.js').expect(200); 138 | 139 | t.regex(response.text, /Hello World/); 140 | 141 | return close(server, middleware); 142 | }); 143 | 144 | test('continues on if the file is not part of webpack', async (t) => { 145 | const mware = (webpackMiddleware) => 146 | compose([ 147 | webpackMiddleware, 148 | async (ctx) => { 149 | ctx.body = 'foo'; // eslint-disable-line no-param-reassign 150 | } 151 | ]); 152 | 153 | const { middleware, req, server } = await setup({}, mware); 154 | 155 | const response = await req.get('/some-other-file.js').expect(200); 156 | 157 | t.is(response.text, 'foo'); 158 | 159 | return close(server, middleware); 160 | }); 161 | 162 | test('uses supplied Webpack configuration file', async (t) => { 163 | const { middleware, req, server } = await setup({ 164 | configPath: resolve(__dirname, 'fixtures', 'webpack.config.js') 165 | }); 166 | 167 | const response = await req.get('/output.js').expect(200); 168 | 169 | t.regex(response.text, /Hello World/); 170 | 171 | return close(server, middleware); 172 | }); 173 | --------------------------------------------------------------------------------