@deot/dev-updater 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @deot/dev-updater
2
+
3
+ 更新npm包
@@ -0,0 +1,243 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const devShared = require('@deot/dev-shared');
6
+ const path = require('node:path');
7
+ const fs = require('fs-extra');
8
+ const chalk = require('chalk');
9
+ const ora = require('ora');
10
+ const semver = require('semver');
11
+
12
+ function _interopNamespaceDefault(e) {
13
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
14
+ if (e) {
15
+ for (const k in e) {
16
+ if (k !== 'default') {
17
+ const d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: () => e[k]
21
+ });
22
+ }
23
+ }
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ const path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
30
+
31
+ const fitVersion = (versions, version, commandOptions) => {
32
+ let vRegex = /([\d]+\.[\d]+\..*)/;
33
+ if (!vRegex.test(version))
34
+ return version;
35
+ const { major = false, minor = false, patch = false } = commandOptions || {};
36
+ let originalPrefix = (version.match(/([^\d]*).*/) || ["", ""])[1];
37
+ let prefix = major ? ">=" : minor ? "^" : patch ? "~" : originalPrefix;
38
+ let oldVersion = version.match(vRegex)[1];
39
+ let newVersion = oldVersion;
40
+ let vailds = versions.slice(versions.indexOf(oldVersion) + 1);
41
+ for (let i = 0; i < vailds.length; i++) {
42
+ if (!semver.satisfies(vailds[i], prefix + oldVersion)) {
43
+ newVersion = i - 1 < 0 ? oldVersion : vailds[i - 1];
44
+ break;
45
+ } else {
46
+ newVersion = vailds[i];
47
+ }
48
+ }
49
+ return `${originalPrefix}${newVersion}`;
50
+ };
51
+
52
+ class Update {
53
+ packageOptionsMap;
54
+ commandOptions;
55
+ constructor(commandOptions) {
56
+ const locals = devShared.Locals.impl();
57
+ const { packageDir } = locals;
58
+ const packageOptionsMap = {
59
+ ...Object.keys(locals.packageOptionsMap).reduce((result, key) => {
60
+ result[path__namespace.resolve(packageDir, key)] = locals.packageOptionsMap[key];
61
+ return result;
62
+ }, {}),
63
+ [packageDir]: locals.packageOptions
64
+ };
65
+ this.packageOptionsMap = packageOptionsMap;
66
+ this.commandOptions = commandOptions;
67
+ }
68
+ async getPackageChanged() {
69
+ const { packageOptionsMap, commandOptions } = this;
70
+ const packageNames = {};
71
+ Object.keys(packageOptionsMap).forEach((key) => {
72
+ const { dependencies = {}, devDependencies = {} } = packageOptionsMap[key];
73
+ const deps = { ...dependencies, ...devDependencies };
74
+ Object.keys(deps).forEach((packageName) => {
75
+ let version = deps[packageName];
76
+ packageNames[packageName] = packageNames[packageName] || {};
77
+ if (typeof packageNames[packageName][version] !== "string") {
78
+ packageNames[packageName][version] = "";
79
+ }
80
+ });
81
+ });
82
+ await Promise.all(Object.keys(packageNames).map((packageName) => {
83
+ return new Promise((resolve) => {
84
+ devShared.Shell.exec("npm", ["view", packageName, "versions"]).then(({ stdout }) => {
85
+ let versions = JSON.parse(stdout.replace(/'/g, '"'));
86
+ Object.keys(packageNames[packageName]).forEach((version) => {
87
+ let newVersion = fitVersion(versions, version, commandOptions);
88
+ if (newVersion === version) {
89
+ delete packageNames[packageName][version];
90
+ } else {
91
+ packageNames[packageName][version] = newVersion;
92
+ }
93
+ });
94
+ if (!Object.keys(packageNames[packageName]).length) {
95
+ delete packageNames[packageName];
96
+ }
97
+ resolve();
98
+ }).catch(() => {
99
+ resolve();
100
+ });
101
+ });
102
+ }));
103
+ return packageNames;
104
+ }
105
+ async updatePackageOptions(changed) {
106
+ const { packageOptionsMap, commandOptions } = this;
107
+ Object.keys(packageOptionsMap).forEach((packageDir) => {
108
+ const packageOptions = packageOptionsMap[packageDir];
109
+ const { devDependencies = {}, dependencies = {} } = packageOptions;
110
+ let isChanged = false;
111
+ [devDependencies, dependencies].forEach((target) => {
112
+ Object.keys(target).forEach((packageName) => {
113
+ let version = target[packageName];
114
+ if (changed[packageName]?.[version]) {
115
+ isChanged = true;
116
+ target[packageName] = changed[packageName][version];
117
+ }
118
+ });
119
+ });
120
+ if (isChanged) {
121
+ if (commandOptions.dryRun) {
122
+ const locals = devShared.Locals.impl();
123
+ devShared.Logger.log(chalk.magenta(`CHANGED: `) + chalk.yellow(`Skipping ${path__namespace.relative(locals.cwd, packageDir)} Update`));
124
+ } else {
125
+ fs.outputFileSync(`${packageDir}/package.json`, JSON.stringify(packageOptions, null, 2));
126
+ }
127
+ }
128
+ });
129
+ }
130
+ async updateLock() {
131
+ if (this.commandOptions.dryRun) {
132
+ devShared.Logger.log(chalk.yellow(`Skipping pnpm-lock.yaml Update`));
133
+ } else {
134
+ devShared.Logger.log(chalk.magenta(`CHANGED: `) + `pnpm-lock.yaml`);
135
+ await devShared.Shell.spawn("npx", ["pnpm", "install", "--lockfile-only"]);
136
+ }
137
+ }
138
+ async commit(message) {
139
+ const { commit, dryRun } = this.commandOptions;
140
+ if (!commit) {
141
+ devShared.Logger.log(chalk.magenta(`COMMIT: `) + "Disabled.");
142
+ } else if (dryRun) {
143
+ devShared.Logger.log(chalk.magenta(`COMMIT: `) + chalk.yellow(`Skipping Git Commit`) + `
144
+ ${message}`);
145
+ } else {
146
+ devShared.Logger.log(chalk.magenta(`COMMIT: `) + `package.json, pnpm-lock.yaml`);
147
+ await devShared.Shell.spawn("git", ["add", process.cwd()]);
148
+ await devShared.Shell.spawn("git", ["commit", "--m", `'${message}'`]);
149
+ }
150
+ }
151
+ async push() {
152
+ const { push, dryRun } = this.commandOptions;
153
+ if (!push) {
154
+ devShared.Logger.log(chalk.magenta(`PUSH: `) + "Disabled.");
155
+ } else if (dryRun) {
156
+ devShared.Logger.log(chalk.magenta(`PUSH: `) + chalk.yellow(`Skipping Git PUSH`));
157
+ } else {
158
+ devShared.Logger.log(chalk.yellow("Git Fetch..."));
159
+ await devShared.Shell.spawn("git", ["fetch", "--prune", "--prune-tags"]);
160
+ await devShared.Shell.spawn("git", ["pull", "--rebase"]);
161
+ await devShared.Shell.spawn("git", ["push"]);
162
+ }
163
+ }
164
+ async test() {
165
+ const { test, dryRun } = this.commandOptions;
166
+ if (!test) {
167
+ devShared.Logger.log(chalk.magenta(`Test: `) + "Disabled.");
168
+ } else if (dryRun) {
169
+ devShared.Logger.log(chalk.yellow("Skipping Test"));
170
+ return;
171
+ } else {
172
+ devShared.Logger.log(chalk.yellow("Test..."));
173
+ }
174
+ const { rootPackageOptions } = devShared.Locals.impl();
175
+ if (rootPackageOptions?.scripts?.test) {
176
+ await devShared.Shell.exec(`npm run test -- --package-name '*'`);
177
+ }
178
+ }
179
+ async process() {
180
+ const spinner = ora(`Analyze ...`);
181
+ spinner.start();
182
+ let changed = await this.getPackageChanged();
183
+ spinner.stop();
184
+ let message = `chore: deps updated
185
+
186
+ `;
187
+ let keys = Object.keys(changed);
188
+ if (!keys.length) {
189
+ devShared.Logger.log(chalk.red(`No Package Update Found.`));
190
+ return;
191
+ }
192
+ devShared.Logger.log(chalk.magenta(`ANALYZE: `));
193
+ keys.forEach((key) => {
194
+ Object.keys(changed[key]).forEach((version) => {
195
+ message += `${key}: ${version} -> ${changed[key][version]}
196
+ `;
197
+ devShared.Logger.log(`${chalk.cyan(key)}: ${chalk.yellow(version)} -> ${chalk.green(changed[key][version])}`);
198
+ });
199
+ });
200
+ await this.updatePackageOptions(changed);
201
+ await this.updateLock();
202
+ await this.test();
203
+ await this.commit(message);
204
+ await this.push();
205
+ devShared.Logger.log(chalk.magenta(`FINISH: `) + chalk.green(`Update Successed.`));
206
+ if (this.commandOptions.dryRun) {
207
+ devShared.Logger.log(
208
+ chalk.green("NO DRY RUN WAY: ") + chalk.grey(`npm run update -- --no-dry-run
209
+ `)
210
+ );
211
+ }
212
+ }
213
+ }
214
+ const update = (commandOptions) => {
215
+ return new Update(commandOptions);
216
+ };
217
+
218
+ const run = (options) => devShared.Utils.autoCatch(async () => {
219
+ options = {
220
+ dryRun: true,
221
+ commit: true,
222
+ push: true,
223
+ test: true,
224
+ major: false,
225
+ minor: false,
226
+ patch: false,
227
+ ...options
228
+ };
229
+ if (process.env.NODE_ENV === "UNIT")
230
+ return devShared.Shell.spawn(`echo update`);
231
+ await update(options).process();
232
+ }, {
233
+ onError: (e) => {
234
+ if (typeof e === "number" && e === 1) {
235
+ devShared.Logger.error("更新失败");
236
+ } else {
237
+ devShared.Logger.error(e);
238
+ }
239
+ process.exit(1);
240
+ }
241
+ });
242
+
243
+ exports.run = run;
@@ -0,0 +1,5 @@
1
+ import type { Options } from '@deot/dev-shared';
2
+
3
+ export declare const run: (options: Options) => Promise<any>;
4
+
5
+ export { }
@@ -0,0 +1,220 @@
1
+ import { Locals, Shell, Logger, Utils } from '@deot/dev-shared';
2
+ import * as path from 'node:path';
3
+ import fs from 'fs-extra';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import semver from 'semver';
7
+
8
+ const fitVersion = (versions, version, commandOptions) => {
9
+ let vRegex = /([\d]+\.[\d]+\..*)/;
10
+ if (!vRegex.test(version))
11
+ return version;
12
+ const { major = false, minor = false, patch = false } = commandOptions || {};
13
+ let originalPrefix = (version.match(/([^\d]*).*/) || ["", ""])[1];
14
+ let prefix = major ? ">=" : minor ? "^" : patch ? "~" : originalPrefix;
15
+ let oldVersion = version.match(vRegex)[1];
16
+ let newVersion = oldVersion;
17
+ let vailds = versions.slice(versions.indexOf(oldVersion) + 1);
18
+ for (let i = 0; i < vailds.length; i++) {
19
+ if (!semver.satisfies(vailds[i], prefix + oldVersion)) {
20
+ newVersion = i - 1 < 0 ? oldVersion : vailds[i - 1];
21
+ break;
22
+ } else {
23
+ newVersion = vailds[i];
24
+ }
25
+ }
26
+ return `${originalPrefix}${newVersion}`;
27
+ };
28
+
29
+ class Update {
30
+ packageOptionsMap;
31
+ commandOptions;
32
+ constructor(commandOptions) {
33
+ const locals = Locals.impl();
34
+ const { packageDir } = locals;
35
+ const packageOptionsMap = {
36
+ ...Object.keys(locals.packageOptionsMap).reduce((result, key) => {
37
+ result[path.resolve(packageDir, key)] = locals.packageOptionsMap[key];
38
+ return result;
39
+ }, {}),
40
+ [packageDir]: locals.packageOptions
41
+ };
42
+ this.packageOptionsMap = packageOptionsMap;
43
+ this.commandOptions = commandOptions;
44
+ }
45
+ async getPackageChanged() {
46
+ const { packageOptionsMap, commandOptions } = this;
47
+ const packageNames = {};
48
+ Object.keys(packageOptionsMap).forEach((key) => {
49
+ const { dependencies = {}, devDependencies = {} } = packageOptionsMap[key];
50
+ const deps = { ...dependencies, ...devDependencies };
51
+ Object.keys(deps).forEach((packageName) => {
52
+ let version = deps[packageName];
53
+ packageNames[packageName] = packageNames[packageName] || {};
54
+ if (typeof packageNames[packageName][version] !== "string") {
55
+ packageNames[packageName][version] = "";
56
+ }
57
+ });
58
+ });
59
+ await Promise.all(Object.keys(packageNames).map((packageName) => {
60
+ return new Promise((resolve) => {
61
+ Shell.exec("npm", ["view", packageName, "versions"]).then(({ stdout }) => {
62
+ let versions = JSON.parse(stdout.replace(/'/g, '"'));
63
+ Object.keys(packageNames[packageName]).forEach((version) => {
64
+ let newVersion = fitVersion(versions, version, commandOptions);
65
+ if (newVersion === version) {
66
+ delete packageNames[packageName][version];
67
+ } else {
68
+ packageNames[packageName][version] = newVersion;
69
+ }
70
+ });
71
+ if (!Object.keys(packageNames[packageName]).length) {
72
+ delete packageNames[packageName];
73
+ }
74
+ resolve();
75
+ }).catch(() => {
76
+ resolve();
77
+ });
78
+ });
79
+ }));
80
+ return packageNames;
81
+ }
82
+ async updatePackageOptions(changed) {
83
+ const { packageOptionsMap, commandOptions } = this;
84
+ Object.keys(packageOptionsMap).forEach((packageDir) => {
85
+ const packageOptions = packageOptionsMap[packageDir];
86
+ const { devDependencies = {}, dependencies = {} } = packageOptions;
87
+ let isChanged = false;
88
+ [devDependencies, dependencies].forEach((target) => {
89
+ Object.keys(target).forEach((packageName) => {
90
+ let version = target[packageName];
91
+ if (changed[packageName]?.[version]) {
92
+ isChanged = true;
93
+ target[packageName] = changed[packageName][version];
94
+ }
95
+ });
96
+ });
97
+ if (isChanged) {
98
+ if (commandOptions.dryRun) {
99
+ const locals = Locals.impl();
100
+ Logger.log(chalk.magenta(`CHANGED: `) + chalk.yellow(`Skipping ${path.relative(locals.cwd, packageDir)} Update`));
101
+ } else {
102
+ fs.outputFileSync(`${packageDir}/package.json`, JSON.stringify(packageOptions, null, 2));
103
+ }
104
+ }
105
+ });
106
+ }
107
+ async updateLock() {
108
+ if (this.commandOptions.dryRun) {
109
+ Logger.log(chalk.yellow(`Skipping pnpm-lock.yaml Update`));
110
+ } else {
111
+ Logger.log(chalk.magenta(`CHANGED: `) + `pnpm-lock.yaml`);
112
+ await Shell.spawn("npx", ["pnpm", "install", "--lockfile-only"]);
113
+ }
114
+ }
115
+ async commit(message) {
116
+ const { commit, dryRun } = this.commandOptions;
117
+ if (!commit) {
118
+ Logger.log(chalk.magenta(`COMMIT: `) + "Disabled.");
119
+ } else if (dryRun) {
120
+ Logger.log(chalk.magenta(`COMMIT: `) + chalk.yellow(`Skipping Git Commit`) + `
121
+ ${message}`);
122
+ } else {
123
+ Logger.log(chalk.magenta(`COMMIT: `) + `package.json, pnpm-lock.yaml`);
124
+ await Shell.spawn("git", ["add", process.cwd()]);
125
+ await Shell.spawn("git", ["commit", "--m", `'${message}'`]);
126
+ }
127
+ }
128
+ async push() {
129
+ const { push, dryRun } = this.commandOptions;
130
+ if (!push) {
131
+ Logger.log(chalk.magenta(`PUSH: `) + "Disabled.");
132
+ } else if (dryRun) {
133
+ Logger.log(chalk.magenta(`PUSH: `) + chalk.yellow(`Skipping Git PUSH`));
134
+ } else {
135
+ Logger.log(chalk.yellow("Git Fetch..."));
136
+ await Shell.spawn("git", ["fetch", "--prune", "--prune-tags"]);
137
+ await Shell.spawn("git", ["pull", "--rebase"]);
138
+ await Shell.spawn("git", ["push"]);
139
+ }
140
+ }
141
+ async test() {
142
+ const { test, dryRun } = this.commandOptions;
143
+ if (!test) {
144
+ Logger.log(chalk.magenta(`Test: `) + "Disabled.");
145
+ } else if (dryRun) {
146
+ Logger.log(chalk.yellow("Skipping Test"));
147
+ return;
148
+ } else {
149
+ Logger.log(chalk.yellow("Test..."));
150
+ }
151
+ const { rootPackageOptions } = Locals.impl();
152
+ if (rootPackageOptions?.scripts?.test) {
153
+ await Shell.exec(`npm run test -- --package-name '*'`);
154
+ }
155
+ }
156
+ async process() {
157
+ const spinner = ora(`Analyze ...`);
158
+ spinner.start();
159
+ let changed = await this.getPackageChanged();
160
+ spinner.stop();
161
+ let message = `chore: deps updated
162
+
163
+ `;
164
+ let keys = Object.keys(changed);
165
+ if (!keys.length) {
166
+ Logger.log(chalk.red(`No Package Update Found.`));
167
+ return;
168
+ }
169
+ Logger.log(chalk.magenta(`ANALYZE: `));
170
+ keys.forEach((key) => {
171
+ Object.keys(changed[key]).forEach((version) => {
172
+ message += `${key}: ${version} -> ${changed[key][version]}
173
+ `;
174
+ Logger.log(`${chalk.cyan(key)}: ${chalk.yellow(version)} -> ${chalk.green(changed[key][version])}`);
175
+ });
176
+ });
177
+ await this.updatePackageOptions(changed);
178
+ await this.updateLock();
179
+ await this.test();
180
+ await this.commit(message);
181
+ await this.push();
182
+ Logger.log(chalk.magenta(`FINISH: `) + chalk.green(`Update Successed.`));
183
+ if (this.commandOptions.dryRun) {
184
+ Logger.log(
185
+ chalk.green("NO DRY RUN WAY: ") + chalk.grey(`npm run update -- --no-dry-run
186
+ `)
187
+ );
188
+ }
189
+ }
190
+ }
191
+ const update = (commandOptions) => {
192
+ return new Update(commandOptions);
193
+ };
194
+
195
+ const run = (options) => Utils.autoCatch(async () => {
196
+ options = {
197
+ dryRun: true,
198
+ commit: true,
199
+ push: true,
200
+ test: true,
201
+ major: false,
202
+ minor: false,
203
+ patch: false,
204
+ ...options
205
+ };
206
+ if (process.env.NODE_ENV === "UNIT")
207
+ return Shell.spawn(`echo update`);
208
+ await update(options).process();
209
+ }, {
210
+ onError: (e) => {
211
+ if (typeof e === "number" && e === 1) {
212
+ Logger.error("更新失败");
213
+ } else {
214
+ Logger.error(e);
215
+ }
216
+ process.exit(1);
217
+ }
218
+ });
219
+
220
+ export { run };
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@deot/dev-updater",
3
+ "version": "2.2.0",
4
+ "main": "dist/index.es.js",
5
+ "module": "dist/index.es.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "license": "MIT",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "dependencies": {
16
+ "@deot/dev-shared": "^2.2.0",
17
+ "chalk": "^5.3.0",
18
+ "fs-extra": "^11.1.1",
19
+ "ora": "^6.3.1",
20
+ "semver": "^7.5.4"
21
+ }
22
+ }