├── .editorconfig ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── lib └── index.js ├── package.json └── test ├── .gitignore ├── index.js ├── package.json ├── some_directory ├── another_module.js └── index.js ├── some_module.js └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | insert_final_newline = false 7 | end_of_line = lf 8 | 9 | indent_size = 2 10 | indent_style = space 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /index.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Renke Grunwald 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | __ATTENTION: This repository is deprecated. See [npm-install-webpack-plugin](https://github.com/ericclemmons/npm-install-webpack-plugin) for way better alternative.__ 2 | 3 | auto-install-webpack-plugin 4 | =========================== 5 | 6 | A [Webpack](http://webpack.github.io/) plugin that automatically installs packages that contain your require'd modules. 7 | 8 | Whenever Webpack resolves a requested module, the plugin first checks if the module is installed and then tries to install the corresponding package using npm. 9 | 10 | For example, assuming the packages [lodash.times](https://www.npmjs.com/package/lodash.times) is not currently installed, but you require it in your code like this: 11 | 12 | ```JavaScript 13 | var times = require("lodash.times"); 14 | 15 | times(5, function(i) { 16 | console.log(i); 17 | }); 18 | ``` 19 | 20 | Running `webpack` without using the plugin will result in an error because the module cannot be resolved. However, with the plugin enabled `lodash.times` will be automatically installed without forcing you to go to your terminal and execute `npm install lodash.times`. 21 | 22 | Automatic installation of packages using the plugin should work in all language that are supported by the various Webpack loaders (such as loaders for Babel or Less). 23 | 24 | ## Installation ## 25 | 26 | `npm install --save-dev auto-install-webpack-plugin` 27 | 28 | ## Usage ## 29 | 30 | Just add an instance of AutoInstallPlugin to your plugins array. 31 | 32 | ```JavaScript 33 | var AutoInstallPlugin = require("auto-install-webpack-plugin"); 34 | 35 | module.exports = { 36 | context: __dirname, 37 | entry: "./index", 38 | 39 | output: { 40 | path: __dirname + "/build", 41 | filename: "index.js" 42 | }, 43 | 44 | plugins: [ 45 | new AutoInstallPlugin({save: true}), 46 | ], 47 | }; 48 | ``` 49 | 50 | ## Configuration ## 51 | 52 | You can pass a configuration object to the `AutoInstallPlugin` constructor. The following keys and values are recognized: 53 | 54 | * `save`: `true` (default) if the plugin should `--save` the installed package, `false` otherwise. 55 | 56 | ## Feedback ## 57 | 58 | I appreciate any kind of feedback. Just create an issue or drop me a mail. Thanks! 59 | 60 | ## License ## 61 | 62 | See [LICENSE](LICENSE). 63 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | require("babel/polyfill"); 2 | 3 | import validatePackageName from "validate-npm-package-name"; 4 | import bluebird from "bluebird"; 5 | 6 | import {exec} from "child_process"; 7 | 8 | const resolve = bluebird.Promise.promisify(require("resolve")); 9 | 10 | class AutoInstallPlugin { 11 | constructor(config={save: true}) { 12 | this.config = config; 13 | this.resetMemory(); 14 | } 15 | 16 | resetMemory() { 17 | this.memory = new Set(); 18 | } 19 | 20 | apply(compiler) { 21 | compiler.plugin("done", this.resetMemory); 22 | 23 | compiler.resolvers.normal.plugin("module", (req, next) => { 24 | var packageName = req.request.split("/")[0]; 25 | 26 | // Make sure we only install a package once 27 | if (this.memory.has(packageName)) { 28 | return next(); 29 | } else { 30 | this.memory.add(packageName); 31 | } 32 | 33 | // Avoid trying to install packages with invalid name 34 | if (!validatePackageName(packageName).validForNewPackages) { 35 | return next(); 36 | } 37 | 38 | resolve(packageName, {basedir: req.path}).then(res => { 39 | next(); // Already installed 40 | }).catch(() => { 41 | let command = this.buildCommand(packageName); 42 | 43 | exec(command, (err, stdout, stderr) => { 44 | if (err) return next(); 45 | 46 | console.log(command); 47 | print(stdout.toString()); 48 | 49 | resolve(packageName, {basedir: req.path}).then(([path]) => { 50 | next(null, resolveRequest(req, path)); 51 | }).catch(err => { 52 | next(); 53 | }); 54 | }); 55 | }).catch(err => { 56 | next(); 57 | }); 58 | }) 59 | } 60 | 61 | buildCommand(packageName) { 62 | if (this.config.save) { 63 | return `npm install --save ${packageName}`; 64 | } 65 | 66 | return `npm install ${packageName}`; 67 | } 68 | } 69 | 70 | function resolveRequest(oldRequest, resolvedPath) { 71 | return Object.assign({}, oldRequest, { 72 | path: resolvedPath, 73 | resolved: true, 74 | }) 75 | } 76 | 77 | function print(output) { 78 | console.log(output.trim()); 79 | } 80 | 81 | export default AutoInstallPlugin; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto-install-webpack-plugin", 3 | "version": "1.2.3", 4 | "description": "Automatically install required modules.", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepublish": "babel lib --out-file index.js" 8 | }, 9 | "author": "Renke Grunwald (http://renke.org)", 10 | "license": "ISC", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/renke/auto-install-webpack-plugin.git" 14 | }, 15 | "keywords": [ 16 | "webpack", 17 | "plugin", 18 | "npm", 19 | "auto-install" 20 | ], 21 | "dependencies": { 22 | "babel": "^5.6.14", 23 | "bluebird": "^2.9.32", 24 | "resolve": "^1.1.6", 25 | "validate-npm-package-name": "^2.2.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require("lodash.throttle"); 2 | 3 | require("./some_directory"); 4 | require("./some_directory/another_module") 5 | 6 | require("./some_module"); -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto-install-webpack-plugin-test", 3 | "version": "1.0.0", 4 | "description": "A simple package to test auto-install-webpack-plugin", 5 | "main": "./index", 6 | "private": true, 7 | "author": "Renke Grunwald" 8 | } 9 | -------------------------------------------------------------------------------- /test/some_directory/another_module.js: -------------------------------------------------------------------------------- 1 | require("lodash.times"); -------------------------------------------------------------------------------- /test/some_directory/index.js: -------------------------------------------------------------------------------- 1 | require("lodash.pluck"); -------------------------------------------------------------------------------- /test/some_module.js: -------------------------------------------------------------------------------- 1 | require("lodash.assign"); -------------------------------------------------------------------------------- /test/webpack.config.js: -------------------------------------------------------------------------------- 1 | var AutoInstallPlugin = require("../index.js"); 2 | 3 | module.exports = { 4 | context: __dirname, 5 | entry: "./index", 6 | 7 | output: { 8 | path: __dirname + "/build", 9 | filename: "index.js" 10 | }, 11 | 12 | plugins: [ 13 | new AutoInstallPlugin({save: true}), 14 | ], 15 | }; --------------------------------------------------------------------------------