├── 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 | };
--------------------------------------------------------------------------------