deplift 1.1.0 → 1.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/dist/cjs/index.cjs +74 -39
- package/dist/esm/index.mjs +74 -39
- package/package.json +12 -7
package/dist/cjs/index.cjs
CHANGED
|
@@ -5,16 +5,14 @@ var path = require('node:path');
|
|
|
5
5
|
var promises = require('node:fs/promises');
|
|
6
6
|
var node_child_process = require('node:child_process');
|
|
7
7
|
var fg = require('fast-glob');
|
|
8
|
+
var yargs = require('yargs');
|
|
9
|
+
var helpers = require('yargs/helpers');
|
|
8
10
|
|
|
9
|
-
const defaultIgnore = [
|
|
10
|
-
const depSections = [
|
|
11
|
-
const
|
|
12
|
-
const dryRun = args.includes("--dry-run");
|
|
13
|
-
const noInstall = args.includes("--no-install");
|
|
14
|
-
if (dryRun) console.log("💡 Dry run enabled — no files will be changed or installed.");
|
|
15
|
-
const stripPrefix = version => version.replace(/^[^0-9]*/, "");
|
|
11
|
+
const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/coverage/**', '**/build/**', '**/.next/**', '**/.docusaurus/**'];
|
|
12
|
+
const depSections = ['dependencies', 'devDependencies'];
|
|
13
|
+
const stripPrefix = version => version.replace(/^\D+/, '');
|
|
16
14
|
const isStableRelease = version => /^\d+\.\d+\.\d+$/.test(version);
|
|
17
|
-
const extractSemVerParts = semver => semver.split(
|
|
15
|
+
const extractSemVerParts = semver => semver.split('.').map(Number);
|
|
18
16
|
function isSemVerGreater(v1, v2) {
|
|
19
17
|
const [major1, minor1, patch1] = extractSemVerParts(v1);
|
|
20
18
|
const [major2, minor2, patch2] = extractSemVerParts(v2);
|
|
@@ -23,11 +21,11 @@ function isSemVerGreater(v1, v2) {
|
|
|
23
21
|
return patch1 > patch2;
|
|
24
22
|
}
|
|
25
23
|
const loadConfig = async () => {
|
|
26
|
-
const configPath = path.resolve(
|
|
24
|
+
const configPath = path.resolve('deplift.config.json');
|
|
27
25
|
try {
|
|
28
|
-
const raw = await promises.readFile(configPath,
|
|
26
|
+
const raw = await promises.readFile(configPath, 'utf-8');
|
|
29
27
|
const parsed = JSON.parse(raw);
|
|
30
|
-
if (parsed && typeof parsed ===
|
|
28
|
+
if (parsed && typeof parsed === 'object') {
|
|
31
29
|
return parsed;
|
|
32
30
|
}
|
|
33
31
|
console.warn(`⚠️ Config file exists but is not a valid object: ${configPath}`);
|
|
@@ -55,19 +53,54 @@ const fetchLatestVersion = async dep => {
|
|
|
55
53
|
}
|
|
56
54
|
return dep;
|
|
57
55
|
};
|
|
56
|
+
const parseArgs = async () => {
|
|
57
|
+
const argv = await yargs(helpers.hideBin(process.argv)).option('major', {
|
|
58
|
+
type: 'array',
|
|
59
|
+
describe: 'major dep=version pairs',
|
|
60
|
+
default: [],
|
|
61
|
+
coerce: pairs => {
|
|
62
|
+
const result = {};
|
|
63
|
+
for (const pair of pairs) {
|
|
64
|
+
const idx = pair.indexOf('=');
|
|
65
|
+
if (idx === -1) {
|
|
66
|
+
throw new Error(`Invalid --major value "${pair}", expected dep=version`);
|
|
67
|
+
}
|
|
68
|
+
const key = pair.slice(0, idx);
|
|
69
|
+
const value = pair.slice(idx + 1);
|
|
70
|
+
result[key] = Number(value);
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
}).option('dry-run', {
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
describe: 'Run without making changes',
|
|
77
|
+
default: false
|
|
78
|
+
}).option('install', {
|
|
79
|
+
type: 'boolean',
|
|
80
|
+
describe: 'Run npm install',
|
|
81
|
+
default: true
|
|
82
|
+
}).strict().help().parse();
|
|
83
|
+
return argv;
|
|
84
|
+
};
|
|
58
85
|
async function main() {
|
|
86
|
+
const {
|
|
87
|
+
dryRun,
|
|
88
|
+
install: runInstall,
|
|
89
|
+
major: majorCaps
|
|
90
|
+
} = await parseArgs();
|
|
91
|
+
if (dryRun) console.log('💡 Dry run enabled — no files will be changed or installed.');
|
|
59
92
|
const config = await loadConfig();
|
|
60
93
|
const ignorePatterns = Array.isArray(config.ignore) ? Array.from(new Set([...defaultIgnore, ...config.ignore])) : defaultIgnore;
|
|
61
|
-
const packageFiles = await fg.glob(
|
|
94
|
+
const packageFiles = await fg.glob('**/package.json', {
|
|
62
95
|
ignore: ignorePatterns
|
|
63
96
|
});
|
|
64
97
|
if (packageFiles.length === 0) {
|
|
65
|
-
console.log(
|
|
98
|
+
console.log('❌ No package.json files found.');
|
|
66
99
|
process.exit(0);
|
|
67
100
|
}
|
|
68
101
|
for (const packageJson of packageFiles) {
|
|
69
102
|
const packageJsonPath = path.resolve(packageJson);
|
|
70
|
-
const pkgRaw = await promises.readFile(packageJsonPath,
|
|
103
|
+
const pkgRaw = await promises.readFile(packageJsonPath, 'utf-8');
|
|
71
104
|
let pkgData;
|
|
72
105
|
try {
|
|
73
106
|
pkgData = JSON.parse(pkgRaw);
|
|
@@ -79,7 +112,7 @@ async function main() {
|
|
|
79
112
|
const dependencies = depSections.reduce((accu, section) => {
|
|
80
113
|
const sectionData = pkgData[section];
|
|
81
114
|
if (!sectionData) return accu;
|
|
82
|
-
const entries = Object.entries(sectionData).filter(([_, version]) => !version.startsWith(
|
|
115
|
+
const entries = Object.entries(sectionData).filter(([_, version]) => !version.startsWith('file:')).map(([pkg, current]) => ({
|
|
83
116
|
section,
|
|
84
117
|
pkg,
|
|
85
118
|
current
|
|
@@ -91,52 +124,54 @@ async function main() {
|
|
|
91
124
|
for (const {
|
|
92
125
|
section,
|
|
93
126
|
pkg,
|
|
94
|
-
current,
|
|
127
|
+
current: rawCurrent,
|
|
95
128
|
latest
|
|
96
129
|
} of latestDeps) {
|
|
97
130
|
// Failed to fetch the pkg
|
|
98
131
|
if (!latest) continue;
|
|
99
|
-
|
|
100
|
-
|
|
132
|
+
const current = stripPrefix(rawCurrent);
|
|
133
|
+
if (current === latest) {
|
|
134
|
+
console.log(` ${pkg} is already up to date (${latest})`);
|
|
101
135
|
continue;
|
|
102
136
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
console.log(` ${pkg} is already up to date (${latest})`);
|
|
137
|
+
if (isStableRelease(current) && !isStableRelease(latest)) {
|
|
138
|
+
console.log(` ⚠️ [skipped] ${pkg}: latest version is not a stable release (${latest})`);
|
|
106
139
|
continue;
|
|
107
140
|
}
|
|
108
|
-
if (isSemVerGreater(
|
|
109
|
-
console.log(` ⚠️ [skipped] ${pkg}: current (${
|
|
141
|
+
if (isSemVerGreater(current, latest)) {
|
|
142
|
+
console.log(` ⚠️ [skipped] ${pkg}: current (${current}) version is higher than the latest (${latest})`);
|
|
110
143
|
continue;
|
|
111
144
|
}
|
|
112
|
-
const [currentMajor] = extractSemVerParts(currentVersion);
|
|
113
145
|
const [latestMajor] = extractSemVerParts(latest);
|
|
114
|
-
|
|
146
|
+
if (latestMajor > majorCaps[pkg]) {
|
|
147
|
+
console.log(` ⚠️ [skipped] ${pkg}: ${latest} is available, but the major version is capped at v${majorCaps[pkg]}`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const [currentMajor] = extractSemVerParts(current);
|
|
151
|
+
console.log(` ${currentMajor === latestMajor ? '✔' : '🚨[major]'} ${pkg}(${section}): ${rawCurrent} → ^${latest}`);
|
|
115
152
|
updated = true;
|
|
116
153
|
if (!dryRun) {
|
|
117
154
|
pkgData[section][pkg] = `^${latest}`;
|
|
118
155
|
}
|
|
119
156
|
}
|
|
120
157
|
if (updated) {
|
|
121
|
-
|
|
122
|
-
|
|
158
|
+
if (!dryRun) {
|
|
159
|
+
await promises.writeFile(packageJsonPath, JSON.stringify(pkgData, null, 2) + '\n');
|
|
160
|
+
console.log(` 💾 ${packageJson} updated.`);
|
|
161
|
+
}
|
|
123
162
|
} else {
|
|
124
163
|
console.log(` ✅ No changes needed for ${packageJson}.`);
|
|
125
164
|
}
|
|
126
|
-
if (
|
|
127
|
-
if (dryRun) {
|
|
128
|
-
console.log(` 📥 [Dry run] "npm install" for ${packageJson}.`);
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
165
|
+
if (!runInstall || dryRun || !updated) continue;
|
|
131
166
|
try {
|
|
132
167
|
const targetDir = path.dirname(packageJsonPath);
|
|
133
|
-
console.log(
|
|
134
|
-
node_child_process.execSync(
|
|
135
|
-
stdio:
|
|
168
|
+
console.log(' 📥 Installing...');
|
|
169
|
+
node_child_process.execSync('npm install', {
|
|
170
|
+
stdio: 'inherit',
|
|
136
171
|
cwd: targetDir
|
|
137
172
|
});
|
|
138
|
-
node_child_process.execSync(
|
|
139
|
-
stdio:
|
|
173
|
+
node_child_process.execSync('npm audit fix', {
|
|
174
|
+
stdio: 'inherit',
|
|
140
175
|
cwd: targetDir
|
|
141
176
|
});
|
|
142
177
|
} catch (err) {
|
|
@@ -144,7 +179,7 @@ async function main() {
|
|
|
144
179
|
}
|
|
145
180
|
}
|
|
146
181
|
}
|
|
147
|
-
main().then(() => console.log(
|
|
148
|
-
console.error(
|
|
182
|
+
main().then(() => console.log('\n[deplift] ✅ All dependency updates completed!')).catch(err => {
|
|
183
|
+
console.error('\n[deplift] ❌ Unexpected error:', err);
|
|
149
184
|
process.exit(1);
|
|
150
185
|
});
|
package/dist/esm/index.mjs
CHANGED
|
@@ -3,16 +3,14 @@ import path from 'node:path';
|
|
|
3
3
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
5
|
import fg from 'fast-glob';
|
|
6
|
+
import yargs from 'yargs';
|
|
7
|
+
import { hideBin } from 'yargs/helpers';
|
|
6
8
|
|
|
7
|
-
const defaultIgnore = [
|
|
8
|
-
const depSections = [
|
|
9
|
-
const
|
|
10
|
-
const dryRun = args.includes("--dry-run");
|
|
11
|
-
const noInstall = args.includes("--no-install");
|
|
12
|
-
if (dryRun) console.log("💡 Dry run enabled — no files will be changed or installed.");
|
|
13
|
-
const stripPrefix = version => version.replace(/^[^0-9]*/, "");
|
|
9
|
+
const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/coverage/**', '**/build/**', '**/.next/**', '**/.docusaurus/**'];
|
|
10
|
+
const depSections = ['dependencies', 'devDependencies'];
|
|
11
|
+
const stripPrefix = version => version.replace(/^\D+/, '');
|
|
14
12
|
const isStableRelease = version => /^\d+\.\d+\.\d+$/.test(version);
|
|
15
|
-
const extractSemVerParts = semver => semver.split(
|
|
13
|
+
const extractSemVerParts = semver => semver.split('.').map(Number);
|
|
16
14
|
function isSemVerGreater(v1, v2) {
|
|
17
15
|
const [major1, minor1, patch1] = extractSemVerParts(v1);
|
|
18
16
|
const [major2, minor2, patch2] = extractSemVerParts(v2);
|
|
@@ -21,11 +19,11 @@ function isSemVerGreater(v1, v2) {
|
|
|
21
19
|
return patch1 > patch2;
|
|
22
20
|
}
|
|
23
21
|
const loadConfig = async () => {
|
|
24
|
-
const configPath = path.resolve(
|
|
22
|
+
const configPath = path.resolve('deplift.config.json');
|
|
25
23
|
try {
|
|
26
|
-
const raw = await readFile(configPath,
|
|
24
|
+
const raw = await readFile(configPath, 'utf-8');
|
|
27
25
|
const parsed = JSON.parse(raw);
|
|
28
|
-
if (parsed && typeof parsed ===
|
|
26
|
+
if (parsed && typeof parsed === 'object') {
|
|
29
27
|
return parsed;
|
|
30
28
|
}
|
|
31
29
|
console.warn(`⚠️ Config file exists but is not a valid object: ${configPath}`);
|
|
@@ -53,19 +51,54 @@ const fetchLatestVersion = async dep => {
|
|
|
53
51
|
}
|
|
54
52
|
return dep;
|
|
55
53
|
};
|
|
54
|
+
const parseArgs = async () => {
|
|
55
|
+
const argv = await yargs(hideBin(process.argv)).option('major', {
|
|
56
|
+
type: 'array',
|
|
57
|
+
describe: 'major dep=version pairs',
|
|
58
|
+
default: [],
|
|
59
|
+
coerce: pairs => {
|
|
60
|
+
const result = {};
|
|
61
|
+
for (const pair of pairs) {
|
|
62
|
+
const idx = pair.indexOf('=');
|
|
63
|
+
if (idx === -1) {
|
|
64
|
+
throw new Error(`Invalid --major value "${pair}", expected dep=version`);
|
|
65
|
+
}
|
|
66
|
+
const key = pair.slice(0, idx);
|
|
67
|
+
const value = pair.slice(idx + 1);
|
|
68
|
+
result[key] = Number(value);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
}).option('dry-run', {
|
|
73
|
+
type: 'boolean',
|
|
74
|
+
describe: 'Run without making changes',
|
|
75
|
+
default: false
|
|
76
|
+
}).option('install', {
|
|
77
|
+
type: 'boolean',
|
|
78
|
+
describe: 'Run npm install',
|
|
79
|
+
default: true
|
|
80
|
+
}).strict().help().parse();
|
|
81
|
+
return argv;
|
|
82
|
+
};
|
|
56
83
|
async function main() {
|
|
84
|
+
const {
|
|
85
|
+
dryRun,
|
|
86
|
+
install: runInstall,
|
|
87
|
+
major: majorCaps
|
|
88
|
+
} = await parseArgs();
|
|
89
|
+
if (dryRun) console.log('💡 Dry run enabled — no files will be changed or installed.');
|
|
57
90
|
const config = await loadConfig();
|
|
58
91
|
const ignorePatterns = Array.isArray(config.ignore) ? Array.from(new Set([...defaultIgnore, ...config.ignore])) : defaultIgnore;
|
|
59
|
-
const packageFiles = await fg.glob(
|
|
92
|
+
const packageFiles = await fg.glob('**/package.json', {
|
|
60
93
|
ignore: ignorePatterns
|
|
61
94
|
});
|
|
62
95
|
if (packageFiles.length === 0) {
|
|
63
|
-
console.log(
|
|
96
|
+
console.log('❌ No package.json files found.');
|
|
64
97
|
process.exit(0);
|
|
65
98
|
}
|
|
66
99
|
for (const packageJson of packageFiles) {
|
|
67
100
|
const packageJsonPath = path.resolve(packageJson);
|
|
68
|
-
const pkgRaw = await readFile(packageJsonPath,
|
|
101
|
+
const pkgRaw = await readFile(packageJsonPath, 'utf-8');
|
|
69
102
|
let pkgData;
|
|
70
103
|
try {
|
|
71
104
|
pkgData = JSON.parse(pkgRaw);
|
|
@@ -77,7 +110,7 @@ async function main() {
|
|
|
77
110
|
const dependencies = depSections.reduce((accu, section) => {
|
|
78
111
|
const sectionData = pkgData[section];
|
|
79
112
|
if (!sectionData) return accu;
|
|
80
|
-
const entries = Object.entries(sectionData).filter(([_, version]) => !version.startsWith(
|
|
113
|
+
const entries = Object.entries(sectionData).filter(([_, version]) => !version.startsWith('file:')).map(([pkg, current]) => ({
|
|
81
114
|
section,
|
|
82
115
|
pkg,
|
|
83
116
|
current
|
|
@@ -89,52 +122,54 @@ async function main() {
|
|
|
89
122
|
for (const {
|
|
90
123
|
section,
|
|
91
124
|
pkg,
|
|
92
|
-
current,
|
|
125
|
+
current: rawCurrent,
|
|
93
126
|
latest
|
|
94
127
|
} of latestDeps) {
|
|
95
128
|
// Failed to fetch the pkg
|
|
96
129
|
if (!latest) continue;
|
|
97
|
-
|
|
98
|
-
|
|
130
|
+
const current = stripPrefix(rawCurrent);
|
|
131
|
+
if (current === latest) {
|
|
132
|
+
console.log(` ${pkg} is already up to date (${latest})`);
|
|
99
133
|
continue;
|
|
100
134
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
console.log(` ${pkg} is already up to date (${latest})`);
|
|
135
|
+
if (isStableRelease(current) && !isStableRelease(latest)) {
|
|
136
|
+
console.log(` ⚠️ [skipped] ${pkg}: latest version is not a stable release (${latest})`);
|
|
104
137
|
continue;
|
|
105
138
|
}
|
|
106
|
-
if (isSemVerGreater(
|
|
107
|
-
console.log(` ⚠️ [skipped] ${pkg}: current (${
|
|
139
|
+
if (isSemVerGreater(current, latest)) {
|
|
140
|
+
console.log(` ⚠️ [skipped] ${pkg}: current (${current}) version is higher than the latest (${latest})`);
|
|
108
141
|
continue;
|
|
109
142
|
}
|
|
110
|
-
const [currentMajor] = extractSemVerParts(currentVersion);
|
|
111
143
|
const [latestMajor] = extractSemVerParts(latest);
|
|
112
|
-
|
|
144
|
+
if (latestMajor > majorCaps[pkg]) {
|
|
145
|
+
console.log(` ⚠️ [skipped] ${pkg}: ${latest} is available, but the major version is capped at v${majorCaps[pkg]}`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const [currentMajor] = extractSemVerParts(current);
|
|
149
|
+
console.log(` ${currentMajor === latestMajor ? '✔' : '🚨[major]'} ${pkg}(${section}): ${rawCurrent} → ^${latest}`);
|
|
113
150
|
updated = true;
|
|
114
151
|
if (!dryRun) {
|
|
115
152
|
pkgData[section][pkg] = `^${latest}`;
|
|
116
153
|
}
|
|
117
154
|
}
|
|
118
155
|
if (updated) {
|
|
119
|
-
|
|
120
|
-
|
|
156
|
+
if (!dryRun) {
|
|
157
|
+
await writeFile(packageJsonPath, JSON.stringify(pkgData, null, 2) + '\n');
|
|
158
|
+
console.log(` 💾 ${packageJson} updated.`);
|
|
159
|
+
}
|
|
121
160
|
} else {
|
|
122
161
|
console.log(` ✅ No changes needed for ${packageJson}.`);
|
|
123
162
|
}
|
|
124
|
-
if (
|
|
125
|
-
if (dryRun) {
|
|
126
|
-
console.log(` 📥 [Dry run] "npm install" for ${packageJson}.`);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
163
|
+
if (!runInstall || dryRun || !updated) continue;
|
|
129
164
|
try {
|
|
130
165
|
const targetDir = path.dirname(packageJsonPath);
|
|
131
|
-
console.log(
|
|
132
|
-
execSync(
|
|
133
|
-
stdio:
|
|
166
|
+
console.log(' 📥 Installing...');
|
|
167
|
+
execSync('npm install', {
|
|
168
|
+
stdio: 'inherit',
|
|
134
169
|
cwd: targetDir
|
|
135
170
|
});
|
|
136
|
-
execSync(
|
|
137
|
-
stdio:
|
|
171
|
+
execSync('npm audit fix', {
|
|
172
|
+
stdio: 'inherit',
|
|
138
173
|
cwd: targetDir
|
|
139
174
|
});
|
|
140
175
|
} catch (err) {
|
|
@@ -142,7 +177,7 @@ async function main() {
|
|
|
142
177
|
}
|
|
143
178
|
}
|
|
144
179
|
}
|
|
145
|
-
main().then(() => console.log(
|
|
146
|
-
console.error(
|
|
180
|
+
main().then(() => console.log('\n[deplift] ✅ All dependency updates completed!')).catch(err => {
|
|
181
|
+
console.error('\n[deplift] ❌ Unexpected error:', err);
|
|
147
182
|
process.exit(1);
|
|
148
183
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deplift",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI to update deps in monorepos",
|
|
5
5
|
"author": "Zheng Song",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,23 +27,28 @@
|
|
|
27
27
|
"watch": "rollup -c -w",
|
|
28
28
|
"clean": "rm -Rf dist types",
|
|
29
29
|
"types": "tsc",
|
|
30
|
+
"pret": "prettier -c .",
|
|
31
|
+
"pret:fix": "prettier -w .",
|
|
30
32
|
"prepare": "rm -Rf types/__tests__",
|
|
31
|
-
"build": "run-s clean types bundle",
|
|
33
|
+
"build": "run-s pret clean types bundle",
|
|
32
34
|
"deplift": "node ./dist/cjs/index.cjs",
|
|
33
35
|
"deplift:dry-run": "node ./dist/cjs/index.cjs --dry-run"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
|
-
"@babel/core": "^7.
|
|
37
|
-
"@babel/preset-env": "^7.
|
|
38
|
+
"@babel/core": "^7.29.0",
|
|
39
|
+
"@babel/preset-env": "^7.29.0",
|
|
38
40
|
"@babel/preset-typescript": "^7.28.5",
|
|
39
41
|
"@rollup/plugin-babel": "^6.1.0",
|
|
40
42
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
41
|
-
"@types/node": "^25.
|
|
43
|
+
"@types/node": "^25.2.2",
|
|
44
|
+
"@types/yargs": "^17.0.35",
|
|
42
45
|
"npm-run-all": "^4.1.5",
|
|
43
|
-
"
|
|
46
|
+
"prettier": "^3.8.1",
|
|
47
|
+
"rollup": "^4.57.1",
|
|
44
48
|
"typescript": "^5.9.3"
|
|
45
49
|
},
|
|
46
50
|
"dependencies": {
|
|
47
|
-
"fast-glob": "^3.3.3"
|
|
51
|
+
"fast-glob": "^3.3.3",
|
|
52
|
+
"yargs": "^18.0.0"
|
|
48
53
|
}
|
|
49
54
|
}
|