@servicetitan/startup 24.0.4 → 24.1.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 (92) hide show
  1. package/dist/cli/commands/build.d.ts +1 -0
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +3 -0
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/bundle-package.d.ts +1 -0
  6. package/dist/cli/commands/bundle-package.d.ts.map +1 -1
  7. package/dist/cli/commands/bundle-package.js +3 -0
  8. package/dist/cli/commands/bundle-package.js.map +1 -1
  9. package/dist/cli/commands/eslint.d.ts +1 -0
  10. package/dist/cli/commands/eslint.d.ts.map +1 -1
  11. package/dist/cli/commands/eslint.js +3 -0
  12. package/dist/cli/commands/eslint.js.map +1 -1
  13. package/dist/cli/commands/get-command.d.ts +6 -0
  14. package/dist/cli/commands/get-command.d.ts.map +1 -0
  15. package/dist/cli/commands/get-command.js +50 -0
  16. package/dist/cli/commands/get-command.js.map +1 -0
  17. package/dist/cli/commands/get-user-commands.d.ts +7 -0
  18. package/dist/cli/commands/get-user-commands.d.ts.map +1 -0
  19. package/dist/cli/commands/get-user-commands.js +17 -0
  20. package/dist/cli/commands/get-user-commands.js.map +1 -0
  21. package/dist/cli/commands/index.d.ts +3 -3
  22. package/dist/cli/commands/index.d.ts.map +1 -1
  23. package/dist/cli/commands/index.js +3 -0
  24. package/dist/cli/commands/index.js.map +1 -1
  25. package/dist/cli/commands/init.d.ts +2 -1
  26. package/dist/cli/commands/init.d.ts.map +1 -1
  27. package/dist/cli/commands/init.js +5 -2
  28. package/dist/cli/commands/init.js.map +1 -1
  29. package/dist/cli/commands/install.d.ts +1 -0
  30. package/dist/cli/commands/install.d.ts.map +1 -1
  31. package/dist/cli/commands/install.js +3 -0
  32. package/dist/cli/commands/install.js.map +1 -1
  33. package/dist/cli/commands/kendo-ui-license.d.ts +1 -0
  34. package/dist/cli/commands/kendo-ui-license.d.ts.map +1 -1
  35. package/dist/cli/commands/kendo-ui-license.js +3 -0
  36. package/dist/cli/commands/kendo-ui-license.js.map +1 -1
  37. package/dist/cli/commands/lint.d.ts +1 -0
  38. package/dist/cli/commands/lint.d.ts.map +1 -1
  39. package/dist/cli/commands/lint.js +3 -0
  40. package/dist/cli/commands/lint.js.map +1 -1
  41. package/dist/cli/commands/mfe-publish.d.ts +3 -0
  42. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  43. package/dist/cli/commands/mfe-publish.js +9 -0
  44. package/dist/cli/commands/mfe-publish.js.map +1 -1
  45. package/dist/cli/commands/prepare-package.d.ts +1 -0
  46. package/dist/cli/commands/prepare-package.d.ts.map +1 -1
  47. package/dist/cli/commands/prepare-package.js +3 -0
  48. package/dist/cli/commands/prepare-package.js.map +1 -1
  49. package/dist/cli/commands/start.d.ts +1 -0
  50. package/dist/cli/commands/start.d.ts.map +1 -1
  51. package/dist/cli/commands/start.js +3 -0
  52. package/dist/cli/commands/start.js.map +1 -1
  53. package/dist/cli/commands/styles-check.d.ts +1 -0
  54. package/dist/cli/commands/styles-check.d.ts.map +1 -1
  55. package/dist/cli/commands/styles-check.js +3 -0
  56. package/dist/cli/commands/styles-check.js.map +1 -1
  57. package/dist/cli/commands/tests.d.ts +1 -0
  58. package/dist/cli/commands/tests.d.ts.map +1 -1
  59. package/dist/cli/commands/tests.js +3 -0
  60. package/dist/cli/commands/tests.js.map +1 -1
  61. package/dist/cli/commands/types.d.ts +5 -0
  62. package/dist/cli/commands/types.d.ts.map +1 -0
  63. package/dist/cli/commands/types.js +3 -0
  64. package/dist/cli/commands/types.js.map +1 -0
  65. package/dist/cli/index.js +24 -37
  66. package/dist/cli/index.js.map +1 -1
  67. package/dist/utils/get-packages.d.ts.map +1 -1
  68. package/dist/utils/get-packages.js +27 -12
  69. package/dist/utils/get-packages.js.map +1 -1
  70. package/package.json +11 -11
  71. package/src/cli/commands/__tests__/get-command.test.ts +17 -0
  72. package/src/cli/commands/__tests__/get-user-commands.test.ts +24 -0
  73. package/src/cli/commands/__tests__/init.test.ts +4 -4
  74. package/src/cli/commands/build.ts +4 -0
  75. package/src/cli/commands/bundle-package.ts +4 -0
  76. package/src/cli/commands/eslint.ts +4 -0
  77. package/src/cli/commands/get-command.ts +50 -0
  78. package/src/cli/commands/get-user-commands.ts +19 -0
  79. package/src/cli/commands/index.ts +3 -4
  80. package/src/cli/commands/init.ts +7 -3
  81. package/src/cli/commands/install.ts +4 -0
  82. package/src/cli/commands/kendo-ui-license.ts +4 -0
  83. package/src/cli/commands/lint.ts +4 -0
  84. package/src/cli/commands/mfe-publish.ts +12 -0
  85. package/src/cli/commands/prepare-package.ts +4 -0
  86. package/src/cli/commands/start.ts +4 -0
  87. package/src/cli/commands/styles-check.ts +4 -0
  88. package/src/cli/commands/tests.ts +4 -0
  89. package/src/cli/commands/types.ts +4 -0
  90. package/src/cli/index.ts +27 -59
  91. package/src/utils/__tests__/get-packages.test.ts +47 -10
  92. package/src/utils/get-packages.ts +32 -17
@@ -45,6 +45,10 @@ interface Args extends ArgsPackagePublish, ArgsPackageClean {
45
45
  export class MFEPublish implements Command {
46
46
  constructor(private args: Args) {}
47
47
 
48
+ description() {
49
+ return 'publish or unpublish MFE packages';
50
+ }
51
+
48
52
  @logErrors
49
53
  async execute() {
50
54
  let packages = splitPackagesByType(getPackages())[PackageType.Webpack] ?? [];
@@ -93,6 +97,10 @@ export class MFEPublish implements Command {
93
97
  export class MFEPackagePublish implements Command {
94
98
  constructor(private args: ArgsPackagePublish) {}
95
99
 
100
+ description() {
101
+ return undefined;
102
+ }
103
+
96
104
  @logErrors
97
105
  async execute() {
98
106
  if (!isWebComponent()) {
@@ -197,6 +205,10 @@ export class MFEPackagePublish implements Command {
197
205
  export class MFEPackageClean implements Command {
198
206
  constructor(private args: ArgsPackageClean) {}
199
207
 
208
+ description() {
209
+ return undefined;
210
+ }
211
+
200
212
  @logErrors
201
213
  async execute() {
202
214
  if (!isWebComponent()) {
@@ -9,6 +9,10 @@ interface Args {
9
9
  export class PreparePackage implements Command {
10
10
  constructor(private args: Args) {}
11
11
 
12
+ description() {
13
+ return undefined;
14
+ }
15
+
12
16
  @logErrors
13
17
  async execute() {
14
18
  if (!this.args.watch) {
@@ -14,6 +14,10 @@ interface Args {
14
14
  export class Start implements Command {
15
15
  constructor(private args: Args) {}
16
16
 
17
+ description() {
18
+ return 'run project in development mode';
19
+ }
20
+
17
21
  @logErrors
18
22
  async execute() {
19
23
  const packages = splitPackagesByType(
@@ -121,6 +121,10 @@ function checkStylesWebComponent(files: FileInfo[]) {
121
121
  }
122
122
 
123
123
  export class StylesCheck implements Command {
124
+ description() {
125
+ return undefined;
126
+ }
127
+
124
128
  // eslint-disable-next-line @typescript-eslint/require-await
125
129
  async execute() {
126
130
  if (isLegacy()) {
@@ -9,6 +9,10 @@ import { Command } from '.';
9
9
  export class Tests implements Command {
10
10
  constructor(private args: Config.Argv) {}
11
11
 
12
+ description() {
13
+ return 'run tests';
14
+ }
15
+
12
16
  @logErrors
13
17
  async execute() {
14
18
  const jestConfig = getJestConfigCLI(this.args);
@@ -0,0 +1,4 @@
1
+ export interface Command {
2
+ execute(): Promise<void>;
3
+ description(): string | undefined;
4
+ }
package/src/cli/index.ts CHANGED
@@ -1,69 +1,23 @@
1
1
  import execa from 'execa';
2
2
  import { argv, Arguments } from 'yargs';
3
-
4
- import { CommandName, log } from '../utils';
5
- import {
6
- Build,
7
- BundlePackage,
8
- Command,
9
- ESLintCommand,
10
- Init,
11
- Install,
12
- KendoUILicense,
13
- Lint,
14
- MFEPackageClean,
15
- MFEPackagePublish,
16
- MFEPublish,
17
- PreparePackage,
18
- Start,
19
- StylesCheck,
20
- Tests,
21
- } from './commands';
3
+ import { CommandName, getStartupVersion, log } from '../utils';
4
+ import { getCommand, getUserCommands } from './commands';
22
5
  import { setNodeOptions } from './utils';
23
6
 
24
- interface Newable<T> {
25
- new (...args: any[]): T;
26
- }
27
-
28
- function getCommand(name: CommandName): Newable<Command> {
29
- switch (name) {
30
- case CommandName.build:
31
- return Build;
32
- case CommandName['bundle-package']:
33
- return BundlePackage;
34
- case CommandName.eslint:
35
- return ESLintCommand;
36
- case CommandName.init:
37
- return Init;
38
- case CommandName.install:
39
- return Install;
40
- case CommandName['kendo-ui-license']:
41
- return KendoUILicense;
42
- case CommandName.lint:
43
- return Lint;
44
- case CommandName['mfe-package-clean']:
45
- return MFEPackageClean;
46
- case CommandName['mfe-package-publish']:
47
- return MFEPackagePublish;
48
- case CommandName['mfe-publish']:
49
- return MFEPublish;
50
- case CommandName['prepare-package']:
51
- return PreparePackage;
52
- case CommandName.start:
53
- return Start;
54
- case CommandName['styles-check']:
55
- return StylesCheck;
56
- case CommandName.test:
57
- return Tests;
58
- default:
59
- log.error(`${name}: command not found!`);
60
- process.exit(127);
61
- }
7
+ const argvSync = argv as Arguments;
8
+ const name = argvSync._[0]?.toString() as CommandName;
9
+ if (!name) {
10
+ log.info(`startup cli v${getStartupVersion()}`);
11
+ usage();
12
+ process.exit(0);
62
13
  }
63
14
 
64
- const argvSync = argv as Arguments;
65
- const name = argvSync._[0].toString() as CommandName;
66
15
  const Command = getCommand(name);
16
+ if (!Command) {
17
+ log.error(`Unknown command: "${name}"`);
18
+ usage();
19
+ process.exit(127);
20
+ }
67
21
 
68
22
  if (setNodeOptions(name)) {
69
23
  // Run command in child process with amended NODE_OPTIONS
@@ -77,3 +31,17 @@ if (setNodeOptions(name)) {
77
31
  process.exit(1);
78
32
  });
79
33
  }
34
+
35
+ function usage() {
36
+ write('\nUsage:\n');
37
+
38
+ const commands = getUserCommands().filter(({ name }) => !!name);
39
+ const maxNameLength = commands.reduce((result, { name }) => Math.max(result, name.length), 0);
40
+ commands.forEach(({ name, description }) => {
41
+ write(`startup ${name.padEnd(maxNameLength, ' ')} ${description}`);
42
+ });
43
+ }
44
+
45
+ function write(text: string) {
46
+ return console.info(text); // eslint-disable-line no-console
47
+ }
@@ -1,5 +1,6 @@
1
1
  import execa from 'execa';
2
2
  import { isBundle, isLegacy } from '../get-configuration';
3
+ import { log } from '../log';
3
4
  import { maybeCreateGitFolder } from '../maybe-create-git-folder';
4
5
 
5
6
  import {
@@ -21,6 +22,15 @@ describe('[startup] Utils', () => {
21
22
  { name: 'baz', location: 'packages/baz', type: PackageType.Webpack },
22
23
  ];
23
24
 
25
+ beforeEach(() => jest.clearAllMocks());
26
+
27
+ function expectExecaToBeCalledWith(command: string, options?: execa.SyncOptions) {
28
+ const [file, ...args] = command.split(' ');
29
+ const { calls } = jest.mocked(execa.sync).mock;
30
+ const expected: any[] = [file, args, ...(options ? [options] : [])];
31
+ expect(calls).toContainEqual(expect.arrayContaining(expected));
32
+ }
33
+
24
34
  describe(`${getPackages.name}`, () => {
25
35
  let options: Parameters<typeof getPackages>[0] | undefined;
26
36
  let dependencies: Record<string, string[]> | undefined;
@@ -72,7 +82,7 @@ describe('[startup] Utils', () => {
72
82
 
73
83
  test('returns lerna packages with type metadata', () => {
74
84
  expect(subject()).toEqual(packages);
75
- expect(execa.sync).toHaveBeenCalledWith('npx', 'lerna la --json'.split(' '));
85
+ expectExecaToBeCalledWith('npx lerna la --json');
76
86
  });
77
87
 
78
88
  test('conditionally creates .git folder', () => {
@@ -81,16 +91,34 @@ describe('[startup] Utils', () => {
81
91
  expect(maybeCreateGitFolder).toHaveBeenCalled();
82
92
  });
83
93
 
94
+ describe('when running on Windows', () => {
95
+ const originalPlatform = process.platform;
96
+ beforeEach(() => {
97
+ Object.defineProperty(process, 'platform', { value: 'win32' });
98
+ });
99
+
100
+ afterEach(() => {
101
+ Object.defineProperty(process, 'platform', { value: originalPlatform });
102
+ });
103
+
104
+ test('sets NX_CACHE_PROJECT_GRAPH=false', () => {
105
+ subject();
106
+
107
+ expectExecaToBeCalledWith('npx lerna la --json', {
108
+ extendEnv: true,
109
+ // eslint-disable-next-line @typescript-eslint/naming-convention
110
+ env: { NX_CACHE_PROJECT_GRAPH: 'false' },
111
+ });
112
+ });
113
+ });
114
+
84
115
  describe.each(['scope', 'ignore'])('with "%s"', option => {
85
116
  beforeEach(() => (options = { [option]: 'foo' }));
86
117
 
87
118
  test(`passes "${option}" option to lerna`, () => {
88
119
  subject();
89
120
 
90
- expect(execa.sync).toHaveBeenCalledWith(
91
- 'npx',
92
- `lerna la --${option} foo --json`.split(' ')
93
- );
121
+ expectExecaToBeCalledWith(`npx lerna la --${option} foo --json`);
94
122
  });
95
123
  });
96
124
 
@@ -111,6 +139,18 @@ describe('[startup] Utils', () => {
111
139
  });
112
140
  });
113
141
  });
142
+
143
+ describe('when execa returns unexpected output', () => {
144
+ beforeEach(() =>
145
+ jest.mocked(execa.sync).mockReturnValue({ stdout: Buffer.from('foo') } as any)
146
+ );
147
+
148
+ test('throws an error and logs the output', () => {
149
+ const logSpy = jest.spyOn(log, 'error');
150
+ expect(() => subject()).toThrow();
151
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('\nfoo'));
152
+ });
153
+ });
114
154
  });
115
155
 
116
156
  describe(`${getPackagesGraph.name}`, () => {
@@ -128,7 +168,7 @@ describe('[startup] Utils', () => {
128
168
 
129
169
  test('returns lerna package graph', () => {
130
170
  expect(subject()).toEqual(packageGraph);
131
- expect(execa.sync).toHaveBeenCalledWith('npx', 'lerna la --graph'.split(' '));
171
+ expectExecaToBeCalledWith('npx lerna la --graph');
132
172
  });
133
173
 
134
174
  describe.each(['scope', 'ignore'])('with "%s"', option => {
@@ -137,10 +177,7 @@ describe('[startup] Utils', () => {
137
177
  test(`passes "${option}" option to lerna`, () => {
138
178
  subject();
139
179
 
140
- expect(execa.sync).toHaveBeenCalledWith(
141
- 'npx',
142
- `lerna la --${option} foo --graph`.split(' ')
143
- );
180
+ expectExecaToBeCalledWith(`npx lerna la --${option} foo --graph`);
144
181
  });
145
182
  });
146
183
  });
@@ -2,6 +2,7 @@ import execa from 'execa';
2
2
  import { isBundle, isLegacy } from './get-configuration';
3
3
  import { maybeCreateGitFolder } from './maybe-create-git-folder';
4
4
  import { toArray } from './to-array';
5
+ import { log } from './log';
5
6
 
6
7
  export enum PackageType {
7
8
  TSC,
@@ -29,23 +30,13 @@ export function getPackages(options: GetPackagesOptions = {}) {
29
30
  const ignore = toArray(options.ignore);
30
31
 
31
32
  maybeCreateGitFolder();
32
- const allPackages: RawPackage[] = JSON.parse(
33
- execa.sync('npx', ['lerna', 'la', '--json']).stdout
34
- );
33
+ const allPackages: RawPackage[] = listPackages();
35
34
 
36
35
  if (!scope.length && !ignore.length) {
37
36
  return withMetadata(allPackages);
38
37
  }
39
38
 
40
- const filteredPackages: RawPackage[] = JSON.parse(
41
- execa.sync('npx', [
42
- 'lerna',
43
- 'la',
44
- ...scope.map(v => ['--scope', v]).flat(),
45
- ...ignore.map(v => ['--ignore', v]).flat(),
46
- '--json',
47
- ]).stdout
48
- );
39
+ const filteredPackages: RawPackage[] = listPackages(options);
49
40
 
50
41
  const graph: Record<string, string[]> = getPackagesGraph();
51
42
 
@@ -86,18 +77,42 @@ interface GetPackagesGraphOptions {
86
77
  * Returns packages and their direct dependencies
87
78
  */
88
79
  export function getPackagesGraph(options: GetPackagesGraphOptions = {}): Record<string, string[]> {
80
+ return listPackages({ ...options, graph: true });
81
+ }
82
+
83
+ function listPackages(options: GetPackagesOptions & { graph?: boolean } = {}) {
89
84
  const scope = toArray(options.scope);
90
85
  const ignore = toArray(options.ignore);
91
86
 
92
- return JSON.parse(
93
- execa.sync('npx', [
87
+ let execaOptions: execa.SyncOptions | undefined;
88
+ if (process.platform === 'win32') {
89
+ /*
90
+ * Disabled the project graph cache to workaround issue when nx fails with
91
+ * "Error: EPERM: operation not permitted, rename ..." error when multiple
92
+ * processes simultaneously generate the project graph.
93
+ */
94
+ // eslint-disable-next-line @typescript-eslint/naming-convention
95
+ execaOptions = { extendEnv: true, env: { NX_CACHE_PROJECT_GRAPH: 'false' } };
96
+ }
97
+
98
+ const output = execa.sync(
99
+ 'npx',
100
+ [
94
101
  'lerna',
95
102
  'la',
96
103
  ...scope.map(v => ['--scope', v]).flat(),
97
104
  ...ignore.map(v => ['--ignore', v]).flat(),
98
- '--graph',
99
- ]).stdout
100
- );
105
+ options.graph ? '--graph' : '--json',
106
+ ],
107
+ execaOptions
108
+ ).stdout;
109
+
110
+ try {
111
+ return JSON.parse(output);
112
+ } catch (error) {
113
+ log.error(`error: could not parse lerna output:\n${output}`);
114
+ throw error;
115
+ }
101
116
  }
102
117
 
103
118
  function withMetadata(packages: RawPackage[]): Package[] {