├── lib ├── index.js ├── LineWriter.js ├── PackageManager.js ├── ModuleAnalyer.js └── VersionComparer.js ├── screen_shot.png ├── package.json ├── LICENSE └── README.md /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports =require('./PackageManager'); -------------------------------------------------------------------------------- /screen_shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bert0324/webpack-package-manager/HEAD/screen_shot.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-package-manager", 3 | "version": "1.0.7", 4 | "description": "a webpack plugin for checking packages' version, concatenating package.json files and auto updating", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "webpack", 11 | "package", 12 | "manager", 13 | "npm", 14 | "modules" 15 | ], 16 | "homepage": "https://github.com/Bert0324/webpack-package-manager#readme", 17 | "author": "Bert", 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/Bert0324/webpack-package-manager" 22 | }, 23 | "dependencies": { 24 | "package-json": "^6.4.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bert Huang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A webpack plugin can check package versions, combine package.json, auto update. 2 | 3 | It is an interesting tiny tool to see whether your packages is outdated, or combine several package.json together. Let's start! 4 | 5 | ## Install 6 | 7 | ``` 8 | npm i --save-dev webpack-package-manager 9 | ``` 10 | 11 | ## Start 12 | 13 | ```JavaScript 14 | const PackageUpdatePlugin = require('webpack-package-manager'); 15 | module.exports = { 16 | plugins:[new PackageUpdatePlugin()] 17 | } 18 | ``` 19 | In command line when compiling, it wll show like: 20 | 21 | 22 | 23 | 24 | ## Options 25 | 26 | ```JavaScript 27 | const PackageUpdatePlugin = require('webpack-package-manager'); 28 | new PackageUpdatePlugin({ 29 | onlyShowAvailable: false, 30 | //whether show the packages that don't need to update, default is false 31 | autoUpdate:false, 32 | //whether automatically update the packages detected to update, default is false 33 | showReleaseTime:true, 34 | //whether show latest release time, default is true 35 | updateFrom:['/package.json'] 36 | //an array, the paths that other package.json files you want to combine, if set this options, 37 | //the latest version showing will be the highest version among all files instead of npm latest 38 | //default is undefined 39 | }) 40 | ``` 41 | 42 | If you have issues or want to make some suggestions, welcome to contact me. It is so kind if you can give a star :star:! 43 | -------------------------------------------------------------------------------- /lib/LineWriter.js: -------------------------------------------------------------------------------- 1 | module.exports = class LineWriter{ 2 | 3 | constructor(options) { 4 | this.options = options; 5 | this.packegeNameMax = 60; 6 | this.info = `${ 7 | LineWriter.colors().reset 8 | }Package Update Remind: ${ 9 | LineWriter.colors().green 10 | }green${ 11 | LineWriter.colors().reset 12 | } means it can be updated safely, ${ 13 | LineWriter.colors().red 14 | }red${ 15 | LineWriter.colors().reset 16 | } means may have problems with compatibility, white means no new version detect\n`; 17 | } 18 | 19 | static colors() { 20 | return { 21 | reset:"\x1b[0m", //no version update detect 22 | red:"\x1b[31m", //update may have problems with compatibility 23 | green:"\x1b[32m" //can update safely 24 | }; 25 | } 26 | 27 | 28 | regulateMessages(name, message, color){ 29 | let version = message.version ? message.version : 'not installed'; 30 | let lastest = message.latest ? message.latest : 'no higher version'; 31 | let release = message.release ? message.release : ''; 32 | color = color ? color : ''; 33 | let start = ` ${name}: ${version}`; 34 | 35 | return `${ 36 | LineWriter.colors().reset 37 | } ${ 38 | name 39 | }: ${ 40 | version 41 | }${ 42 | new Array(this.packegeNameMax - start.length > 0 ? this.packegeNameMax - start.length : 0) 43 | .fill(' ').join('') 44 | }---> ${ 45 | color 46 | } ${ 47 | lastest 48 | }${ 49 | this.options.showReleaseTime && !this.options.isUpdateFrom ? 50 | ' released on ' + release : '' 51 | }\n`; 52 | } 53 | 54 | 55 | draw(name, message, color){ 56 | if (this.options.onlyShowAvailable){ 57 | if (color === LineWriter.colors().green){ 58 | this.info += this.regulateMessages(name, message, color); 59 | } 60 | } else { 61 | this.info += this.regulateMessages(name, message, color); 62 | } 63 | return this; 64 | } 65 | 66 | end(){ 67 | this.info += `${LineWriter.colors().reset}Package Update Remind End`; 68 | return this.info; 69 | } 70 | 71 | 72 | }; -------------------------------------------------------------------------------- /lib/PackageManager.js: -------------------------------------------------------------------------------- 1 | module.exports = class PackageManager{ 2 | 3 | constructor(options){ 4 | this.options = { 5 | onlyShowAvailable: false, //only show packages that can be updated 6 | autoUpdate:false, //automatically update available packages 7 | showReleaseTime:true, //show release time of latest version 8 | updateFrom: void 0, //Array: another package.json files, will choose newest version from them 9 | ...options 10 | }; 11 | 12 | this.options.isUpdateFrom = Object.prototype.toString.call(this.options.updateFrom) === "[object Array]"; 13 | this.plugin = {name:this.constructor.name}; 14 | 15 | this.usedModules = void 0; //ModuleAnalyser instance 16 | this.Writer = require('./LineWriter'); 17 | this.VersionComparer = require('./VersionComparer'); 18 | } 19 | 20 | 21 | createMessage(){ 22 | let write = new this.Writer(this.options); 23 | this.usedModules.modules.forEach((item, key)=>{ 24 | let color = this.VersionComparer.compare(item.version, item.latest); 25 | write = write.draw(key, item, color); 26 | this.usedModules.modules.set(key, { 27 | ...item, 28 | color:color 29 | }) 30 | }); 31 | console.log(write.end()); 32 | } 33 | 34 | 35 | autoUpdate(){ 36 | if (this.options.autoUpdate){ 37 | this.usedModules.modules.forEach((item, key)=>{ 38 | if (item.color){ 39 | if (item.color === this.Writer.colors().green) { 40 | require('child_process').exec(`npm install ${key}@${item.latest}`, ()=>{ 41 | console.log(`${key} has ${item.version ? 'from' + item.version : ''} updated to ${item.latest}`); 42 | }); 43 | } 44 | } 45 | }); 46 | } 47 | } 48 | 49 | 50 | apply(compiler){ 51 | const doneCompileAsync = stats => { 52 | this.usedModules = new (require('./ModuleAnalyer'))(`${compiler.options.context}/package.json`, stats); 53 | let versionComparer = new this.VersionComparer(this.options); 54 | this.usedModules = versionComparer.latestUpdate(this.usedModules, ()=>{ 55 | this.createMessage(); 56 | this.autoUpdate(); 57 | }); 58 | }; 59 | if (compiler.hooks){ 60 | compiler.hooks.done.tapAsync(this.plugin, doneCompileAsync); 61 | } else { 62 | compiler.plugin('done', doneCompileAsync); 63 | } 64 | } 65 | }; -------------------------------------------------------------------------------- /lib/ModuleAnalyer.js: -------------------------------------------------------------------------------- 1 | module.exports = class ModuleAnalyer{ 2 | 3 | constructor(path, stats){ //names is an array of string that limits dependencies range 4 | this.moduleReg = /\.\/node_modules\/([^\/]*)\/([^\.\/]*)(.*)$/g; 5 | this.names = void 0; 6 | this.dependencies = require(path).dependencies; 7 | if (stats){ 8 | let modules = stats.toJson(null).modules; 9 | Object.keys(modules).forEach(key=>{ 10 | if (modules[key].name && modules[key].issuerName){ 11 | if (!modules[key].issuerName.startsWith('./node_modules')) { 12 | this.extractModuleName(modules[key].name); 13 | } 14 | } 15 | }); 16 | if (this.names){ 17 | this.names = Array.from(new Set(this.names.filter(item=>{ 18 | if (item !== undefined){ 19 | return item; 20 | } 21 | }))) 22 | } 23 | } 24 | this.modules = this.getModulesArray(); //Map 25 | } 26 | 27 | 28 | 29 | extractModuleName(path){ 30 | let match; 31 | do { 32 | match = this.moduleReg.exec(path); 33 | if (match){ 34 | if (this.names === undefined){ 35 | this.names = []; 36 | } 37 | this.names.push(match[1].startsWith('@') ? `${match[1]}/${match[2].includes('.') ? '' : match[2]}` : match[1]); 38 | } 39 | } while (match); 40 | } 41 | 42 | 43 | getModulesArray(){ 44 | let modules = new Map(); 45 | Object.keys(this.dependencies).forEach(key=>{ 46 | if (this.names === undefined){ 47 | modules.set(key, { 48 | version: this.dependencies[key] 49 | }) 50 | } else if (this.names.includes(key)){ 51 | modules.set(key, { 52 | version: this.dependencies[key] 53 | }) 54 | } 55 | }); 56 | return modules; 57 | } 58 | 59 | concatModuleAnalyer(path){ 60 | 61 | let dependencies = require(path).dependencies; 62 | if (Object.prototype.toString.call(dependencies) !== '[object Object]'){ 63 | throw new Error('invalid package.json path'); 64 | } 65 | Object.keys(dependencies).forEach(key=>{ 66 | if (this.modules.has(key)){ 67 | let item = this.modules.get(key); 68 | if (item.version < dependencies[key]){ 69 | this.modules.set(key, { 70 | ...item, 71 | latest:dependencies[key] 72 | }) 73 | } 74 | } else { 75 | this.modules.set(key, { 76 | latest:dependencies[key] 77 | }) 78 | } 79 | }); 80 | return this; 81 | } 82 | }; -------------------------------------------------------------------------------- /lib/VersionComparer.js: -------------------------------------------------------------------------------- 1 | module.exports = class VersionComparer{ 2 | 3 | constructor(options) { 4 | this.options = options; 5 | } 6 | 7 | 8 | latestUpdate(usedModules, callback){ 9 | if (this.options.isUpdateFrom){ 10 | this.options.updateFrom.forEach(item=>{ 11 | usedModules.concatModuleAnalyer(item); 12 | }); 13 | callback(); 14 | } else { 15 | const semver = require('semver'); 16 | const packageJson = require('package-json'); 17 | const lodash = require('lodash'); 18 | let iterNum = 0; 19 | usedModules.modules.forEach((item, key)=>{ 20 | packageJson(key, { fullMetadata: true, allVersions: true }) 21 | .then(data=>{ 22 | const sortedVersions = lodash(data.versions) 23 | .keys() 24 | .remove(lodash.partial(semver.gt, '8000.0.0')) 25 | .sort(semver.compare) 26 | .valueOf(); 27 | const latest = data['dist-tags'].latest; 28 | const latestStableRelease = semver.satisfies(latest, '*') ? 29 | latest : 30 | semver.maxSatisfying(sortedVersions, '*'); 31 | usedModules.modules.set(key, { 32 | ...item, 33 | latest:latestStableRelease, 34 | release:data['time'][latestStableRelease] 35 | }); 36 | iterNum += 1; 37 | if (iterNum === usedModules.modules.size){ 38 | callback(); 39 | } 40 | }) 41 | }); 42 | } 43 | return usedModules; 44 | } 45 | 46 | 47 | 48 | 49 | static compare(version, latest){ 50 | let choices = require('./LineWriter').colors(); 51 | let result =void 0; 52 | if (version && latest){ 53 | const filterMarker = (item)=>{ 54 | if (isNaN(parseInt(item[0], 10))){ 55 | return item.substr(1); 56 | } 57 | return item; 58 | }; 59 | let availableNumberArr = latest.split('.').map(filterMarker); 60 | let usedNumberArr = version.split('.').map(filterMarker); 61 | 62 | const placeCompare = (used, available, place) => { 63 | for (let i = 0; i < used.length; i++) { 64 | if (used[i] !== available[i]) { 65 | if (used[i] < available[i]) return i < place + 1 ? choices.red : choices.green; 66 | } 67 | } 68 | }; 69 | switch (version[0]) { 70 | case '^': 71 | result = placeCompare(usedNumberArr, availableNumberArr, 0); 72 | break; 73 | case '~': 74 | result = placeCompare(usedNumberArr, availableNumberArr, 1); 75 | break; 76 | default: 77 | result = version === latest ? '' : choices.red; 78 | break; 79 | } 80 | } else { 81 | if (version === undefined && latest !== undefined){ 82 | result = choices.green; 83 | } 84 | } 85 | return result; 86 | } 87 | 88 | 89 | }; --------------------------------------------------------------------------------