@servicetitan/startup 28.5.0 → 29.0.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.
Files changed (31) hide show
  1. package/dist/cli/commands/mfe-package-clean.d.ts +5 -1
  2. package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -1
  3. package/dist/cli/commands/mfe-package-clean.js +46 -36
  4. package/dist/cli/commands/mfe-package-clean.js.map +1 -1
  5. package/dist/cli/commands/mfe-package-publish.d.ts +1 -1
  6. package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -1
  7. package/dist/cli/commands/mfe-package-publish.js +3 -13
  8. package/dist/cli/commands/mfe-package-publish.js.map +1 -1
  9. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  10. package/dist/cli/commands/mfe-publish.js +6 -5
  11. package/dist/cli/commands/mfe-publish.js.map +1 -1
  12. package/dist/cli/utils/cli-npm.d.ts +10 -3
  13. package/dist/cli/utils/cli-npm.d.ts.map +1 -1
  14. package/dist/cli/utils/cli-npm.js +13 -15
  15. package/dist/cli/utils/cli-npm.js.map +1 -1
  16. package/dist/cli/utils/cli-os.d.ts.map +1 -1
  17. package/dist/cli/utils/publish.d.ts.map +1 -1
  18. package/dist/utils/get-jest-config.d.ts.map +1 -1
  19. package/dist/webpack/configs/plugins/ignore-plugin/check-resource.d.ts.map +1 -1
  20. package/dist/webpack/utils/hash-mod.d.ts.map +1 -1
  21. package/dist/webpack/utils/testing/execute.d.ts.map +1 -1
  22. package/dist/webpack/utils/testing/get-compiler.d.ts.map +1 -1
  23. package/package.json +19 -21
  24. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +143 -73
  25. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +27 -20
  26. package/src/cli/commands/__tests__/mfe-publish.test.ts +18 -7
  27. package/src/cli/commands/mfe-package-clean.ts +53 -52
  28. package/src/cli/commands/mfe-package-publish.ts +7 -21
  29. package/src/cli/commands/mfe-publish.ts +6 -5
  30. package/src/cli/utils/__tests__/cli-npm.test.ts +39 -26
  31. package/src/cli/utils/cli-npm.ts +25 -16
@@ -1,7 +1,7 @@
1
1
  import { fs, vol } from 'memfs';
2
2
  import { isWebComponent, log } from '../../../utils';
3
3
  import { gitGetBranch } from '../../utils/cli-git';
4
- import { npmGetPackageVersionDates, npmUnpublish } from '../../utils/cli-npm';
4
+ import { Version, npmGetPackageVersionsDetails, npmUnpublish } from '../../utils/cli-npm';
5
5
 
6
6
  import { MFEPackageClean } from '../mfe-package-clean';
7
7
 
@@ -12,7 +12,7 @@ jest.mock('../../../utils', () => ({
12
12
  log: { error: jest.fn(), info: jest.fn() },
13
13
  }));
14
14
  jest.mock('../../utils/cli-npm', () => ({
15
- npmGetPackageVersionDates: jest.fn(),
15
+ npmGetPackageVersionsDetails: jest.fn(),
16
16
  npmUnpublish: jest.fn(),
17
17
  }));
18
18
  jest.mock('../../utils/cli-git', () => ({
@@ -24,26 +24,26 @@ const DEFAULT_VERSIONS_TO_KEEP = 5;
24
24
  describe(`[startup] ${MFEPackageClean.name}`, () => {
25
25
  const registry = 'https://verdaccio.servicetitan.com';
26
26
  const packageName = '@servicetitan/foo';
27
+ let gitBranch: string;
27
28
  let args: ConstructorParameters<typeof MFEPackageClean>[0];
28
- let versions: Record<string, Date>;
29
+ let versions: Version[];
29
30
 
30
31
  function getBranchVersions(branchName: string, length: number) {
31
- return Object.fromEntries(
32
- Array.from({ length }).map((_, index) => [
33
- `0.0.0-${branchName}.${(0xff000000 + index).toString(16)}`,
34
- dayAgo(index + 1), // tests assume versions are in reverse chronological order
35
- ])
36
- );
32
+ return Array.from({ length }).map((_, index) => ({
33
+ name: `0.0.0-${branchName}.${(0xff000000 + index).toString(16)}`,
34
+ date: dayAgo(index + 1), // tests assume versions are in reverse chronological order
35
+ }));
37
36
  }
38
37
 
39
38
  beforeEach(() => {
40
39
  args = {};
41
- versions = getBranchVersions('master', 10);
40
+ gitBranch = 'master';
41
+ versions = getBranchVersions(gitBranch, 10);
42
42
 
43
43
  jest.resetAllMocks();
44
44
  jest.mocked(isWebComponent).mockReturnValue(true);
45
- jest.mocked(npmGetPackageVersionDates).mockImplementation(() => Object.entries(versions));
46
- jest.mocked(gitGetBranch).mockImplementation(() => 'current');
45
+ jest.mocked(npmGetPackageVersionsDetails).mockImplementation(() => versions);
46
+ jest.mocked(gitGetBranch).mockImplementation(() => gitBranch);
47
47
  vol.fromJSON({ 'package.json': JSON.stringify({ name: packageName }) });
48
48
  });
49
49
 
@@ -54,69 +54,91 @@ describe(`[startup] ${MFEPackageClean.name}`, () => {
54
54
  test('fetches package info from registry', async () => {
55
55
  await subject();
56
56
 
57
- expect(npmGetPackageVersionDates).toHaveBeenCalledWith(registry, packageName);
57
+ expect(npmGetPackageVersionsDetails).toHaveBeenCalledWith(registry, packageName);
58
58
  });
59
59
 
60
- function itLogsAndUnpublishedOldestPackages({ branch = 'master' }: { branch?: string } = {}) {
61
- test('logs and unpublishes oldest packages', async () => {
62
- const oldest = Object.entries(versions).slice(DEFAULT_VERSIONS_TO_KEEP);
60
+ function itLogsAndUnpublishedOldestPackages({
61
+ oldest,
62
+ title,
63
+ }: { branch?: string; oldest?: () => Record<string, Version[]>; title?: string } = {}) {
64
+ test(title ?? `logs and unpublishes oldest packages`, async () => {
65
+ const expected = oldest?.() ?? {
66
+ [gitBranch]: versions.slice(DEFAULT_VERSIONS_TO_KEEP),
67
+ };
63
68
 
64
69
  await subject();
65
70
 
66
71
  expect(log.info).toHaveBeenCalledWith(
67
72
  'found versions for unpublish:',
68
- JSON.stringify({ [branch]: oldest }, null, 4)
73
+ JSON.stringify(expected, null, 2)
69
74
  );
70
- oldest.forEach(([version]) => {
71
- expect(npmUnpublish).toHaveBeenCalledWith(registry, packageName, version);
72
- });
75
+
76
+ Object.values(expected)
77
+ .flat()
78
+ .forEach(({ name }) => {
79
+ expect(npmUnpublish).toHaveBeenCalledWith(registry, packageName, name);
80
+ });
73
81
  });
74
82
  }
75
83
 
76
- itLogsAndUnpublishedOldestPackages();
77
-
78
- describe.each(['dev', 'develop', 'next'])('when branch is %s', branch => {
84
+ describe.each(['dev', 'develop', 'master', 'next'])('when branch is %s', branch => {
79
85
  beforeEach(() => {
80
- versions = transform(versions, (v: string) => v.replace('master', branch));
86
+ versions = transform(versions, name => name.replace(gitBranch, branch));
87
+ gitBranch = branch;
81
88
  });
82
89
 
83
- itLogsAndUnpublishedOldestPackages({ branch });
90
+ itLogsAndUnpublishedOldestPackages();
84
91
  });
85
92
 
86
- describe.each(['qa', true])('when selected branch is %s', (branch: string | true) => {
87
- let branchedVersions: typeof versions = {};
88
- const branchName = branch === true ? 'current' : branch;
93
+ describe('with versions from multiple branches', () => {
94
+ const otherBranch = 'next';
89
95
 
90
- beforeEach(() => {
91
- args.branch = branch;
96
+ beforeEach(() => (versions = [...versions, ...getBranchVersions(otherBranch, 7)]));
92
97
 
93
- branchedVersions = getBranchVersions(branchName, 7);
94
-
95
- versions = {
96
- ...versions,
97
- ...branchedVersions,
98
- };
98
+ itLogsAndUnpublishedOldestPackages({
99
+ oldest: () => ({
100
+ [gitBranch]: versions
101
+ .filter(({ name }) => name.includes(gitBranch))
102
+ .slice(DEFAULT_VERSIONS_TO_KEEP),
103
+ }),
104
+ title: 'log and unpublishes oldest versions from the current branch',
99
105
  });
100
106
 
101
- test(`logs and unpublishes oldest packages for ${branchName} branch`, async () => {
102
- const oldest = Object.entries(branchedVersions).slice(DEFAULT_VERSIONS_TO_KEEP);
103
-
104
- await subject();
107
+ describe('with --branch argument', () => {
108
+ beforeEach(() => (args.branch = otherBranch));
109
+
110
+ itLogsAndUnpublishedOldestPackages({
111
+ branch: otherBranch,
112
+ oldest: () => ({
113
+ [otherBranch]: versions
114
+ .filter(({ name }) => name.includes(otherBranch))
115
+ .slice(DEFAULT_VERSIONS_TO_KEEP),
116
+ }),
117
+ title: 'logs and unpublishes oldest versions from the specified branch',
118
+ });
119
+ });
105
120
 
106
- expect(log.info).toHaveBeenCalledWith(
107
- 'found versions for unpublish:',
108
- JSON.stringify({ [branchName]: oldest }, null, 4)
109
- );
110
- oldest.forEach(([version]) => {
111
- expect(npmUnpublish).toHaveBeenCalledWith(registry, packageName, version);
121
+ describe('with --all argument', () => {
122
+ beforeEach(() => (args.all = true));
123
+
124
+ itLogsAndUnpublishedOldestPackages({
125
+ oldest: () => ({
126
+ [gitBranch]: versions
127
+ .filter(({ name }) => name.includes(gitBranch))
128
+ .slice(DEFAULT_VERSIONS_TO_KEEP),
129
+ [otherBranch]: versions
130
+ .filter(({ name }) => name.includes(otherBranch))
131
+ .slice(DEFAULT_VERSIONS_TO_KEEP),
132
+ }),
133
+ title: 'logs and unpublishes oldest versions from all branches',
112
134
  });
113
135
  });
114
136
  });
115
137
 
116
138
  describe('with master versions generated by nerdbank versioning', () => {
117
139
  beforeEach(() => {
118
- versions = transform(versions, (v: string, index: number) =>
119
- v.replace(/master.*/, `${index}.0.0`)
140
+ versions = transform(versions, (name, index) =>
141
+ name.replace(/master.*/, `${index}.0.0`)
120
142
  );
121
143
  });
122
144
 
@@ -125,26 +147,78 @@ describe(`[startup] ${MFEPackageClean.name}`, () => {
125
147
 
126
148
  describe('with branch versions generated by nerdbank versioning', () => {
127
149
  beforeEach(() => {
128
- versions = transform(versions, (v: string, index: number) =>
129
- v.replace(/master.*/, `${index}.0.0-next-${index}`)
150
+ gitBranch = 'next';
151
+ versions = transform(versions, (name, index) =>
152
+ name.replace(/master.*/, `${index}.0.0-next-${index}`)
130
153
  );
131
154
  });
132
155
 
133
- itLogsAndUnpublishedOldestPackages({ branch: 'next' });
156
+ itLogsAndUnpublishedOldestPackages();
134
157
  });
135
158
 
136
- describe('with "count" argument', () => {
137
- beforeEach(() => (args.count = Object.keys(versions).length - 1));
159
+ describe('with --count argument', () => {
160
+ beforeEach(() => (args.count = versions.length - 1));
138
161
 
139
162
  test('keeps the specified number of versions', async () => {
140
163
  await subject();
141
164
 
165
+ const oldest = versions.slice(args.count);
166
+
142
167
  expect(log.info).toHaveBeenCalledWith(
143
168
  'found versions for unpublish:',
144
- JSON.stringify({ master: Object.entries(versions).slice(-1) }, null, 4)
169
+ JSON.stringify({ master: oldest }, null, 2)
170
+ );
171
+
172
+ expect(npmUnpublish).toHaveBeenCalledTimes(oldest.length);
173
+ });
174
+ });
175
+
176
+ describe('with --registry argument', () => {
177
+ beforeEach(() => (args.registry = 'https://foo.com'));
178
+
179
+ test('fetches package info from specified registry', async () => {
180
+ await subject();
181
+
182
+ expect(npmGetPackageVersionsDetails).toHaveBeenCalledWith(args.registry, packageName);
183
+ });
184
+
185
+ test('unpublishes from the specified registry', async () => {
186
+ await subject();
187
+
188
+ expect(npmUnpublish).toHaveBeenCalledWith(
189
+ args.registry,
190
+ packageName,
191
+ expect.any(String)
145
192
  );
193
+ });
194
+ });
195
+
196
+ describe('with --dry argument', () => {
197
+ beforeEach(() => (args.dry = true));
198
+
199
+ test('does not unpublish packages', async () => {
200
+ await subject();
201
+
202
+ expect(npmUnpublish).not.toHaveBeenCalled();
203
+ });
204
+ });
146
205
 
147
- expect(npmUnpublish).toHaveBeenCalledTimes(1);
206
+ describe('when version has a tag', () => {
207
+ const tag = 'prod';
208
+ let tagged: Version;
209
+
210
+ beforeEach(() => {
211
+ tagged = versions[DEFAULT_VERSIONS_TO_KEEP];
212
+ tagged.tag = tag;
213
+ });
214
+
215
+ test('does not unpublish tagged version', async () => {
216
+ await subject();
217
+
218
+ expect(npmUnpublish).toHaveBeenCalledTimes(DEFAULT_VERSIONS_TO_KEEP - 1);
219
+ expect(log.info).toHaveBeenCalledWith(
220
+ `ignoring version ${tagged.name} tagged "${tag}"`
221
+ );
148
222
  });
149
223
  });
150
224
 
@@ -158,9 +232,9 @@ describe(`[startup] ${MFEPackageClean.name}`, () => {
158
232
  test('logs error', async () => {
159
233
  await subject();
160
234
 
161
- const firstUnpublishVersion = Object.entries(versions)[DEFAULT_VERSIONS_TO_KEEP][0];
235
+ const firstUnpublishVersion = versions[DEFAULT_VERSIONS_TO_KEEP];
162
236
  expect(log.error).toHaveBeenCalledWith(
163
- `error while removing ${packageName} version ${firstUnpublishVersion}`
237
+ `error while removing ${packageName} version ${firstUnpublishVersion.name}`
164
238
  );
165
239
  });
166
240
  });
@@ -175,30 +249,31 @@ describe(`[startup] ${MFEPackageClean.name}`, () => {
175
249
  }
176
250
 
177
251
  describe('when version does not start with "0.0.0-"', () => {
178
- beforeEach(() => {
179
- versions = transform(versions, (v: string) => v.replace('0.0.0-', ''));
180
- });
252
+ beforeEach(() => (versions = transform(versions, name => name.replace('0.0.0-', ''))));
181
253
 
182
254
  itDoesNotUnpublish();
183
255
  });
184
256
 
185
257
  describe('when branch is not recognized', () => {
186
- beforeEach(() => {
187
- versions = transform(versions, (v: string) => v.replace('master', 'foo'));
188
- });
258
+ beforeEach(() => (versions = transform(versions, name => name.replace(gitBranch, 'foo'))));
189
259
 
190
260
  itDoesNotUnpublish();
191
261
  });
192
262
 
193
263
  describe('when version is not recognized', () => {
194
- const unknownVersions = ['0.0.0-master-1^', '0.0.0-master-2^'];
264
+ const unknownVersions = [
265
+ { name: '0.0.0-master-1^', date: new Date() },
266
+ { name: '0.0.0-master-2^', date: new Date() },
267
+ ];
195
268
 
196
- beforeEach(() => unknownVersions.forEach(version => (versions[version] = new Date())));
269
+ beforeEach(() => (versions = [...versions, ...unknownVersions]));
197
270
 
198
271
  test('logs unrecognized versions', async () => {
199
272
  await subject();
200
273
 
201
- expect(log.info).toHaveBeenCalledWith('unknown versions:', unknownVersions.join());
274
+ unknownVersions.forEach(({ name }) => {
275
+ expect(log.info).toHaveBeenCalledWith(`skipping unrecognized version: ${name}`);
276
+ });
202
277
  });
203
278
  });
204
279
 
@@ -223,11 +298,6 @@ function dayAgo(count: number) {
223
298
  return date;
224
299
  }
225
300
 
226
- function transform(
227
- versions: Record<string, Date>,
228
- callback: (version: string, index: number) => string
229
- ) {
230
- return Object.fromEntries(
231
- Object.entries(versions).map(([version, date], index) => [callback(version, index), date])
232
- );
301
+ function transform(versions: Version[], callback: (name: string, index: number) => string) {
302
+ return versions.map((v, index) => ({ ...v, name: callback(v.name, index) }));
233
303
  }
@@ -6,7 +6,6 @@ import {
6
6
  npmGetPackageVersions,
7
7
  npmPackageSet,
8
8
  npmPublish,
9
- npmPublishDry,
10
9
  npmTagVersion,
11
10
  } from '../../utils/cli-npm';
12
11
  import { MFEPackagePublish } from '../mfe-package-publish';
@@ -25,7 +24,6 @@ jest.mock('../../utils/cli-npm', () => ({
25
24
  npmGetPackageVersions: jest.fn(),
26
25
  npmPackageSet: jest.fn(),
27
26
  npmPublish: jest.fn(),
28
- npmPublishDry: jest.fn(),
29
27
  npmTagVersion: jest.fn(),
30
28
  }));
31
29
 
@@ -108,7 +106,7 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
108
106
  test('publishes package and logs message', async () => {
109
107
  await subject();
110
108
 
111
- expect(npmPublish).toHaveBeenCalledWith('prod');
109
+ expect(npmPublish).toHaveBeenCalled();
112
110
  expect(log.info).toHaveBeenCalledWith(
113
111
  expect.stringContaining(`published ${packageName} version ${defaultPackageVersion()}`)
114
112
  );
@@ -119,10 +117,20 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
119
117
  describe.each(branches)('when branch is %s', branch => {
120
118
  beforeEach(() => (args.branch = branch));
121
119
 
122
- test(`uses tag ${tag}`, async () => {
120
+ test(`uses tag "${tag}"`, async () => {
123
121
  await subject();
124
122
 
125
- expect(npmPublish).toHaveBeenCalledWith(tag);
123
+ expect(npmPublish).toHaveBeenCalledWith({ dry: false, tag });
124
+ });
125
+
126
+ describe('with "dry" argument', () => {
127
+ beforeEach(() => (args.dry = true));
128
+
129
+ test(`does dry run with tag "${tag}"`, async () => {
130
+ await subject();
131
+
132
+ expect(npmPublish).toHaveBeenCalledWith({ dry: true, tag });
133
+ });
126
134
  });
127
135
  });
128
136
  }
@@ -134,17 +142,7 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
134
142
  test('uses specified tag', async () => {
135
143
  await subject();
136
144
 
137
- expect(npmPublish).toHaveBeenCalledWith(args.tag);
138
- });
139
- });
140
-
141
- describe('when "tag" is false', () => {
142
- beforeEach(() => (args.tag = false));
143
-
144
- test('omits the tag', async () => {
145
- await subject();
146
-
147
- expect(npmPublish).toHaveBeenCalledWith('');
145
+ expect(npmPublish).toHaveBeenCalledWith({ dry: false, tag: args.tag });
148
146
  });
149
147
  });
150
148
 
@@ -161,10 +159,9 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
161
159
  describe('with "dry" argument', () => {
162
160
  beforeEach(() => (args.dry = true));
163
161
 
164
- test('does dry run and logs message', async () => {
162
+ test('logs message', async () => {
165
163
  await subject();
166
164
 
167
- expect(npmPublishDry).toHaveBeenCalled();
168
165
  expect(log.info).toHaveBeenCalledWith(
169
166
  expect.stringContaining(
170
167
  `(dry-run) published ${packageName} version ${defaultPackageVersion()}`
@@ -263,10 +260,20 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
263
260
  describe('with "force" argument"', () => {
264
261
  beforeEach(() => (args.force = true));
265
262
 
266
- test('publishes the package', async () => {
263
+ test('publishes the package with tag "latest"', async () => {
267
264
  await subject();
268
265
 
269
- expect(npmPublish).toHaveBeenCalledWith('');
266
+ expect(npmPublish).toHaveBeenCalledWith({ dry: false, tag: 'latest' });
267
+ });
268
+
269
+ describe('with "dry" argument', () => {
270
+ beforeEach(() => (args.dry = true));
271
+
272
+ test(`does dry run with tag "latest"`, async () => {
273
+ await subject();
274
+
275
+ expect(npmPublish).toHaveBeenCalledWith({ dry: true, tag: 'latest' });
276
+ });
270
277
  });
271
278
  });
272
279
  });
@@ -96,10 +96,13 @@ describe(`[startup] ${MFEPublish.name}`, () => {
96
96
  });
97
97
 
98
98
  type ArgumentName = keyof typeof args;
99
- const testArgs: { name: ArgumentName; value: any; expected?: string }[] = [
100
- { name: 'count', value: 10 },
99
+ const testArgs: { name: ArgumentName; value: any; expected?: string | string[] }[] = [
100
+ { name: 'all', value: true, expected: '--all' },
101
101
  { name: 'branch', value: 'foo-123' },
102
- { name: 'branch', value: true, expected: '--branch' },
102
+ { name: 'branch', value: true, expected: [] }, // check ignores obsolete --branch with no name
103
+ { name: 'count', value: 10 },
104
+ { name: 'dry', value: true, expected: '--dry' },
105
+ { name: 'registry', value: 'https://foo' },
103
106
  ];
104
107
 
105
108
  describe.each(testArgs)('with "{$name: $value}"', ({ name, value, expected }) => {
@@ -109,18 +112,22 @@ describe(`[startup] ${MFEPublish.name}`, () => {
109
112
  await subject();
110
113
 
111
114
  expect(lernaExec).toHaveBeenCalledWith(
112
- expect.objectContaining({ '--': [expected ?? `--${name} ${value}`] })
115
+ expect.objectContaining({
116
+ '--': Array.isArray(expected)
117
+ ? expected
118
+ : [expected ?? `--${name} ${value}`],
119
+ })
113
120
  );
114
121
  });
115
122
  });
116
123
  });
117
124
 
118
125
  type ArgumentName = keyof typeof args;
119
- const testArgs: { name: ArgumentName; value: any; expected?: string }[] = [
126
+ const testArgs: { name: ArgumentName; value: any; expected?: string | string[] }[] = [
120
127
  { name: 'build', value: true },
121
128
  { name: 'branch', value: 'foo-123' },
122
129
  { name: 'tag', value: 'foo' },
123
- { name: 'tag', value: false, expected: '--no-tag' },
130
+ { name: 'tag', value: true, expected: [] }, // check ignores obsolete --tag with no name
124
131
  { name: 'dry', value: true, expected: '--dry' },
125
132
  { name: 'force', value: true, expected: '--force' },
126
133
  { name: 'registry', value: 'https://foo' },
@@ -133,7 +140,11 @@ describe(`[startup] ${MFEPublish.name}`, () => {
133
140
  await subject();
134
141
 
135
142
  expect(lernaExec).toHaveBeenCalledWith(
136
- expect.objectContaining({ '--': [expected ?? `--${name} ${value}`] })
143
+ expect.objectContaining({
144
+ '--': Array.isArray(expected)
145
+ ? expected
146
+ : [expected ?? `--${name} ${value}`],
147
+ })
137
148
  );
138
149
  });
139
150
  });
@@ -1,12 +1,15 @@
1
1
  import { isWebComponent, log, logErrors, readJson } from '../../utils';
2
- import { gitGetBranch } from '../utils/cli-git';
3
- import { npmGetPackageVersionDates, npmUnpublish } from '../utils/cli-npm';
4
2
  import { getBranchesConfigs } from '../../utils/get-branch-configs';
3
+ import { gitGetBranch } from '../utils/cli-git';
4
+ import { Version, npmGetPackageVersionsDetails, npmUnpublish } from '../utils/cli-npm';
5
5
  import { Command } from './types';
6
6
 
7
7
  export interface ArgsPackageClean {
8
- branch?: string | true;
8
+ all?: true;
9
+ branch?: string;
9
10
  count?: number;
11
+ dry?: boolean;
12
+ registry?: string;
10
13
  }
11
14
 
12
15
  export class MFEPackageClean implements Command {
@@ -23,37 +26,34 @@ export class MFEPackageClean implements Command {
23
26
  }
24
27
 
25
28
  const data = this.getCleanData();
26
- const packageJson = readJson('package.json');
27
- const packageName = packageJson.name;
29
+ const packageName = readJson('package.json').name;
28
30
  const branchedVersions = this.getBranchedVersions(packageName, data.registry);
29
31
 
30
- log.info(
31
- `branched versions (${data.count}):`,
32
- JSON.stringify(branchedVersions, undefined, 4)
33
- );
34
-
35
- const branchedVersionsToClean: Record<string, [string, Date][]> = {};
32
+ log.info(`branched versions (${data.count}):`, JSON.stringify(branchedVersions, null, 2));
36
33
 
37
- for (const branch of Object.keys(branchedVersions)) {
38
- // limit branches for now
39
- if (!branchedVersions[branch] || !data.branches.includes(branch)) {
34
+ const branchedVersionsToClean: Record<string, Version[]> = {};
35
+ for (const [branch, versions] of Object.entries(branchedVersions)) {
36
+ // Limit branches for now
37
+ if (!data.branches.includes(branch)) {
38
+ log.info(`ignoring unrecognized branch "${branch}"`);
40
39
  continue;
41
40
  }
42
41
 
43
- branchedVersions[branch].sort(([, adt], [, bdt]) => (adt > bdt ? -1 : 1));
44
- branchedVersionsToClean[branch] = branchedVersions[branch].slice(data.count);
42
+ branchedVersionsToClean[branch] = this.excludeTagged(versions)
43
+ .sort(({ date: adt }, { date: bdt }) => (adt > bdt ? -1 : 1))
44
+ .slice(data.count);
45
45
  }
46
46
 
47
- log.info(
48
- 'found versions for unpublish:',
49
- JSON.stringify(branchedVersionsToClean, undefined, 4)
50
- );
51
-
47
+ log.info('found versions for unpublish:', JSON.stringify(branchedVersionsToClean, null, 2));
52
48
  const unVersions = Object.keys(branchedVersionsToClean).reduce(
53
- (out, br) => [...out, ...branchedVersionsToClean[br].map(([v]) => v)],
49
+ (out, br) => [...out, ...branchedVersionsToClean[br].map(({ name }) => name)],
54
50
  []
55
51
  );
56
52
 
53
+ if (this.args.dry) {
54
+ return;
55
+ }
56
+
57
57
  for (const version of unVersions) {
58
58
  try {
59
59
  // eslint-disable-next-line no-await-in-loop
@@ -64,6 +64,16 @@ export class MFEPackageClean implements Command {
64
64
  }
65
65
  }
66
66
 
67
+ private excludeTagged(versions: Version[]) {
68
+ return versions.filter(({ name, tag }) => {
69
+ if (tag) {
70
+ log.info(`ignoring version ${name} tagged "${tag}"`);
71
+ return false;
72
+ }
73
+ return true;
74
+ });
75
+ }
76
+
67
77
  private getCleanData(): {
68
78
  count: number;
69
79
  registry: string;
@@ -76,66 +86,57 @@ export class MFEPackageClean implements Command {
76
86
 
77
87
  let branches: string[];
78
88
 
79
- if (this.args.branch === true) {
80
- branches = [gitGetBranch()];
89
+ if (this.args.all === true) {
90
+ branches = Object.keys(getBranchesConfigs());
81
91
  } else if (typeof this.args.branch === 'string') {
82
92
  branches = [this.args.branch];
83
93
  } else {
84
- branches = Object.keys(getBranchesConfigs());
94
+ branches = [gitGetBranch()];
85
95
  }
86
96
 
87
- const registry = 'https://verdaccio.servicetitan.com';
97
+ const registry = this.args.registry ?? 'https://verdaccio.servicetitan.com';
88
98
 
89
99
  return { count, registry, branches };
90
100
  }
91
101
 
92
102
  private getBranchedVersions(packageName: string, registry: string) {
93
- const versions = npmGetPackageVersionDates(registry, packageName);
94
- const branchedVersions: Record<string, [string, Date][]> = {};
95
- const unknownVersions: string[] = [];
96
-
97
- const addVersion = (branch: string, version: string, dt: Date) => {
98
- if (!branchedVersions[branch]) {
99
- branchedVersions[branch] = [];
100
- }
103
+ const versions = npmGetPackageVersionsDetails(registry, packageName);
104
+ const branchedVersions: Record<string, Version[]> = {};
101
105
 
102
- branchedVersions[branch].push([version, dt]);
103
- };
106
+ function addVersion(branch: string, version: Version) {
107
+ branchedVersions[branch] ??= [];
108
+ branchedVersions[branch].push(version);
109
+ }
104
110
 
105
- for (const [version, dt] of versions) {
106
- if (!version.startsWith('0.0.0-')) {
111
+ for (const version of versions) {
112
+ const { name } = version;
113
+ if (!name.startsWith('0.0.0-')) {
107
114
  continue;
108
115
  }
109
116
 
110
- const buildVersion = version.replace('0.0.0-', '');
117
+ const buildVersion = name.replace('0.0.0-', '');
111
118
 
119
+ // master version generated by nerdbank versioning
112
120
  if (/^(\d+)\.(\d+)\.(\d+)$/.test(buildVersion)) {
113
- // master version generated by nerdbank versioning
114
- addVersion('master', version, dt);
121
+ addVersion('master', version);
115
122
  continue;
116
123
  }
117
124
 
125
+ // branch version generated by nerdbank versioning
118
126
  const match1 = buildVersion.match(/^(\d+)\.(\d+)\.(\d+)-([\dA-Za-z-]+).([\dA-Za-z]+)$/);
119
-
120
127
  if (match1?.length) {
121
- // branch version generated by nerdbank versioning
122
- addVersion(match1[4], version, dt);
128
+ addVersion(match1[4], version);
123
129
  continue;
124
130
  }
125
131
 
132
+ // branch version generated by mfe-publisher versioning
126
133
  const match2 = buildVersion.match(/^([\dA-Za-z-]+).([\dA-Za-z]+)$/);
127
-
128
134
  if (match2?.length) {
129
- // branch version generated by mfe-publisher versioning
130
- addVersion(match2[1], version, dt);
135
+ addVersion(match2[1], version);
131
136
  continue;
132
137
  }
133
138
 
134
- unknownVersions.push(version);
135
- }
136
-
137
- if (unknownVersions.length) {
138
- log.info('unknown versions:', unknownVersions.join());
139
+ log.info(`skipping unrecognized version: ${name}`);
139
140
  }
140
141
 
141
142
  return branchedVersions;