@servicetitan/startup 32.1.0 → 32.3.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 (253) hide show
  1. package/dist/cli/commands/build.d.ts +36 -7
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +35 -10
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/bundle-package.d.ts +9 -10
  6. package/dist/cli/commands/bundle-package.d.ts.map +1 -1
  7. package/dist/cli/commands/bundle-package.js +6 -22
  8. package/dist/cli/commands/bundle-package.js.map +1 -1
  9. package/dist/cli/commands/clean.d.ts +2 -2
  10. package/dist/cli/commands/clean.d.ts.map +1 -1
  11. package/dist/cli/commands/clean.js +16 -4
  12. package/dist/cli/commands/clean.js.map +1 -1
  13. package/dist/cli/commands/convert-eslint-config.d.ts +2 -2
  14. package/dist/cli/commands/convert-eslint-config.d.ts.map +1 -1
  15. package/dist/cli/commands/convert-eslint-config.js +3 -4
  16. package/dist/cli/commands/convert-eslint-config.js.map +1 -1
  17. package/dist/cli/commands/eslint.d.ts +3 -5
  18. package/dist/cli/commands/eslint.d.ts.map +1 -1
  19. package/dist/cli/commands/eslint.js +2 -18
  20. package/dist/cli/commands/eslint.js.map +1 -1
  21. package/dist/cli/commands/get-command.d.ts.map +1 -1
  22. package/dist/cli/commands/get-command.js +2 -0
  23. package/dist/cli/commands/get-command.js.map +1 -1
  24. package/dist/cli/commands/index.d.ts +0 -1
  25. package/dist/cli/commands/index.d.ts.map +1 -1
  26. package/dist/cli/commands/index.js +0 -1
  27. package/dist/cli/commands/index.js.map +1 -1
  28. package/dist/cli/commands/init.d.ts +10 -6
  29. package/dist/cli/commands/init.d.ts.map +1 -1
  30. package/dist/cli/commands/init.js +9 -8
  31. package/dist/cli/commands/init.js.map +1 -1
  32. package/dist/cli/commands/install.d.ts +21 -7
  33. package/dist/cli/commands/install.d.ts.map +1 -1
  34. package/dist/cli/commands/install.js +33 -11
  35. package/dist/cli/commands/install.js.map +1 -1
  36. package/dist/cli/commands/kendo-ui-license.d.ts +3 -3
  37. package/dist/cli/commands/kendo-ui-license.d.ts.map +1 -1
  38. package/dist/cli/commands/kendo-ui-license.js +17 -5
  39. package/dist/cli/commands/kendo-ui-license.js.map +1 -1
  40. package/dist/cli/commands/lint.d.ts +31 -12
  41. package/dist/cli/commands/lint.d.ts.map +1 -1
  42. package/dist/cli/commands/lint.js +34 -13
  43. package/dist/cli/commands/lint.js.map +1 -1
  44. package/dist/cli/commands/mfe-list.d.ts +46 -0
  45. package/dist/cli/commands/mfe-list.d.ts.map +1 -0
  46. package/dist/cli/commands/mfe-list.js +200 -0
  47. package/dist/cli/commands/mfe-list.js.map +1 -0
  48. package/dist/cli/commands/mfe-package-clean.d.ts +29 -5
  49. package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -1
  50. package/dist/cli/commands/mfe-package-clean.js +28 -5
  51. package/dist/cli/commands/mfe-package-clean.js.map +1 -1
  52. package/dist/cli/commands/mfe-package-publish.d.ts +37 -5
  53. package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -1
  54. package/dist/cli/commands/mfe-package-publish.js +36 -5
  55. package/dist/cli/commands/mfe-package-publish.js.map +1 -1
  56. package/dist/cli/commands/mfe-publish.d.ts +17 -4
  57. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  58. package/dist/cli/commands/mfe-publish.js +37 -7
  59. package/dist/cli/commands/mfe-publish.js.map +1 -1
  60. package/dist/cli/commands/prepare-package.d.ts +3 -5
  61. package/dist/cli/commands/prepare-package.d.ts.map +1 -1
  62. package/dist/cli/commands/prepare-package.js +2 -18
  63. package/dist/cli/commands/prepare-package.js.map +1 -1
  64. package/dist/cli/commands/review/review.d.ts +14 -6
  65. package/dist/cli/commands/review/review.d.ts.map +1 -1
  66. package/dist/cli/commands/review/review.js +27 -23
  67. package/dist/cli/commands/review/review.js.map +1 -1
  68. package/dist/cli/commands/run-task.d.ts +10 -6
  69. package/dist/cli/commands/run-task.d.ts.map +1 -1
  70. package/dist/cli/commands/run-task.js +16 -13
  71. package/dist/cli/commands/run-task.js.map +1 -1
  72. package/dist/cli/commands/start.d.ts +28 -7
  73. package/dist/cli/commands/start.d.ts.map +1 -1
  74. package/dist/cli/commands/start.js +27 -10
  75. package/dist/cli/commands/start.js.map +1 -1
  76. package/dist/cli/commands/styles-check.d.ts +2 -2
  77. package/dist/cli/commands/styles-check.d.ts.map +1 -1
  78. package/dist/cli/commands/styles-check.js +2 -1
  79. package/dist/cli/commands/styles-check.js.map +1 -1
  80. package/dist/cli/commands/test/runners/vitest.js +2 -1
  81. package/dist/cli/commands/test/runners/vitest.js.map +1 -1
  82. package/dist/cli/commands/test/tests.d.ts +14 -5
  83. package/dist/cli/commands/test/tests.d.ts.map +1 -1
  84. package/dist/cli/commands/test/tests.js +26 -13
  85. package/dist/cli/commands/test/tests.js.map +1 -1
  86. package/dist/cli/commands/types.d.ts +13 -4
  87. package/dist/cli/commands/types.d.ts.map +1 -1
  88. package/dist/cli/commands/types.js +28 -0
  89. package/dist/cli/commands/types.js.map +1 -1
  90. package/dist/cli/commands/upload-sourcemaps.d.ts +3 -5
  91. package/dist/cli/commands/upload-sourcemaps.d.ts.map +1 -1
  92. package/dist/cli/commands/upload-sourcemaps.js +5 -20
  93. package/dist/cli/commands/upload-sourcemaps.js.map +1 -1
  94. package/dist/cli/index.js +48 -39
  95. package/dist/cli/index.js.map +1 -1
  96. package/dist/cli/tasks/swc-compile-package.d.ts +2 -2
  97. package/dist/cli/tasks/swc-compile-package.d.ts.map +1 -1
  98. package/dist/cli/tasks/swc-compile-package.js.map +1 -1
  99. package/dist/cli/tasks/tsc-compile-package.d.ts +2 -2
  100. package/dist/cli/tasks/tsc-compile-package.d.ts.map +1 -1
  101. package/dist/cli/tasks/tsc-compile-package.js +1 -1
  102. package/dist/cli/tasks/tsc-compile-package.js.map +1 -1
  103. package/dist/cli/tasks/tsc-compile.d.ts +2 -2
  104. package/dist/cli/tasks/tsc-compile.d.ts.map +1 -1
  105. package/dist/cli/tasks/tsc-compile.js.map +1 -1
  106. package/dist/cli/utils/bundle.js +2 -2
  107. package/dist/cli/utils/bundle.js.map +1 -1
  108. package/dist/cli/utils/cli-npm.d.ts +14 -0
  109. package/dist/cli/utils/cli-npm.d.ts.map +1 -1
  110. package/dist/cli/utils/cli-npm.js +44 -0
  111. package/dist/cli/utils/cli-npm.js.map +1 -1
  112. package/dist/cli/utils/cli-os.d.ts +2 -2
  113. package/dist/cli/utils/cli-os.d.ts.map +1 -1
  114. package/dist/cli/utils/cli-os.js +13 -9
  115. package/dist/cli/utils/cli-os.js.map +1 -1
  116. package/dist/cli/utils/eslint.d.ts +2 -1
  117. package/dist/cli/utils/eslint.d.ts.map +1 -1
  118. package/dist/cli/utils/eslint.js.map +1 -1
  119. package/dist/cli/utils/index.d.ts +1 -0
  120. package/dist/cli/utils/index.d.ts.map +1 -1
  121. package/dist/cli/utils/index.js +1 -0
  122. package/dist/cli/utils/index.js.map +1 -1
  123. package/dist/cli/utils/is-tty.d.ts +2 -0
  124. package/dist/cli/utils/is-tty.d.ts.map +1 -0
  125. package/dist/cli/utils/is-tty.js +15 -0
  126. package/dist/cli/utils/is-tty.js.map +1 -0
  127. package/dist/cli/utils/maybe-create-git-folder.d.ts +1 -1
  128. package/dist/cli/utils/maybe-create-git-folder.d.ts.map +1 -1
  129. package/dist/cli/utils/maybe-create-git-folder.js +12 -6
  130. package/dist/cli/utils/maybe-create-git-folder.js.map +1 -1
  131. package/dist/cli/utils/process-tree.d.ts.map +1 -1
  132. package/dist/cli/utils/process-tree.js +2 -2
  133. package/dist/cli/utils/process-tree.js.map +1 -1
  134. package/dist/cli/utils/set-node-options.d.ts.map +1 -1
  135. package/dist/cli/utils/set-node-options.js +2 -1
  136. package/dist/cli/utils/set-node-options.js.map +1 -1
  137. package/dist/utils/find-packages.d.ts +1 -0
  138. package/dist/utils/find-packages.d.ts.map +1 -1
  139. package/dist/utils/find-packages.js.map +1 -1
  140. package/dist/utils/format-relative-date.d.ts +2 -0
  141. package/dist/utils/format-relative-date.d.ts.map +1 -0
  142. package/dist/utils/format-relative-date.js +60 -0
  143. package/dist/utils/format-relative-date.js.map +1 -0
  144. package/dist/utils/get-configuration.d.ts +10 -3
  145. package/dist/utils/get-configuration.d.ts.map +1 -1
  146. package/dist/utils/get-configuration.js +1 -0
  147. package/dist/utils/get-configuration.js.map +1 -1
  148. package/dist/utils/get-jest-config.d.ts.map +1 -1
  149. package/dist/utils/get-jest-config.js +20 -9
  150. package/dist/utils/get-jest-config.js.map +1 -1
  151. package/dist/utils/get-packages.d.ts +3 -5
  152. package/dist/utils/get-packages.d.ts.map +1 -1
  153. package/dist/utils/get-packages.js +1 -4
  154. package/dist/utils/get-packages.js.map +1 -1
  155. package/dist/utils/get-startup-version.d.ts.map +1 -1
  156. package/dist/utils/get-startup-version.js +1 -1
  157. package/dist/utils/get-startup-version.js.map +1 -1
  158. package/dist/utils/index.d.ts +2 -0
  159. package/dist/utils/index.d.ts.map +1 -1
  160. package/dist/utils/index.js +2 -0
  161. package/dist/utils/index.js.map +1 -1
  162. package/dist/utils/omit.d.ts +2 -0
  163. package/dist/utils/omit.d.ts.map +1 -0
  164. package/dist/utils/omit.js +28 -0
  165. package/dist/utils/omit.js.map +1 -0
  166. package/dist/webpack/configs/loaders/style-loader.d.ts +1 -1
  167. package/dist/webpack/configs/loaders/style-loader.d.ts.map +1 -1
  168. package/dist/webpack/configs/loaders/style-loader.js +2 -2
  169. package/dist/webpack/configs/loaders/style-loader.js.map +1 -1
  170. package/dist/webpack/configs/optimization-config.js +5 -5
  171. package/dist/webpack/configs/optimization-config.js.map +1 -1
  172. package/dist/webpack/configs/output-config.d.ts.map +1 -1
  173. package/dist/webpack/configs/output-config.js +14 -7
  174. package/dist/webpack/configs/output-config.js.map +1 -1
  175. package/dist/webpack/configs/plugins/assets-manifest-plugin.d.ts.map +1 -1
  176. package/dist/webpack/configs/plugins/assets-manifest-plugin.js +10 -3
  177. package/dist/webpack/configs/plugins/assets-manifest-plugin.js.map +1 -1
  178. package/package.json +26 -20
  179. package/src/cli/commands/__tests__/build.test.ts +1 -1
  180. package/src/cli/commands/__tests__/bundle-package.test.ts +22 -2
  181. package/src/cli/commands/__tests__/install.test.ts +42 -4
  182. package/src/cli/commands/__tests__/lint.test.ts +1 -1
  183. package/src/cli/commands/__tests__/mfe-list.test.ts +394 -0
  184. package/src/cli/commands/__tests__/mfe-publish.test.ts +25 -0
  185. package/src/cli/commands/__tests__/start.test.ts +1 -1
  186. package/src/cli/commands/build.ts +14 -10
  187. package/src/cli/commands/bundle-package.ts +19 -13
  188. package/src/cli/commands/clean.ts +2 -4
  189. package/src/cli/commands/convert-eslint-config.ts +3 -5
  190. package/src/cli/commands/eslint.ts +3 -5
  191. package/src/cli/commands/get-command.ts +2 -0
  192. package/src/cli/commands/index.ts +0 -1
  193. package/src/cli/commands/init.ts +7 -8
  194. package/src/cli/commands/install.ts +24 -11
  195. package/src/cli/commands/kendo-ui-license.ts +4 -6
  196. package/src/cli/commands/lint.ts +19 -19
  197. package/src/cli/commands/mfe-list.ts +173 -0
  198. package/src/cli/commands/mfe-package-clean.ts +25 -4
  199. package/src/cli/commands/mfe-package-publish.ts +33 -4
  200. package/src/cli/commands/mfe-publish.ts +37 -6
  201. package/src/cli/commands/prepare-package.ts +3 -5
  202. package/src/cli/commands/review/review.ts +9 -9
  203. package/src/cli/commands/run-task.ts +15 -11
  204. package/src/cli/commands/start.ts +12 -10
  205. package/src/cli/commands/styles-check.ts +2 -2
  206. package/src/cli/commands/test/__tests__/tests.test.ts +1 -1
  207. package/src/cli/commands/test/runners/__tests__/vitest.test.ts +82 -13
  208. package/src/cli/commands/test/runners/vitest.ts +4 -2
  209. package/src/cli/commands/test/tests.ts +20 -10
  210. package/src/cli/commands/types.ts +14 -4
  211. package/src/cli/commands/upload-sourcemaps.ts +3 -5
  212. package/src/cli/index.ts +59 -36
  213. package/src/cli/tasks/swc-compile-package.ts +2 -2
  214. package/src/cli/tasks/tsc-compile-package.ts +4 -3
  215. package/src/cli/tasks/tsc-compile.ts +2 -2
  216. package/src/cli/utils/__tests__/bundle.test.ts +13 -0
  217. package/src/cli/utils/__tests__/cli-npm.test.ts +89 -0
  218. package/src/cli/utils/__tests__/is-tty.test.ts +17 -0
  219. package/src/cli/utils/__tests__/maybe-create-git-folder.test.ts +10 -17
  220. package/src/cli/utils/__tests__/set-node-options.test.ts +10 -10
  221. package/src/cli/utils/bundle.ts +2 -2
  222. package/src/cli/utils/cli-npm.ts +34 -0
  223. package/src/cli/utils/cli-os.ts +12 -25
  224. package/src/cli/utils/eslint.ts +2 -1
  225. package/src/cli/utils/index.ts +1 -0
  226. package/src/cli/utils/is-tty.ts +3 -0
  227. package/src/cli/utils/maybe-create-git-folder.ts +10 -8
  228. package/src/cli/utils/process-tree.ts +4 -2
  229. package/src/cli/utils/set-node-options.ts +2 -1
  230. package/src/utils/__tests__/format-relative-date.test.ts +61 -0
  231. package/src/utils/__tests__/get-jest-config.test.ts +44 -0
  232. package/src/utils/__tests__/get-packages.test.ts +3 -0
  233. package/src/utils/find-packages.ts +1 -0
  234. package/src/utils/format-relative-date.ts +33 -0
  235. package/src/utils/get-configuration.ts +7 -2
  236. package/src/utils/get-jest-config.ts +36 -18
  237. package/src/utils/get-packages.ts +3 -9
  238. package/src/utils/get-startup-version.ts +1 -3
  239. package/src/utils/index.ts +2 -0
  240. package/src/utils/omit.ts +12 -0
  241. package/src/webpack/__mocks__/style-rules.ts +3 -3
  242. package/src/webpack/__tests__/create-webpack-config-shared-dependencies.test.ts +6 -14
  243. package/src/webpack/__tests__/create-webpack-config-web-component.test.ts +52 -29
  244. package/src/webpack/configs/loaders/style-loader.ts +5 -2
  245. package/src/webpack/configs/optimization-config.ts +5 -5
  246. package/src/webpack/configs/output-config.ts +10 -5
  247. package/src/webpack/configs/plugins/assets-manifest-plugin.ts +11 -4
  248. package/dist/cli/commands/get-user-commands.d.ts +0 -7
  249. package/dist/cli/commands/get-user-commands.d.ts.map +0 -1
  250. package/dist/cli/commands/get-user-commands.js +0 -27
  251. package/dist/cli/commands/get-user-commands.js.map +0 -1
  252. package/src/cli/commands/__tests__/get-user-commands.test.ts +0 -27
  253. package/src/cli/commands/get-user-commands.ts +0 -19
@@ -0,0 +1,394 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ import { fs, vol } from 'memfs';
4
+ import { createInterface } from 'readline/promises';
5
+ import { formatRelativeDate, toArray } from '../../../utils';
6
+ import { isTTY, npmView, npmWhoAmI, runCommand } from '../../utils';
7
+ import { MFEList } from '../mfe-list';
8
+
9
+ jest.mock('cli-table3');
10
+ jest.mock('fs', () => fs);
11
+ jest.mock('readline/promises', () => ({
12
+ createInterface: jest.fn(),
13
+ }));
14
+ jest.mock('../../utils', () => ({
15
+ ...jest.requireActual('../../utils'),
16
+ isTTY: jest.fn(),
17
+ npmView: jest.fn(),
18
+ npmWhoAmI: jest.fn(),
19
+ runCommand: jest.fn(),
20
+ }));
21
+
22
+ describe(`[startup] ${MFEList.name}`, () => {
23
+ const DEFAULT_LIMIT = 20;
24
+ const TOTAL_VERSIONS = DEFAULT_LIMIT + 1;
25
+ const mfeName = 'foo';
26
+ let distTags: Record<string, string>;
27
+ let time: Record<string, string>;
28
+ let args: ConstructorParameters<typeof MFEList>[0];
29
+
30
+ function volFromJSON(overrides: Record<string, any> = {}) {
31
+ vol.fromJSON({
32
+ 'package.json': JSON.stringify({ workspaces: ['packages/*'] }),
33
+ 'packages/lib/package.json': JSON.stringify({ name: 'lib' }),
34
+ 'packages/mfe/package.json': JSON.stringify({
35
+ name: mfeName,
36
+ cli: { 'web-component': true },
37
+ }),
38
+ ...overrides,
39
+ });
40
+ }
41
+
42
+ beforeEach(() => {
43
+ const versions = Array.from(new Array(TOTAL_VERSIONS), makeVersion);
44
+ time = versions.reduce<Record<string, string>>((result, version, index) => {
45
+ result[version] = hourAgo(index + 1).toISOString();
46
+ return result;
47
+ }, {});
48
+ distTags = {
49
+ dev: versions[0],
50
+ other: versions[0], // version with multiple tags
51
+ qa: versions[5],
52
+ stage: versions[10],
53
+ prod: versions[15],
54
+ latest: versions[DEFAULT_LIMIT],
55
+ };
56
+ args = { _: [] }; // eslint-disable-line @typescript-eslint/naming-convention
57
+ jest.clearAllMocks();
58
+ jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn()); // suppress output
59
+ jest.mocked(npmWhoAmI).mockReturnValue('Foo');
60
+ jest.mocked(npmView).mockImplementation(({ packageName }) => ({
61
+ 'name': packageName,
62
+ 'version': distTags?.latest,
63
+ 'dist-tags': distTags,
64
+ 'time': {
65
+ created: hourAgo(24).toDateString(),
66
+ modified: new Date().toISOString(),
67
+ ...time,
68
+ },
69
+ }));
70
+ volFromJSON();
71
+ });
72
+
73
+ afterEach(() => vol.reset());
74
+
75
+ const subject = () => new MFEList(args).execute();
76
+
77
+ function hourAgo(count: number) {
78
+ const date = new Date();
79
+ date.setHours(date.getHours() - count);
80
+ return date;
81
+ }
82
+
83
+ function makeVersion() {
84
+ const hash = Math.floor(Math.random() * 1e8).toString(16);
85
+ return `0.0.0-master.${hash}`;
86
+ }
87
+
88
+ interface ExpectTableInfo {
89
+ name: string;
90
+ tags: Record<string, string>;
91
+ versions: [string, string][];
92
+ }
93
+
94
+ function expectCaption({
95
+ name,
96
+ version,
97
+ total,
98
+ }: {
99
+ name: string;
100
+ version?: string;
101
+ total: number;
102
+ }) {
103
+ const caption = [
104
+ chalk.bold.cyan(name),
105
+ version && `version: ${version}`,
106
+ `versions: ${total}`,
107
+ ]
108
+ .filter(el => !!el)
109
+ .join(' | ');
110
+
111
+ expect(jest.mocked(process.stdout.write)).toHaveBeenCalledWith(caption + '\n');
112
+ }
113
+
114
+ function expectTable(info: ExpectTableInfo | ExpectTableInfo[]) {
115
+ if (Array.isArray(info) && info.length === 0) {
116
+ expect(jest.mocked(Table).mock.instances).toHaveLength(0);
117
+ return;
118
+ }
119
+
120
+ toArray(info).forEach(({ name, tags, versions }, index) => {
121
+ const table = jest.mocked(Table).mock.instances[index];
122
+ const pushes = jest.mocked(table.push).mock.calls.map((...args) => args[0][0]);
123
+
124
+ const versionTags = Object.entries(tags).reduce<Record<string, string[]>>(
125
+ (result, [tag, version]) => {
126
+ result[version] = [...(result[version] ?? []), tag];
127
+ return result;
128
+ },
129
+ {}
130
+ );
131
+
132
+ expectCaption({ name, version: tags.latest, total: TOTAL_VERSIONS });
133
+
134
+ expect(pushes).toEqual([
135
+ ...versions.map(([version, when]) => [
136
+ version,
137
+ versionTags[version] ? chalk.bold.cyan(versionTags[version].join(', ')) : '',
138
+ formatRelativeDate(when),
139
+ ]),
140
+ ]);
141
+ });
142
+ }
143
+
144
+ function itOmitsTable() {
145
+ test('omits table', async () => {
146
+ await subject();
147
+
148
+ expectTable([]);
149
+ });
150
+ }
151
+
152
+ test(`outputs table with last ${DEFAULT_LIMIT} published versions`, async () => {
153
+ await subject();
154
+
155
+ expect(Table).toHaveBeenCalledWith({
156
+ head: ['Version', 'Tag', 'When'].map(title => chalk.bold.cyan(title)),
157
+ style: { head: [] },
158
+ });
159
+
160
+ expectTable({
161
+ name: mfeName,
162
+ tags: distTags,
163
+ versions: Object.entries(time).slice(0, DEFAULT_LIMIT),
164
+ });
165
+ });
166
+
167
+ describe('when MFE has no versions', () => {
168
+ beforeEach(() => {
169
+ jest.mocked(npmView).mockImplementation(
170
+ ({ packageName }) => ({ name: packageName }) as any
171
+ );
172
+ });
173
+
174
+ itOmitsTable();
175
+ });
176
+
177
+ describe('when MFE has no "latest" tag', () => {
178
+ beforeEach(() => delete distTags.latest);
179
+
180
+ test('outputs table', async () => {
181
+ await subject();
182
+
183
+ expectTable({
184
+ name: mfeName,
185
+ tags: distTags,
186
+ versions: Object.entries(time).slice(0, DEFAULT_LIMIT),
187
+ });
188
+ });
189
+ });
190
+
191
+ describe('with --tagged', () => {
192
+ beforeEach(() => (args.tagged = true));
193
+
194
+ test('outputs only tagged versions', async () => {
195
+ await subject();
196
+
197
+ expectTable({
198
+ name: mfeName,
199
+ tags: distTags,
200
+ versions: [...new Set(Object.values(distTags))].map(version => [
201
+ version,
202
+ time[version],
203
+ ]),
204
+ });
205
+ });
206
+
207
+ describe('when MFE has no tags', () => {
208
+ beforeEach(() => ((distTags as any) = undefined));
209
+
210
+ itOmitsTable();
211
+ });
212
+ });
213
+
214
+ describe('with --registry', () => {
215
+ beforeEach(() => (args.registry = 'https://verdaccio.st.dev'));
216
+
217
+ test('passes registry to npmView', async () => {
218
+ await subject();
219
+
220
+ expect(npmView).toHaveBeenCalledWith({
221
+ packageName: mfeName,
222
+ registry: args.registry,
223
+ });
224
+ });
225
+ });
226
+
227
+ describe('with --all', () => {
228
+ beforeEach(() => (args.all = true));
229
+
230
+ test('outputs all published versions', async () => {
231
+ await subject();
232
+
233
+ expectTable([
234
+ {
235
+ name: mfeName,
236
+ tags: distTags,
237
+ versions: Object.entries(time),
238
+ },
239
+ ]);
240
+ });
241
+ });
242
+
243
+ describe('with --limit', () => {
244
+ beforeEach(() => (args.limit = 5));
245
+
246
+ test('outputs specified number of versions', async () => {
247
+ await subject();
248
+
249
+ expectTable([
250
+ {
251
+ name: mfeName,
252
+ tags: distTags,
253
+ versions: Object.entries(time).slice(0, args.limit),
254
+ },
255
+ ]);
256
+ });
257
+ });
258
+
259
+ describe('when workspace has no MFEs', () => {
260
+ beforeEach(() => vol.reset());
261
+
262
+ itOmitsTable();
263
+ });
264
+
265
+ describe('when workspace has multiple MFEs', () => {
266
+ const otherMfeName = `${mfeName}2`; // ensure sorts after first name
267
+
268
+ beforeEach(() => {
269
+ volFromJSON({
270
+ 'packages/mfe2/package.json': JSON.stringify({
271
+ name: otherMfeName,
272
+ cli: { 'web-component': true },
273
+ }),
274
+ });
275
+ });
276
+
277
+ test('outputs published versions for all MFEs', async () => {
278
+ await subject();
279
+
280
+ expectTable([
281
+ {
282
+ name: mfeName,
283
+ tags: distTags,
284
+ versions: Object.entries(time).slice(0, DEFAULT_LIMIT),
285
+ },
286
+ {
287
+ name: otherMfeName,
288
+ tags: distTags,
289
+ versions: Object.entries(time).slice(0, DEFAULT_LIMIT),
290
+ },
291
+ ]);
292
+ });
293
+
294
+ describe('with MFE name', () => {
295
+ beforeEach(() => (args._ = [otherMfeName]));
296
+
297
+ test('outputs only specified MFE', async () => {
298
+ await subject();
299
+
300
+ expectTable({
301
+ name: otherMfeName,
302
+ tags: distTags,
303
+ versions: Object.entries(time).slice(0, DEFAULT_LIMIT),
304
+ });
305
+ });
306
+ });
307
+
308
+ describe('with --ignore', () => {
309
+ beforeEach(() => (args.ignore = mfeName));
310
+
311
+ test('omits specified MFE', async () => {
312
+ await subject();
313
+
314
+ expectTable({
315
+ name: otherMfeName,
316
+ tags: distTags,
317
+ versions: Object.entries(time).slice(0, DEFAULT_LIMIT),
318
+ });
319
+ });
320
+ });
321
+
322
+ describe('with non-existent MFE name', () => {
323
+ beforeEach(() => {
324
+ jest.mocked(npmView).mockReturnValue(undefined);
325
+ });
326
+
327
+ itOmitsTable();
328
+ });
329
+ });
330
+
331
+ describe('when machine is not authorized', () => {
332
+ const readline = { question: jest.fn(), close: jest.fn() };
333
+ let answer: string;
334
+
335
+ beforeEach(() => {
336
+ answer = 'Y';
337
+ jest.mocked(readline.question).mockImplementation(() => Promise.resolve(answer));
338
+ jest.mocked(createInterface).mockReturnValue(readline as any);
339
+ jest.mocked(npmWhoAmI).mockReturnValue(undefined);
340
+ jest.mocked(isTTY).mockReturnValue(true);
341
+ });
342
+
343
+ test('prompts and authorizes machine', async () => {
344
+ await subject();
345
+
346
+ expect(readline.question).toHaveBeenCalledWith(
347
+ expect.stringMatching(/machine is not authorized.*Authorize\? \[Y\/n\]/)
348
+ );
349
+ expect(readline.close).toHaveBeenCalled();
350
+ expect(runCommand).toHaveBeenCalledWith(
351
+ 'npx --yes verdaccio-okta-oauth@latest --registry=https://verdaccio.servicetitan.com',
352
+ { quiet: true }
353
+ );
354
+ });
355
+
356
+ describe('when user does not answer', () => {
357
+ beforeEach(() => (answer = ''));
358
+
359
+ test('authorizes machine', async () => {
360
+ await subject();
361
+
362
+ expect(runCommand).toHaveBeenCalled();
363
+ });
364
+ });
365
+
366
+ describe('when user declines', () => {
367
+ beforeEach(() => (answer = 'n'));
368
+
369
+ test('throws error', async () => {
370
+ await expect(subject()).rejects.toThrow(/Not authorized/);
371
+ });
372
+ });
373
+
374
+ describe('when terminal is not interactive', () => {
375
+ beforeEach(() => jest.mocked(isTTY).mockReturnValue(false));
376
+
377
+ test('skips prompt and throws error', async () => {
378
+ await expect(subject()).rejects.toThrow(/machine is not authorized/);
379
+
380
+ expect(readline.question).not.toHaveBeenCalled();
381
+ });
382
+ });
383
+
384
+ describe('with --registry', () => {
385
+ beforeEach(() => (args.registry = 'https://verdaccio.st.dev'));
386
+
387
+ test('skips check whether machine is authorized', async () => {
388
+ await subject();
389
+
390
+ expect(npmWhoAmI).not.toHaveBeenCalled();
391
+ });
392
+ });
393
+ });
394
+ });
@@ -3,6 +3,8 @@ import { lernaExec } from '../../utils';
3
3
  import { Package, PackageType, getPackages } from '../../../utils';
4
4
  import { createPackage } from '../../../__mocks__';
5
5
 
6
+ import { MFEPackageClean } from '../mfe-package-clean';
7
+ import { MFEPackagePublish } from '../mfe-package-publish';
6
8
  import { MFEPublish } from '../mfe-publish';
7
9
 
8
10
  jest.mock('fs', () => fs);
@@ -209,4 +211,27 @@ describe(`[startup] ${MFEPublish.name}`, () => {
209
211
  });
210
212
  });
211
213
  });
214
+
215
+ describe('options', () => {
216
+ const subject = () => MFEPublish.options;
217
+
218
+ test('groups publish and clean options', () => {
219
+ expect(subject()).toEqual(
220
+ expect.objectContaining({
221
+ ...Object.fromEntries(
222
+ Object.entries(MFEPackagePublish.options).map(([key, value]) => [
223
+ key,
224
+ { group: 'Publish Options:', ...value },
225
+ ])
226
+ ),
227
+ ...Object.fromEntries(
228
+ Object.entries(MFEPackageClean.options).map(([key, value]) => [
229
+ Object.hasOwn(MFEPackagePublish.options, key) ? `${key}\u00A0` : key,
230
+ { group: 'Clean Options:', ...value },
231
+ ])
232
+ ),
233
+ })
234
+ );
235
+ });
236
+ });
212
237
  });
@@ -83,7 +83,7 @@ describe(`[startup] ${Start.name}`, () => {
83
83
  }
84
84
 
85
85
  test('command is greedy', () => {
86
- expect(new Start(args).greedy).toBe(true);
86
+ expect(Start.greedy).toBe(true);
87
87
  });
88
88
 
89
89
  describe('with TSC package', () => {
@@ -1,9 +1,9 @@
1
1
  import { getPackages, logErrors, PackageType } from '../../utils';
2
2
  import { checkArgs, compile, lernaExec, typeCheck, ProcessTree } from '../utils';
3
3
  import { kendoUILicense } from './kendo-ui-license';
4
- import { Command } from './types';
4
+ import { Command, CommandArgs } from './types';
5
5
 
6
- interface Args {
6
+ interface Args extends CommandArgs {
7
7
  'cdn-path'?: string;
8
8
  'code-coverage'?: boolean;
9
9
  'config'?: string;
@@ -22,14 +22,18 @@ enum BuildProcesses {
22
22
  BundlePackage,
23
23
  }
24
24
 
25
- export class Build implements Command {
26
- readonly greedy = true;
27
-
28
- constructor(private readonly args: Args) {}
29
-
30
- description() {
31
- return 'build project for production';
32
- }
25
+ export class Build extends Command<Args> {
26
+ static readonly description = 'Build project for production';
27
+ static readonly greedy = true;
28
+ static readonly options = {
29
+ 'cdn-path': { string: true, description: 'Base url for application assets' },
30
+ 'code-coverage': { boolean: true, description: 'Instrument code for coverage analysis?' },
31
+ 'config': { string: true, hidden: true, description: 'Use specified configuration' },
32
+ 'ignore': { array: true, description: 'Packages to skip' },
33
+ 'scope': { array: true, description: 'Packages to build' },
34
+ 'stat': { boolean: true, description: 'Generate bundle report?' },
35
+ 'use-tsc': { boolean: true, description: 'Use tsc to compile packages?' },
36
+ };
33
37
 
34
38
  @logErrors
35
39
  async execute() {
@@ -1,21 +1,27 @@
1
1
  import { getTsConfig, log, logErrors } from '../../utils';
2
2
  import { bundle, getModuleType } from '../utils';
3
- import { Command } from '.';
3
+ import { Command, CommandArgs } from './types';
4
4
 
5
- interface Args {
6
- 'config'?: string;
7
- 'code-coverage'?: boolean;
8
- 'exposed-dependencies'?: boolean;
9
- 'stat'?: boolean;
10
- 'watch'?: boolean;
5
+ interface Args extends CommandArgs {
6
+ codeCoverage?: boolean;
7
+ config?: string;
8
+ exposedDependencies?: boolean;
9
+ stat?: boolean;
10
+ useWatchConfig?: boolean;
11
+ watch?: boolean;
11
12
  }
12
13
 
13
- export class BundlePackage implements Command {
14
- constructor(private readonly args: Args) {}
15
-
14
+ export class BundlePackage extends Command<Args> {
16
15
  @logErrors
17
16
  async execute() {
18
- const { config, stat = false, watch } = this.args;
17
+ const {
18
+ config,
19
+ codeCoverage,
20
+ exposedDependencies: emitExposedDependencies,
21
+ stat = false,
22
+ useWatchConfig,
23
+ watch,
24
+ } = this.args;
19
25
 
20
26
  if (getModuleType(getTsConfig()) !== 'esnext') {
21
27
  log.warning(
@@ -23,11 +29,11 @@ export class BundlePackage implements Command {
23
29
  );
24
30
  }
25
31
 
26
- const emitExposedDependencies = this.args['exposed-dependencies'];
27
32
  const options: Parameters<typeof bundle>[0] = {
28
33
  ...(emitExposedDependencies ? {} : { config }),
29
- codeCoverage: this.args['code-coverage'],
34
+ codeCoverage,
30
35
  emitExposedDependencies,
36
+ useWatchConfig,
31
37
  };
32
38
 
33
39
  await bundle({ buildStat: stat, ...options, watch });
@@ -2,10 +2,8 @@ import { exec, execSync } from 'child_process';
2
2
  import { log, logErrors } from '../../utils';
3
3
  import { Command } from './types';
4
4
 
5
- export class Clean implements Command {
6
- description() {
7
- return 'reset project to fresh state';
8
- }
5
+ export class Clean extends Command {
6
+ static readonly description = 'Reset project to fresh state';
9
7
 
10
8
  @logErrors
11
9
  async execute() {
@@ -40,7 +40,9 @@ interface Recipe {
40
40
  import: { as: string; from?: string };
41
41
  }
42
42
 
43
- export class ConvertEslintConfig implements Command {
43
+ export class ConvertEslintConfig extends Command {
44
+ static readonly description = 'Convert v8.x eslintrc.json to v9.x flat config';
45
+
44
46
  private static readonly recipes: Record<string, Recipe | null> = {
45
47
  '@servicetitan/eslint-config/mono': {
46
48
  import: { as: 'mono' },
@@ -74,10 +76,6 @@ export class ConvertEslintConfig implements Command {
74
76
  'prettier': null,
75
77
  };
76
78
 
77
- description() {
78
- return 'convert v8.x eslintrc.json to v9.x flat config';
79
- }
80
-
81
79
  @logErrors
82
80
  async execute() {
83
81
  if (!fs.existsSync(oldConfigFile)) {
@@ -1,16 +1,14 @@
1
- import { Command } from '.';
2
1
  import { logErrors } from '../../utils';
3
2
  import { eslint } from '../utils';
3
+ import { Command, CommandArgs } from './types';
4
4
 
5
- interface Args {
5
+ interface Args extends CommandArgs {
6
6
  // eslint-disable-next-line @typescript-eslint/naming-convention
7
7
  _: string[];
8
8
  fix?: boolean;
9
9
  }
10
10
 
11
- export class ESLintCommand implements Command {
12
- constructor(private readonly args: Args) {}
13
-
11
+ export class ESLintCommand extends Command<Args> {
14
12
  @logErrors
15
13
  async execute() {
16
14
  const { _: paths, fix } = this.args;
@@ -9,6 +9,7 @@ import { Init } from './init';
9
9
  import { Install } from './install';
10
10
  import { KendoUILicense } from './kendo-ui-license';
11
11
  import { Lint } from './lint';
12
+ import { MFEList } from './mfe-list';
12
13
  import { MFEPackageClean } from './mfe-package-clean';
13
14
  import { MFEPackagePublish } from './mfe-package-publish';
14
15
  import { MFEPublish } from './mfe-publish';
@@ -31,6 +32,7 @@ const commands: Record<CommandName, Newable<Command>> = {
31
32
  [CommandName.install]: Install,
32
33
  [CommandName['kendo-ui-license']]: KendoUILicense,
33
34
  [CommandName.lint]: Lint,
35
+ [CommandName['mfe-list']]: MFEList,
34
36
  [CommandName['mfe-package-clean']]: MFEPackageClean,
35
37
  [CommandName['mfe-package-publish']]: MFEPackagePublish,
36
38
  [CommandName['mfe-publish']]: MFEPublish,
@@ -2,7 +2,6 @@ export * from './build';
2
2
  export * from './bundle-package';
3
3
  export * from './eslint';
4
4
  export * from './get-command';
5
- export * from './get-user-commands';
6
5
  export * from './init';
7
6
  export * from './install';
8
7
  export * from './kendo-ui-license';
@@ -3,20 +3,19 @@ import path from 'path';
3
3
 
4
4
  import { log, logErrors } from '../../utils';
5
5
  import { gitCloneRepo, gitIsReachable } from '../utils';
6
- import { Command } from './types';
6
+ import { Command, CommandArgs } from './types';
7
7
 
8
- interface Args {
8
+ interface Args extends CommandArgs {
9
9
  output?: string;
10
10
  }
11
11
 
12
12
  const REPO_NAME = 'frontend-example';
13
13
 
14
- export class Init implements Command {
15
- constructor(private readonly args: Args) {}
16
-
17
- description() {
18
- return 'create example project';
19
- }
14
+ export class Init extends Command<Args> {
15
+ static readonly description = 'Create example project';
16
+ static readonly options = {
17
+ output: { string: true, description: 'Output path' },
18
+ };
20
19
 
21
20
  @logErrors
22
21
  async execute() {