@servicetitan/startup 28.4.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 (47) hide show
  1. package/dist/cli/commands/get-command.d.ts +1 -3
  2. package/dist/cli/commands/get-command.d.ts.map +1 -1
  3. package/dist/cli/commands/get-command.js.map +1 -1
  4. package/dist/cli/commands/mfe-package-clean.d.ts +5 -1
  5. package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -1
  6. package/dist/cli/commands/mfe-package-clean.js +46 -36
  7. package/dist/cli/commands/mfe-package-clean.js.map +1 -1
  8. package/dist/cli/commands/mfe-package-publish.d.ts +1 -1
  9. package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -1
  10. package/dist/cli/commands/mfe-package-publish.js +3 -13
  11. package/dist/cli/commands/mfe-package-publish.js.map +1 -1
  12. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  13. package/dist/cli/commands/mfe-publish.js +6 -5
  14. package/dist/cli/commands/mfe-publish.js.map +1 -1
  15. package/dist/cli/commands/types.d.ts +1 -0
  16. package/dist/cli/commands/types.d.ts.map +1 -1
  17. package/dist/cli/index.js +1 -1
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/cli/utils/cli-npm.d.ts +10 -3
  20. package/dist/cli/utils/cli-npm.d.ts.map +1 -1
  21. package/dist/cli/utils/cli-npm.js +13 -15
  22. package/dist/cli/utils/cli-npm.js.map +1 -1
  23. package/dist/cli/utils/cli-os.d.ts.map +1 -1
  24. package/dist/cli/utils/maybe-create-git-folder.d.ts +2 -1
  25. package/dist/cli/utils/maybe-create-git-folder.d.ts.map +1 -1
  26. package/dist/cli/utils/maybe-create-git-folder.js +5 -1
  27. package/dist/cli/utils/maybe-create-git-folder.js.map +1 -1
  28. package/dist/cli/utils/publish.d.ts.map +1 -1
  29. package/dist/utils/get-jest-config.d.ts.map +1 -1
  30. package/dist/webpack/configs/plugins/ignore-plugin/check-resource.d.ts.map +1 -1
  31. package/dist/webpack/utils/hash-mod.d.ts.map +1 -1
  32. package/dist/webpack/utils/testing/execute.d.ts.map +1 -1
  33. package/dist/webpack/utils/testing/get-compiler.d.ts.map +1 -1
  34. package/package.json +20 -22
  35. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +143 -73
  36. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +27 -20
  37. package/src/cli/commands/__tests__/mfe-publish.test.ts +18 -7
  38. package/src/cli/commands/get-command.ts +1 -3
  39. package/src/cli/commands/mfe-package-clean.ts +53 -52
  40. package/src/cli/commands/mfe-package-publish.ts +7 -21
  41. package/src/cli/commands/mfe-publish.ts +6 -5
  42. package/src/cli/commands/types.ts +2 -0
  43. package/src/cli/index.ts +1 -1
  44. package/src/cli/utils/__tests__/cli-npm.test.ts +39 -26
  45. package/src/cli/utils/__tests__/maybe-create-git-folder.test.ts +23 -7
  46. package/src/cli/utils/cli-npm.ts +25 -16
  47. package/src/cli/utils/maybe-create-git-folder.ts +7 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/startup",
3
- "version": "28.4.0",
3
+ "version": "29.0.0",
4
4
  "description": "",
5
5
  "homepage": "https://docs.st.dev/docs/frontend/startup",
6
6
  "repository": {
@@ -28,35 +28,35 @@
28
28
  "@types/yargs": "~17.0.24"
29
29
  },
30
30
  "dependencies": {
31
- "@babel/preset-env": "~7.26.7",
31
+ "@babel/preset-env": "~7.26.9",
32
32
  "@jest/core": "~29.7.0",
33
33
  "@jest/types": "~29.6.3",
34
34
  "@jsdevtools/coverage-istanbul-loader": "^3.0.5",
35
- "@servicetitan/eslint-config": "28.4.0",
36
- "@servicetitan/stylelint-config": "28.4.0",
35
+ "@servicetitan/eslint-config": "29.0.0",
36
+ "@servicetitan/stylelint-config": "29.0.0",
37
37
  "@svgr/webpack": "^8.1.0",
38
38
  "@swc/core": "1.10.7",
39
39
  "@types/debug": "^4.1.12",
40
40
  "@types/jest": "~29.5.12",
41
41
  "chalk": "~4.1.2",
42
42
  "chokidar": "~4.0.3",
43
- "cpx2": "7.0.1",
43
+ "cpx2": "8.0.0",
44
44
  "css-loader": "~7.1.2",
45
- "css-minimizer-webpack-plugin": "^7.0.0",
45
+ "css-minimizer-webpack-plugin": "^7.0.2",
46
46
  "debug": "^4.4.0",
47
47
  "deepmerge": "~4.3.1",
48
- "esbuild-loader": "~4.2.2",
48
+ "esbuild-loader": "~4.3.0",
49
49
  "eslint": "~8.57.0",
50
50
  "execa": "~5.1.1",
51
51
  "fork-ts-checker-webpack-plugin": "~9.0.2",
52
- "glob": "~10.4.2",
52
+ "glob": "~11.0.1",
53
53
  "html-webpack-plugin": "~5.6.3",
54
54
  "identity-obj-proxy": "~3.0.0",
55
55
  "jest": "~29.7.0",
56
56
  "jest-circus": "~29.7.0",
57
57
  "jest-environment-jsdom": "^29.7.0",
58
58
  "jest-fetch-mock": "~3.0.3",
59
- "lerna": "~8.1.9",
59
+ "lerna": "~8.2.1",
60
60
  "less": "~4.2.2",
61
61
  "less-loader": "~12.2.0",
62
62
  "less-plugin-npm-import": "~2.1.0",
@@ -67,22 +67,22 @@
67
67
  "moment-locales-webpack-plugin": "~1.2.0",
68
68
  "multimatch": "~5.0.0",
69
69
  "portfinder": "~1.0.32",
70
- "postcss": "~8.5.1",
71
- "prettier": "~3.4.2",
72
- "sass": "~1.83.4",
73
- "sass-loader": "~16.0.4",
70
+ "postcss": "~8.5.3",
71
+ "prettier": "~3.5.3",
72
+ "sass": "~1.85.1",
73
+ "sass-loader": "~16.0.5",
74
74
  "source-map-loader": "~5.0.0",
75
75
  "style-loader": "~4.0.0",
76
- "stylelint": "~16.14.1",
76
+ "stylelint": "~16.16.0",
77
77
  "swc-loader": "0.2.6",
78
- "terser-webpack-plugin": "^5.3.11",
79
- "ts-jest": "29.2.5",
78
+ "terser-webpack-plugin": "^5.3.14",
79
+ "ts-jest": "29.2.6",
80
80
  "ts-loader": "~9.5.2",
81
81
  "ts-node": "~10.9.2",
82
82
  "typed-css-modules": "~0.9.1",
83
- "typescript": "5.7.3",
83
+ "typescript": "5.8.2",
84
84
  "username": "~5.1.0",
85
- "webpack": "~5.97.1",
85
+ "webpack": "~5.98.0",
86
86
  "webpack-assets-manifest": "~5.2.1",
87
87
  "webpack-bundle-analyzer": "^4.10.2",
88
88
  "webpack-dev-server": "~5.2.0",
@@ -93,14 +93,12 @@
93
93
  "xhr-mock": "~2.5.1",
94
94
  "yargs": "~17.7.2"
95
95
  },
96
- "DEVELOPER_NOTES": {
97
- "cpx2": "Must use 7.0.1 because later versions require glob 11.x which does not support Node 18"
98
- },
96
+ "DEVELOPER_NOTES": {},
99
97
  "publishConfig": {
100
98
  "access": "public"
101
99
  },
102
100
  "cli": {
103
101
  "webpack": false
104
102
  },
105
- "gitHead": "051b5b2f70ae6be345aa08ffdd49d8c049272940"
103
+ "gitHead": "67b2763ad7c21532ea47c920974f87e8de433d32"
106
104
  }
@@ -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
  });
@@ -14,9 +14,7 @@ import { PreparePackage } from './prepare-package';
14
14
  import { Start } from './start';
15
15
  import { StylesCheck } from './styles-check';
16
16
  import { Tests } from './tests';
17
- import { Command } from './types';
18
-
19
- type Newable<T> = new (...args: any[]) => T;
17
+ import { Command, Newable } from './types';
20
18
 
21
19
  export function getCommand(name: CommandName): Newable<Command> | undefined {
22
20
  switch (name) {