npm-workspaces-publish-tool 0.0.4 ā 0.0.6
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/build/package.json +1 -1
- package/build/src/cli.js +11 -8
- package/package.json +1 -1
- package/build/src/bin/cli.js +0 -482
- package/build/src/bin/cli2.js +0 -444
- package/build/src/bin/cli3.js +0 -548
- package/build/src/diff.js +0 -1
- package/build/src/index.js +0 -1
package/build/package.json
CHANGED
package/build/src/cli.js
CHANGED
|
@@ -56,14 +56,14 @@ function getReleaseOrderFromInDegree(inDegree, dependencies) {
|
|
|
56
56
|
}
|
|
57
57
|
return order;
|
|
58
58
|
}
|
|
59
|
-
function getDirtyMap(workspaces, lastTag) {
|
|
59
|
+
function getDirtyMap(workspaces, lastTag, cwd) {
|
|
60
60
|
const dirtyMap = new Map();
|
|
61
61
|
const changedFiles = lastTag
|
|
62
62
|
? getChangesBetweenRefs(lastTag, 'HEAD', [], '', cwd)
|
|
63
63
|
: [];
|
|
64
64
|
for (const ws of workspaces) {
|
|
65
65
|
const isNew = !lastTag;
|
|
66
|
-
const isDirty = changedFiles.some((f) => f.
|
|
66
|
+
const isDirty = changedFiles.some((f) => resolve(cwd, f).includes(ws.path));
|
|
67
67
|
if (isNew) {
|
|
68
68
|
dirtyMap.set(ws.name, 'new');
|
|
69
69
|
}
|
|
@@ -123,7 +123,8 @@ function getDirtyPackagesVersionChanges(workspaces, dirtyMap, lastTag, cwd) {
|
|
|
123
123
|
});
|
|
124
124
|
continue;
|
|
125
125
|
}
|
|
126
|
-
const
|
|
126
|
+
const relativeWsPath = relative(cwd, ws.path);
|
|
127
|
+
const previousPkgRaw = git(['show', `${lastTag}:${relativeWsPath}/package.json`], {
|
|
127
128
|
cwd,
|
|
128
129
|
});
|
|
129
130
|
if (!previousPkgRaw.success) {
|
|
@@ -352,7 +353,7 @@ function validatePublish() {
|
|
|
352
353
|
const { dependencies } = createDependencyMap(packageInfos);
|
|
353
354
|
const inDegree = calculateWorkspaceInDegree(workspaces, dependencies);
|
|
354
355
|
const releaseOrder = getReleaseOrderFromInDegree(inDegree, dependencies);
|
|
355
|
-
const dirtyMap = getDirtyMap(workspaces, lastMonoRepoTag);
|
|
356
|
+
const dirtyMap = getDirtyMap(workspaces, lastMonoRepoTag, cwd);
|
|
356
357
|
const dirtyVersionChanges = getDirtyPackagesVersionChanges(workspaces, dirtyMap, lastMonoRepoTag, cwd);
|
|
357
358
|
console.log('šļø Building packages...');
|
|
358
359
|
for (const pkgName of releaseOrder) {
|
|
@@ -406,6 +407,7 @@ function validatePublish() {
|
|
|
406
407
|
const rootPackageJsonPath = resolve(cwd, 'package.json');
|
|
407
408
|
let previousRootVersion;
|
|
408
409
|
let currentRootVersion;
|
|
410
|
+
let rootPackageSuccessMessage;
|
|
409
411
|
try {
|
|
410
412
|
const rootPackage = JSON.parse(readFileSync(rootPackageJsonPath, 'utf8'));
|
|
411
413
|
currentRootVersion = rootPackage.version;
|
|
@@ -426,19 +428,19 @@ function validatePublish() {
|
|
|
426
428
|
hasError = true;
|
|
427
429
|
}
|
|
428
430
|
else {
|
|
429
|
-
|
|
431
|
+
rootPackageSuccessMessage = `š³ Root: ${lastRootPackage.version} ā ${currentRootVersion}`;
|
|
430
432
|
}
|
|
431
433
|
}
|
|
432
434
|
catch {
|
|
433
|
-
|
|
435
|
+
rootPackageSuccessMessage = `ā ļø Root: ${currentRootVersion} (previous version unavailable)`;
|
|
434
436
|
}
|
|
435
437
|
}
|
|
436
438
|
else {
|
|
437
|
-
|
|
439
|
+
rootPackageSuccessMessage = `ā ļø Root: ${currentRootVersion} (previous version unavailable)`;
|
|
438
440
|
}
|
|
439
441
|
}
|
|
440
442
|
else {
|
|
441
|
-
|
|
443
|
+
rootPackageSuccessMessage = `š³ Root: ${currentRootVersion} (first release)`;
|
|
442
444
|
}
|
|
443
445
|
}
|
|
444
446
|
catch (e) {
|
|
@@ -449,6 +451,7 @@ function validatePublish() {
|
|
|
449
451
|
console.error('\nFix the above issues before publishing.\n');
|
|
450
452
|
process.exit(1);
|
|
451
453
|
}
|
|
454
|
+
console.log(rootPackageSuccessMessage);
|
|
452
455
|
console.log('\nšļø Validating git status...\n');
|
|
453
456
|
if (!verifyCleanGitStatus(workspaces, dirtyMap, cwd)) {
|
|
454
457
|
process.exit(1);
|
package/package.json
CHANGED
package/build/src/bin/cli.js
DELETED
|
@@ -1,482 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { program } from 'commander';
|
|
3
|
-
import { readFileSync, writeFileSync } from 'fs';
|
|
4
|
-
import { createRequire } from 'module';
|
|
5
|
-
import { dirname, join, relative, resolve } from 'path';
|
|
6
|
-
import { cwd as getCwd } from 'process';
|
|
7
|
-
import semver from 'semver';
|
|
8
|
-
import { createDependencyMap, getChangesBetweenRefs, getPackageInfos, getWorkspaces, git, } from 'workspace-tools';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
|
-
const require = createRequire(import.meta.url);
|
|
11
|
-
const pkg = require('../../package.json');
|
|
12
|
-
const cwd = getCwd();
|
|
13
|
-
function getLastTag() {
|
|
14
|
-
const result = git(['tag', '--list', 'v*', '--sort=-v:refname'], { cwd });
|
|
15
|
-
if (!result.success)
|
|
16
|
-
return null;
|
|
17
|
-
const tags = result.stdout.split('\n').filter(Boolean);
|
|
18
|
-
return tags[0] || null;
|
|
19
|
-
}
|
|
20
|
-
function calculateWorkspaceInDegree(packageInfos, dependencies) {
|
|
21
|
-
const inDegree = new Map();
|
|
22
|
-
for (const pkgName of Object.keys(packageInfos)) {
|
|
23
|
-
inDegree.set(pkgName, 0);
|
|
24
|
-
}
|
|
25
|
-
for (const [pkgName, deps] of dependencies) {
|
|
26
|
-
for (const _ of deps) {
|
|
27
|
-
inDegree.set(pkgName, (inDegree.get(pkgName) ?? 0) + 1);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return inDegree;
|
|
31
|
-
}
|
|
32
|
-
function getReleaseOrderFromInDegree(inDegree, dependencies) {
|
|
33
|
-
const queue = Array.from(inDegree.entries())
|
|
34
|
-
.filter(([_, count]) => count === 0)
|
|
35
|
-
.map(([pkg]) => pkg);
|
|
36
|
-
const order = [];
|
|
37
|
-
while (queue.length > 0) {
|
|
38
|
-
const current = queue.shift();
|
|
39
|
-
order.push(current);
|
|
40
|
-
for (const [pkg, deps] of dependencies) {
|
|
41
|
-
if (deps.has(current)) {
|
|
42
|
-
const newCount = inDegree.get(pkg) - 1;
|
|
43
|
-
inDegree.set(pkg, newCount);
|
|
44
|
-
if (newCount === 0) {
|
|
45
|
-
queue.push(pkg);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return order;
|
|
51
|
-
}
|
|
52
|
-
function getDirtyMap(workspaces, lastTag) {
|
|
53
|
-
const dirtyMap = new Map();
|
|
54
|
-
const changedFiles = lastTag
|
|
55
|
-
? getChangesBetweenRefs(lastTag, 'HEAD', [], '', cwd)
|
|
56
|
-
: [];
|
|
57
|
-
for (const ws of workspaces) {
|
|
58
|
-
const isNew = !lastTag;
|
|
59
|
-
const isDirty = changedFiles.some((f) => f.startsWith(ws.path + '/'));
|
|
60
|
-
if (isNew) {
|
|
61
|
-
dirtyMap.set(ws.name, 'new');
|
|
62
|
-
}
|
|
63
|
-
else if (isDirty) {
|
|
64
|
-
dirtyMap.set(ws.name, 'dirty');
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
dirtyMap.set(ws.name, 'unchanged');
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return dirtyMap;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Returns version changes only for packages marked as 'dirty' in dirtyMap,
|
|
74
|
-
* comparing current package.json version to the version at the last tag.
|
|
75
|
-
* Has a `versionIncremented` field indicating if version was increased semver-wise.
|
|
76
|
-
*/
|
|
77
|
-
function getDirtyPackagesVersionChanges(workspaces, dirtyMap, lastTag, cwd) {
|
|
78
|
-
const result = new Map();
|
|
79
|
-
if (!lastTag)
|
|
80
|
-
return result;
|
|
81
|
-
for (const ws of workspaces) {
|
|
82
|
-
if (dirtyMap.get(ws.name) !== 'dirty')
|
|
83
|
-
continue;
|
|
84
|
-
const pkgPath = resolve(cwd, ws.path, 'package.json');
|
|
85
|
-
let currentPkg;
|
|
86
|
-
try {
|
|
87
|
-
currentPkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
88
|
-
}
|
|
89
|
-
catch (err) {
|
|
90
|
-
throw new Error(`Failed to parse current package.json for package "${ws.name}" at path "${pkgPath}": ${err}`);
|
|
91
|
-
}
|
|
92
|
-
const newVersion = currentPkg.version;
|
|
93
|
-
const previousPkgRaw = git(['show', `${lastTag}:${ws.path}/package.json`], { cwd });
|
|
94
|
-
if (!previousPkgRaw.success) {
|
|
95
|
-
result.set(ws.name, {
|
|
96
|
-
oldVersion: null,
|
|
97
|
-
newVersion,
|
|
98
|
-
versionChanged: true,
|
|
99
|
-
versionIncremented: true,
|
|
100
|
-
});
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
let previousPkg;
|
|
104
|
-
try {
|
|
105
|
-
previousPkg = JSON.parse(previousPkgRaw.stdout);
|
|
106
|
-
}
|
|
107
|
-
catch (err) {
|
|
108
|
-
throw new Error(`Failed to parse package.json from git at tag "${lastTag}" for package "${ws.name}": ${err}`);
|
|
109
|
-
}
|
|
110
|
-
const oldVersion = previousPkg.version;
|
|
111
|
-
const versionChanged = newVersion !== oldVersion;
|
|
112
|
-
const versionIncremented = oldVersion === null ||
|
|
113
|
-
(semver.valid(newVersion) && semver.valid(oldVersion))
|
|
114
|
-
? semver.gt(newVersion, oldVersion)
|
|
115
|
-
: false;
|
|
116
|
-
result.set(ws.name, {
|
|
117
|
-
oldVersion,
|
|
118
|
-
newVersion,
|
|
119
|
-
versionChanged,
|
|
120
|
-
versionIncremented,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
// Check git status to ensure all changes for release are committed appropriately
|
|
126
|
-
function verifyCleanGitStatus(workspaces, dirtyMap, cwd) {
|
|
127
|
-
// Files changed since last commit
|
|
128
|
-
const changedFilesRaw = git(['diff', '--name-only', 'HEAD'], { cwd })
|
|
129
|
-
.stdout.trim()
|
|
130
|
-
.split('\n')
|
|
131
|
-
.filter(Boolean);
|
|
132
|
-
// Files staged for commit
|
|
133
|
-
const stagedFilesRaw = git(['diff', '--cached', '--name-only'], { cwd })
|
|
134
|
-
.stdout.trim()
|
|
135
|
-
.split('\n')
|
|
136
|
-
.filter(Boolean);
|
|
137
|
-
// Files unstaged but changed (working dir different from index)
|
|
138
|
-
const unstagedFilesRaw = git(['diff', '--name-only'], { cwd })
|
|
139
|
-
.stdout.trim()
|
|
140
|
-
.split('\n')
|
|
141
|
-
.filter(Boolean);
|
|
142
|
-
// Get untracked files from git status
|
|
143
|
-
const status = git(['status', '--porcelain'], { cwd })
|
|
144
|
-
.stdout.trim()
|
|
145
|
-
.split('\n')
|
|
146
|
-
.filter(Boolean);
|
|
147
|
-
const untrackedFilesRaw = status
|
|
148
|
-
.filter((line) => line.startsWith('??'))
|
|
149
|
-
.map((line) => line.slice(3));
|
|
150
|
-
// Prepare map of workspace issues
|
|
151
|
-
const workspaceIssues = new Map();
|
|
152
|
-
for (const ws of workspaces) {
|
|
153
|
-
workspaceIssues.set(ws.name, {
|
|
154
|
-
stagedPackageJsonDirty: false,
|
|
155
|
-
unstagedPackageJsonDirty: false,
|
|
156
|
-
stagedFilesNotCommitted: [],
|
|
157
|
-
unstagedFilesNotStaged: [],
|
|
158
|
-
untrackedFiles: [],
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
function findWorkspaceForFile(filePath) {
|
|
162
|
-
const absoluteFilePath = resolve(cwd, filePath).replace(/\\/g, '/');
|
|
163
|
-
for (const ws of workspaces) {
|
|
164
|
-
const wsPathNormalized = ws.path.replace(/\\/g, '/');
|
|
165
|
-
if (absoluteFilePath === wsPathNormalized ||
|
|
166
|
-
absoluteFilePath.startsWith(wsPathNormalized + '/')) {
|
|
167
|
-
return ws;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return undefined;
|
|
171
|
-
}
|
|
172
|
-
// Helper sets for quick lookup
|
|
173
|
-
const changedFiles = new Set(changedFilesRaw);
|
|
174
|
-
const stagedFiles = new Set(stagedFilesRaw);
|
|
175
|
-
const unstagedFiles = new Set(unstagedFilesRaw);
|
|
176
|
-
const untrackedFiles = new Set(untrackedFilesRaw);
|
|
177
|
-
for (const filePath of changedFiles) {
|
|
178
|
-
const ws = findWorkspaceForFile(filePath);
|
|
179
|
-
if (!ws)
|
|
180
|
-
continue;
|
|
181
|
-
if (dirtyMap.get(ws.name) !== 'new' &&
|
|
182
|
-
dirtyMap.get(ws.name) !== 'dirty')
|
|
183
|
-
continue;
|
|
184
|
-
const issues = workspaceIssues.get(ws.name);
|
|
185
|
-
const isPackageJson = filePath.endsWith('package.json');
|
|
186
|
-
const isStaged = stagedFiles.has(filePath);
|
|
187
|
-
const isUnstaged = unstagedFiles.has(filePath);
|
|
188
|
-
// FIXME here
|
|
189
|
-
if (isPackageJson) {
|
|
190
|
-
if (!isStaged) {
|
|
191
|
-
issues.unstagedPackageJsonDirty = true;
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
issues.stagedPackageJsonDirty = true;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
if (!isStaged) {
|
|
199
|
-
issues.unstagedFilesNotStaged.push(filePath);
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
issues.stagedFilesNotCommitted.push(filePath);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
for (const filePath of untrackedFilesRaw) {
|
|
207
|
-
const ws = findWorkspaceForFile(filePath);
|
|
208
|
-
if (!ws)
|
|
209
|
-
continue;
|
|
210
|
-
if (dirtyMap.get(ws.name) !== 'new' &&
|
|
211
|
-
dirtyMap.get(ws.name) !== 'dirty')
|
|
212
|
-
continue;
|
|
213
|
-
workspaceIssues.get(ws.name).untrackedFiles.push(filePath);
|
|
214
|
-
}
|
|
215
|
-
let hasIssues = false;
|
|
216
|
-
for (const [wsName, issues] of workspaceIssues.entries()) {
|
|
217
|
-
const { stagedPackageJsonDirty, unstagedPackageJsonDirty, stagedFilesNotCommitted, unstagedFilesNotStaged, untrackedFiles, } = issues;
|
|
218
|
-
if (!stagedPackageJsonDirty &&
|
|
219
|
-
!unstagedPackageJsonDirty &&
|
|
220
|
-
stagedFilesNotCommitted.length === 0 &&
|
|
221
|
-
unstagedFilesNotStaged.length === 0 &&
|
|
222
|
-
untrackedFiles.length === 0) {
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
hasIssues = true;
|
|
226
|
-
console.error(`ā Workspace '${wsName}' has issues preventing publish:`);
|
|
227
|
-
const ws = workspaces.find((w) => w.name === wsName);
|
|
228
|
-
const missingFiles = [];
|
|
229
|
-
if (unstagedPackageJsonDirty) {
|
|
230
|
-
const relativePkgJson = relative(cwd, join(ws.path, 'package.json')).replace(/\\/g, '/');
|
|
231
|
-
missingFiles.push(relativePkgJson);
|
|
232
|
-
}
|
|
233
|
-
for (const file of unstagedFilesNotStaged) {
|
|
234
|
-
missingFiles.push(file);
|
|
235
|
-
}
|
|
236
|
-
if (missingFiles.length > 0) {
|
|
237
|
-
console.error(' ā ļø These files have changes since last commit but are NOT staged. Please stage them:');
|
|
238
|
-
for (const f of missingFiles) {
|
|
239
|
-
console.error(` - ${f}`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
// fix this this is not relative to the workspace root.. FIXME
|
|
243
|
-
if (stagedPackageJsonDirty) {
|
|
244
|
-
console.error(' ā ļø package.json is staged but not committed. Please commit it.');
|
|
245
|
-
}
|
|
246
|
-
if (stagedFilesNotCommitted.length > 0) {
|
|
247
|
-
console.error(' ā ļø These files are staged but not committed. Please commit them:');
|
|
248
|
-
for (const f of stagedFilesNotCommitted)
|
|
249
|
-
console.error(` - ${f}`);
|
|
250
|
-
}
|
|
251
|
-
if (untrackedFiles.length > 0) {
|
|
252
|
-
console.error(' ā ļø Untracked files (consider adding or ignoring):');
|
|
253
|
-
for (const f of untrackedFiles)
|
|
254
|
-
console.error(` - ${f}`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
// Check if root package.json has unstaged changes not committed
|
|
258
|
-
const rootPackageJsonPath = 'package.json';
|
|
259
|
-
const isRootPackageJsonChanged = changedFiles.has(rootPackageJsonPath);
|
|
260
|
-
const isRootPackageJsonStaged = stagedFiles.has(rootPackageJsonPath);
|
|
261
|
-
const isRootPackageJsonUnstaged = unstagedFiles.has(rootPackageJsonPath);
|
|
262
|
-
if (isRootPackageJsonChanged) {
|
|
263
|
-
if (!isRootPackageJsonStaged) {
|
|
264
|
-
console.error(`ā Root package.json has unstaged changes. Please stage or discard them before publishing.`);
|
|
265
|
-
hasIssues = true;
|
|
266
|
-
}
|
|
267
|
-
if (isRootPackageJsonStaged) {
|
|
268
|
-
console.error(`ā Root package.json is staged but not committed. Please commit it before publishing.`);
|
|
269
|
-
hasIssues = true;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (hasIssues) {
|
|
273
|
-
console.error('Please commit or stash the above changes before publishing.');
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
function validatePublish() {
|
|
279
|
-
const lastMonoRepoTag = getLastTag();
|
|
280
|
-
//console.log('lastMonoRepoTag', lastMonoRepoTag);
|
|
281
|
-
const workspaces = getWorkspaces(cwd);
|
|
282
|
-
//console.log('workspaces', workspaces);
|
|
283
|
-
const packageInfos = getPackageInfos(cwd); // this is a map of package.json "name" field and an unpackaked packagejson with packJsonPath extended into it
|
|
284
|
-
//const dependencyMap = createDependencyMap(packageInfos);
|
|
285
|
-
/*console.log(packageInfos);
|
|
286
|
-
console.log('---------------------------------------');*/
|
|
287
|
-
const workspacePackageNames = Object.keys(packageInfos); // nodes
|
|
288
|
-
//console.log('workspacePackageNames', workspacePackageNames);
|
|
289
|
-
//console.log('---------------------------------------');
|
|
290
|
-
//console.log(dependencyMap);
|
|
291
|
-
//const packageGraph = createPackageGraph(packageInfos);
|
|
292
|
-
//console.log('---------------------------------------');
|
|
293
|
-
//console.log('packageGraph', packageGraph);
|
|
294
|
-
const { dependencies, dependents } = createDependencyMap(packageInfos);
|
|
295
|
-
const roots = workspacePackageNames.filter((name) => !dependencies.get(name)?.size);
|
|
296
|
-
const visited = new Set();
|
|
297
|
-
function buildNode(name) {
|
|
298
|
-
if (visited.has(name)) {
|
|
299
|
-
return { name, children: [] };
|
|
300
|
-
}
|
|
301
|
-
visited.add(name);
|
|
302
|
-
const kids = Array.from(dependents.get(name) ?? []);
|
|
303
|
-
return { name, children: kids.map(buildNode) };
|
|
304
|
-
}
|
|
305
|
-
const tree = roots.map(buildNode);
|
|
306
|
-
console.log('tree', JSON.stringify(tree, null, 4));
|
|
307
|
-
const inDegree = calculateWorkspaceInDegree(packageInfos, dependencies);
|
|
308
|
-
console.log('inDegree', inDegree);
|
|
309
|
-
// Determine release order
|
|
310
|
-
const releaseOrder = getReleaseOrderFromInDegree(inDegree, dependencies);
|
|
311
|
-
console.log('releaseOrder', releaseOrder);
|
|
312
|
-
// Determine status of each workspace
|
|
313
|
-
const dirtyMap = getDirtyMap(workspaces, lastMonoRepoTag);
|
|
314
|
-
console.log('dirtyMap', dirtyMap);
|
|
315
|
-
// Determine which dirty packages have had their version field updated (which permits them to be published) if we have dirty packages which have not all been incremented
|
|
316
|
-
// Determine which dirty packages have version changes
|
|
317
|
-
const dirtyVersionChanges = getDirtyPackagesVersionChanges(workspaces, dirtyMap, lastMonoRepoTag, cwd);
|
|
318
|
-
let hasError = false;
|
|
319
|
-
for (const [pkgName, { oldVersion, newVersion, versionChanged, versionIncremented },] of dirtyVersionChanges.entries()) {
|
|
320
|
-
if (!semver.valid(newVersion)) {
|
|
321
|
-
console.error(`ā Package "${pkgName}" has an invalid current version: "${newVersion}".`);
|
|
322
|
-
hasError = true;
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
if (!versionChanged) {
|
|
326
|
-
console.error(`ā Package "${pkgName}" was modified but the version has not been updated (still "${newVersion}").`);
|
|
327
|
-
hasError = true;
|
|
328
|
-
// Print changed files directly from git using path as pattern
|
|
329
|
-
const ws = workspaces.find((w) => w.name === pkgName);
|
|
330
|
-
try {
|
|
331
|
-
const changedFiles = getChangesBetweenRefs(lastMonoRepoTag, 'HEAD', [], ws.path, // use path as pattern for precision
|
|
332
|
-
cwd);
|
|
333
|
-
if (changedFiles.length > 0) {
|
|
334
|
-
console.error(` āŖ Changed files in "${pkgName}":`);
|
|
335
|
-
for (const file of changedFiles) {
|
|
336
|
-
console.error(` - ${file.replace(ws.path + '/', '')}`);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
catch (err) {
|
|
341
|
-
console.error(` ā ļø Failed to get changed files for "${pkgName}": ${err}`);
|
|
342
|
-
}
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
if (!versionIncremented) {
|
|
346
|
-
console.error(`ā Package "${pkgName}" version changed from "${oldVersion}" to "${newVersion}", but the new version is not greater (semver).`);
|
|
347
|
-
hasError = true;
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
console.log(`ā
Package "${pkgName}" is ready to publish. Version increased from "${oldVersion}" to "${newVersion}".`);
|
|
351
|
-
}
|
|
352
|
-
if (hasError) {
|
|
353
|
-
console.error('\nFix the above issues before publishing.\n');
|
|
354
|
-
process.exit(1);
|
|
355
|
-
}
|
|
356
|
-
// Now if we get this far we have a possible combination of 'unchanged', 'dirty' but incremented correctly, or new.
|
|
357
|
-
// Before we set of ensure that we have commited all of our changes. And that our git status is clean.
|
|
358
|
-
const cleanGitStatus = verifyCleanGitStatus(workspaces, dirtyMap, cwd);
|
|
359
|
-
if (!cleanGitStatus)
|
|
360
|
-
process.exit(1);
|
|
361
|
-
// Print publish summary
|
|
362
|
-
console.log('\nPublish summary:\n');
|
|
363
|
-
const packagesToRelease = [];
|
|
364
|
-
for (const pkgName of releaseOrder) {
|
|
365
|
-
const status = dirtyMap.get(pkgName);
|
|
366
|
-
const versionInfo = dirtyVersionChanges.get(pkgName);
|
|
367
|
-
if (status === 'new') {
|
|
368
|
-
packagesToRelease.push(pkgName);
|
|
369
|
-
const ws = workspaces.find((w) => w.name === pkgName);
|
|
370
|
-
const currentVersion = JSON.parse(readFileSync(resolve(cwd, ws.path, 'package.json'), 'utf8')).version;
|
|
371
|
-
console.log(`š ${pkgName} @ ${currentVersion} (new)`);
|
|
372
|
-
}
|
|
373
|
-
if (status === 'dirty' && versionInfo?.versionIncremented) {
|
|
374
|
-
packagesToRelease.push(pkgName);
|
|
375
|
-
console.log(`ā¬ļø ${pkgName}: ${versionInfo.oldVersion} ā ${versionInfo.newVersion} (version incremented)`);
|
|
376
|
-
}
|
|
377
|
-
if (status === 'unchanged') {
|
|
378
|
-
const ws = workspaces.find((w) => w.name === pkgName);
|
|
379
|
-
const currentVersion = JSON.parse(readFileSync(resolve(cwd, ws.path, 'package.json'), 'utf8')).version;
|
|
380
|
-
console.log(`āļø ${pkgName} @ ${currentVersion} (unchanged)`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
console.log('');
|
|
384
|
-
// Now return useful information to the main program
|
|
385
|
-
return { releaseOrder, packageInfos };
|
|
386
|
-
}
|
|
387
|
-
export function replaceWorkspaceDepsWithVersions(packageInfos) {
|
|
388
|
-
for (const pkgName in packageInfos) {
|
|
389
|
-
const pkgInfo = packageInfos[pkgName];
|
|
390
|
-
let changed = false;
|
|
391
|
-
[
|
|
392
|
-
'dependencies',
|
|
393
|
-
'devDependencies',
|
|
394
|
-
'peerDependencies',
|
|
395
|
-
'optionalDependencies',
|
|
396
|
-
].forEach((depType) => {
|
|
397
|
-
const deps = pkgInfo.packageJson[depType];
|
|
398
|
-
if (!deps)
|
|
399
|
-
return;
|
|
400
|
-
for (const depName in deps) {
|
|
401
|
-
if (deps[depName] === '*') {
|
|
402
|
-
const wsPkg = packageInfos[depName];
|
|
403
|
-
if (wsPkg) {
|
|
404
|
-
deps[depName] = wsPkg.packageJson.version;
|
|
405
|
-
changed = true;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
if (changed) {
|
|
411
|
-
writeFileSync(pkgInfo.packageJsonPath, JSON.stringify(pkgInfo.packageJson, null, 2));
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
export function restoreWorkspaceDepsToStar(packageInfos) {
|
|
416
|
-
for (const pkgName in packageInfos) {
|
|
417
|
-
const pkgInfo = packageInfos[pkgName];
|
|
418
|
-
let changed = false;
|
|
419
|
-
[
|
|
420
|
-
'dependencies',
|
|
421
|
-
'devDependencies',
|
|
422
|
-
'peerDependencies',
|
|
423
|
-
'optionalDependencies',
|
|
424
|
-
].forEach((depType) => {
|
|
425
|
-
const deps = pkgInfo.packageJson[depType];
|
|
426
|
-
if (!deps)
|
|
427
|
-
return;
|
|
428
|
-
for (const depName in deps) {
|
|
429
|
-
if (typeof deps[depName] === 'string' &&
|
|
430
|
-
deps[depName] !== '*' &&
|
|
431
|
-
packageInfos[depName]) {
|
|
432
|
-
deps[depName] = '*';
|
|
433
|
-
changed = true;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
if (changed) {
|
|
438
|
-
writeFileSync(pkgInfo.packageJsonPath, JSON.stringify(pkgInfo.packageJson, null, 2));
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
export function runNpmPublishInReleaseOrder(releaseOrder, packageInfos) {
|
|
443
|
-
for (const packageName of releaseOrder) {
|
|
444
|
-
const pkgInfo = packageInfos[packageName];
|
|
445
|
-
const pkgDir = dirname(pkgInfo.packageJsonPath);
|
|
446
|
-
execSync('npm publish', { cwd: pkgDir, stdio: 'inherit' });
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
program.name(pkg.name).description(pkg.description).version(pkg.version);
|
|
450
|
-
program
|
|
451
|
-
.command('publish')
|
|
452
|
-
.description('Publish packages')
|
|
453
|
-
.option('--dry-run', 'Run the publish command without making changes')
|
|
454
|
-
.action((options) => {
|
|
455
|
-
const { packageInfos, releaseOrder } = validatePublish();
|
|
456
|
-
if (options.dryRun) {
|
|
457
|
-
console.log('Dry run: no publishing will be performed.');
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
try {
|
|
461
|
-
console.log('Replacing workspace dependencies "*" with actual versions...');
|
|
462
|
-
replaceWorkspaceDepsWithVersions(packageInfos);
|
|
463
|
-
console.log('Publishing packages in release order...');
|
|
464
|
-
runNpmPublishInReleaseOrder(releaseOrder, packageInfos);
|
|
465
|
-
console.log('Publish process completed.');
|
|
466
|
-
}
|
|
467
|
-
catch (error) {
|
|
468
|
-
console.error('Publishing failed:', error);
|
|
469
|
-
}
|
|
470
|
-
finally {
|
|
471
|
-
console.log('Restoring workspace dependencies back to "*" ...');
|
|
472
|
-
try {
|
|
473
|
-
restoreWorkspaceDepsToStar(packageInfos);
|
|
474
|
-
console.log('Restoration complete.');
|
|
475
|
-
}
|
|
476
|
-
catch (restoreError) {
|
|
477
|
-
console.error('Failed to restore workspace dependencies:', restoreError);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
program.parse(process.argv);
|