@servicetitan/startup 28.5.0 → 30.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 (136) hide show
  1. package/bin/index.js +1 -1
  2. package/dist/cli/commands/convert-eslint-config.d.ts +21 -0
  3. package/dist/cli/commands/convert-eslint-config.d.ts.map +1 -0
  4. package/dist/cli/commands/convert-eslint-config.js +235 -0
  5. package/dist/cli/commands/convert-eslint-config.js.map +1 -0
  6. package/dist/cli/commands/get-command.d.ts.map +1 -1
  7. package/dist/cli/commands/get-command.js +6 -0
  8. package/dist/cli/commands/get-command.js.map +1 -1
  9. package/dist/cli/commands/init.d.ts.map +1 -1
  10. package/dist/cli/commands/init.js +4 -3
  11. package/dist/cli/commands/init.js.map +1 -1
  12. package/dist/cli/commands/lint.d.ts +1 -1
  13. package/dist/cli/commands/lint.js +2 -2
  14. package/dist/cli/commands/mfe-package-clean.d.ts +5 -1
  15. package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -1
  16. package/dist/cli/commands/mfe-package-clean.js +46 -36
  17. package/dist/cli/commands/mfe-package-clean.js.map +1 -1
  18. package/dist/cli/commands/mfe-package-publish.d.ts +1 -1
  19. package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -1
  20. package/dist/cli/commands/mfe-package-publish.js +3 -13
  21. package/dist/cli/commands/mfe-package-publish.js.map +1 -1
  22. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  23. package/dist/cli/commands/mfe-publish.js +6 -5
  24. package/dist/cli/commands/mfe-publish.js.map +1 -1
  25. package/dist/cli/commands/run-task.d.ts +13 -0
  26. package/dist/cli/commands/run-task.d.ts.map +1 -0
  27. package/dist/cli/commands/run-task.js +53 -0
  28. package/dist/cli/commands/run-task.js.map +1 -0
  29. package/dist/cli/index.js +13 -4
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/tasks/cli-task.d.ts +16 -0
  32. package/dist/cli/tasks/cli-task.d.ts.map +1 -0
  33. package/dist/cli/tasks/cli-task.js +58 -0
  34. package/dist/cli/tasks/cli-task.js.map +1 -0
  35. package/dist/cli/tasks/swc-compile-package.d.ts +12 -0
  36. package/dist/cli/tasks/swc-compile-package.d.ts.map +1 -0
  37. package/dist/cli/tasks/swc-compile-package.js +66 -0
  38. package/dist/cli/tasks/swc-compile-package.js.map +1 -0
  39. package/dist/cli/tasks/task.d.ts +42 -0
  40. package/dist/cli/tasks/task.d.ts.map +1 -0
  41. package/dist/cli/tasks/task.js +113 -0
  42. package/dist/cli/tasks/task.js.map +1 -0
  43. package/dist/cli/tasks/tsc-compile-package.d.ts +12 -0
  44. package/dist/cli/tasks/tsc-compile-package.d.ts.map +1 -0
  45. package/dist/cli/tasks/tsc-compile-package.js +42 -0
  46. package/dist/cli/tasks/tsc-compile-package.js.map +1 -0
  47. package/dist/cli/tasks/tsc-compile.d.ts +16 -0
  48. package/dist/cli/tasks/tsc-compile.d.ts.map +1 -0
  49. package/dist/cli/tasks/tsc-compile.js +48 -0
  50. package/dist/cli/tasks/tsc-compile.js.map +1 -0
  51. package/dist/cli/utils/bundle.js +1 -1
  52. package/dist/cli/utils/bundle.js.map +1 -1
  53. package/dist/cli/utils/cli-npm.d.ts +10 -3
  54. package/dist/cli/utils/cli-npm.d.ts.map +1 -1
  55. package/dist/cli/utils/cli-npm.js +13 -15
  56. package/dist/cli/utils/cli-npm.js.map +1 -1
  57. package/dist/cli/utils/cli-os.d.ts.map +1 -1
  58. package/dist/cli/utils/cli-os.js +2 -2
  59. package/dist/cli/utils/cli-os.js.map +1 -1
  60. package/dist/cli/utils/eslint.d.ts.map +1 -1
  61. package/dist/cli/utils/eslint.js +13 -12
  62. package/dist/cli/utils/eslint.js.map +1 -1
  63. package/dist/cli/utils/is-module-installed.js +1 -1
  64. package/dist/cli/utils/is-module-installed.js.map +1 -1
  65. package/dist/cli/utils/publish.d.ts.map +1 -1
  66. package/dist/cli/utils/tsc.d.ts +3 -2
  67. package/dist/cli/utils/tsc.d.ts.map +1 -1
  68. package/dist/cli/utils/tsc.js +20 -16
  69. package/dist/cli/utils/tsc.js.map +1 -1
  70. package/dist/utils/get-configuration.d.ts +3 -1
  71. package/dist/utils/get-configuration.d.ts.map +1 -1
  72. package/dist/utils/get-configuration.js +2 -0
  73. package/dist/utils/get-configuration.js.map +1 -1
  74. package/dist/utils/get-destination-folders.d.ts.map +1 -1
  75. package/dist/utils/get-destination-folders.js +9 -13
  76. package/dist/utils/get-destination-folders.js.map +1 -1
  77. package/dist/utils/get-folders.js +2 -2
  78. package/dist/utils/get-folders.js.map +1 -1
  79. package/dist/utils/get-jest-config.d.ts.map +1 -1
  80. package/dist/utils/log.d.ts +1 -0
  81. package/dist/utils/log.d.ts.map +1 -1
  82. package/dist/utils/log.js +3 -0
  83. package/dist/utils/log.js.map +1 -1
  84. package/dist/webpack/configs/plugins/ignore-plugin/check-resource.d.ts.map +1 -1
  85. package/dist/webpack/configs/plugins/ignore-plugin/check-resource.js +1 -1
  86. package/dist/webpack/configs/plugins/ignore-plugin/check-resource.js.map +1 -1
  87. package/dist/webpack/utils/hash-mod.d.ts.map +1 -1
  88. package/dist/webpack/utils/testing/execute.d.ts.map +1 -1
  89. package/dist/webpack/utils/testing/get-compiler.d.ts.map +1 -1
  90. package/package.json +28 -24
  91. package/src/cli/commands/__tests__/convert-eslint-config.test.ts +455 -0
  92. package/src/cli/commands/__tests__/lint.test.ts +2 -2
  93. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +143 -73
  94. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +27 -20
  95. package/src/cli/commands/__tests__/mfe-publish.test.ts +18 -7
  96. package/src/cli/commands/convert-eslint-config.ts +289 -0
  97. package/src/cli/commands/get-command.ts +6 -0
  98. package/src/cli/commands/init.ts +4 -3
  99. package/src/cli/commands/lint.ts +3 -3
  100. package/src/cli/commands/mfe-package-clean.ts +53 -52
  101. package/src/cli/commands/mfe-package-publish.ts +7 -21
  102. package/src/cli/commands/mfe-publish.ts +6 -5
  103. package/src/cli/commands/run-task.ts +41 -0
  104. package/src/cli/index.ts +16 -4
  105. package/src/cli/tasks/__tests__/cli-task.test.ts +115 -0
  106. package/src/cli/tasks/__tests__/swc-compile.test.ts +192 -0
  107. package/src/cli/tasks/__tests__/task.test.ts +88 -0
  108. package/src/cli/tasks/__tests__/tsc-compile-package.test.ts +72 -0
  109. package/src/cli/tasks/__tests__/tsc-compile.test.ts +156 -0
  110. package/src/cli/tasks/cli-task.ts +64 -0
  111. package/src/cli/tasks/swc-cli.d.ts +3 -0
  112. package/src/cli/tasks/swc-compile-package.ts +70 -0
  113. package/src/cli/tasks/task.ts +112 -0
  114. package/src/cli/tasks/tsc-compile-package.ts +47 -0
  115. package/src/cli/tasks/tsc-compile.ts +64 -0
  116. package/src/cli/utils/__tests__/assets-copy.test.ts +1 -1
  117. package/src/cli/utils/__tests__/bundle.test.ts +1 -1
  118. package/src/cli/utils/__tests__/cli-npm.test.ts +39 -26
  119. package/src/cli/utils/__tests__/cli-os.test.ts +2 -2
  120. package/src/cli/utils/__tests__/eslint.test.ts +37 -8
  121. package/src/cli/utils/__tests__/styles-copy.test.ts +1 -1
  122. package/src/cli/utils/__tests__/tsc.test.ts +34 -55
  123. package/src/cli/utils/bundle.ts +1 -1
  124. package/src/cli/utils/cli-npm.ts +25 -16
  125. package/src/cli/utils/cli-os.ts +2 -2
  126. package/src/cli/utils/eslint.ts +16 -13
  127. package/src/cli/utils/is-module-installed.ts +1 -1
  128. package/src/cli/utils/tsc.ts +25 -20
  129. package/src/utils/__tests__/get-destination-folders.test.ts +1 -1
  130. package/src/utils/__tests__/get-folders.test.ts +6 -18
  131. package/src/utils/__tests__/log.test.ts +6 -0
  132. package/src/utils/get-configuration.ts +2 -0
  133. package/src/utils/get-destination-folders.ts +11 -17
  134. package/src/utils/get-folders.ts +2 -2
  135. package/src/utils/log.ts +4 -0
  136. package/src/webpack/configs/plugins/ignore-plugin/check-resource.ts +1 -1
@@ -0,0 +1,156 @@
1
+ import execa from 'execa';
2
+ import { fs, vol } from 'memfs';
3
+ import path from 'path';
4
+ import { TscCompile } from '../tsc-compile';
5
+
6
+ jest.mock('fs', () => fs);
7
+ jest.mock('execa', () =>
8
+ jest.fn().mockReturnValue(
9
+ Object.assign(Promise.resolve(), {
10
+ stdout: {
11
+ on: jest.fn(),
12
+ },
13
+ })
14
+ )
15
+ );
16
+
17
+ describe(`[startup] ${TscCompile.name} task`, () => {
18
+ let watch = false;
19
+ let typeCheckOnly = false;
20
+ let scope: string[];
21
+ let ignore: string[];
22
+
23
+ beforeEach(() => {
24
+ vol.fromJSON({
25
+ 'packages/foo/tsconfig.json': JSON.stringify({}),
26
+ 'packages/foo/package.json': JSON.stringify({
27
+ name: 'foo',
28
+ cli: {
29
+ webpack: false,
30
+ },
31
+ dependencies: {
32
+ baz: '*',
33
+ },
34
+ }),
35
+ 'packages/bar/tsconfig.json': JSON.stringify({}),
36
+ 'packages/bar/package.json': JSON.stringify({
37
+ name: 'bar',
38
+ cli: {
39
+ webpack: false,
40
+ },
41
+ }),
42
+ 'packages/baz/tsconfig.json': JSON.stringify({}),
43
+ 'packages/baz/package.json': JSON.stringify({
44
+ name: 'baz',
45
+ cli: {
46
+ webpack: false,
47
+ },
48
+ }),
49
+ 'package.json': JSON.stringify({ workspaces: ['packages/*'] }),
50
+ });
51
+ });
52
+
53
+ afterEach(() => {
54
+ vol.reset();
55
+ globalThis.performance.clearMarks();
56
+ globalThis.performance.clearMeasures();
57
+ jest.clearAllMocks();
58
+ watch = false;
59
+ typeCheckOnly = false;
60
+ scope = [];
61
+ ignore = [];
62
+ });
63
+
64
+ function itRunsTsc(
65
+ caption: string,
66
+ {
67
+ watch = false,
68
+ typeCheckOnly = false,
69
+ tsConfigs,
70
+ stdio,
71
+ }: {
72
+ watch?: boolean;
73
+ typeCheckOnly?: boolean;
74
+ tsConfigs: string[];
75
+ stdio: string | string[];
76
+ }
77
+ ) {
78
+ test(`runs tsc ${caption}`, () => {
79
+ subject();
80
+
81
+ expect(execa).toHaveBeenCalledWith(
82
+ 'tsc',
83
+ expect.arrayContaining(
84
+ [
85
+ '-b',
86
+ watch ? '-w' : undefined,
87
+ '--pretty',
88
+ '--preserveWatchOutput',
89
+ typeCheckOnly ? '--emitDeclarationOnly' : undefined,
90
+ ...tsConfigs.map(config => expect.stringContaining(path.normalize(config))),
91
+ ].filter(item => item !== undefined)
92
+ ),
93
+ {
94
+ stdio,
95
+ }
96
+ );
97
+ });
98
+ }
99
+
100
+ const subject = () => new TscCompile({ watch, typeCheckOnly, scope, ignore }).execute();
101
+
102
+ itRunsTsc('', {
103
+ tsConfigs: ['foo/tsconfig.json', 'bar/tsconfig.json'],
104
+ stdio: 'inherit',
105
+ });
106
+
107
+ describe('in watch mode', () => {
108
+ beforeEach(() => (watch = true));
109
+
110
+ itRunsTsc('in watch mode', {
111
+ watch: true,
112
+ tsConfigs: ['foo/tsconfig.json', 'bar/tsconfig.json'],
113
+ stdio: ['inherit', 'pipe', 'inherit'],
114
+ });
115
+ });
116
+
117
+ describe('with "typeCheckOnly" mode', () => {
118
+ beforeEach(() => (typeCheckOnly = true));
119
+
120
+ itRunsTsc('in "typeCheckOnly" mode', {
121
+ typeCheckOnly: true,
122
+ tsConfigs: ['foo/tsconfig.json', 'bar/tsconfig.json'],
123
+ stdio: 'inherit',
124
+ });
125
+ });
126
+
127
+ describe('with "scope"', () => {
128
+ beforeEach(() => (scope = ['foo']));
129
+
130
+ itRunsTsc('with only specified package', {
131
+ tsConfigs: ['foo/tsconfig.json'],
132
+ stdio: 'inherit',
133
+ });
134
+ });
135
+
136
+ describe('with "ignore"', () => {
137
+ beforeEach(() => (ignore = ['foo']));
138
+
139
+ itRunsTsc('without specified package', {
140
+ tsConfigs: ['baz/tsconfig.json', 'bar/tsconfig.json'],
141
+ stdio: 'inherit',
142
+ });
143
+ });
144
+
145
+ describe('with both "scope" and "ignore"', () => {
146
+ beforeEach(() => {
147
+ scope = ['ba*'];
148
+ ignore = ['bar'];
149
+ });
150
+
151
+ itRunsTsc('with combined filters', {
152
+ tsConfigs: ['baz/tsconfig.json'],
153
+ stdio: 'inherit',
154
+ });
155
+ });
156
+ });
@@ -0,0 +1,64 @@
1
+ import execa from 'execa';
2
+ import { Task, TaskParameters } from './task';
3
+
4
+ export interface Indicators {
5
+ end: string;
6
+ watchStart: string;
7
+ watchEnd?: string;
8
+ }
9
+
10
+ export interface CliTaskParameters extends TaskParameters {
11
+ indicators: Indicators;
12
+ }
13
+
14
+ export abstract class CliTask extends Task {
15
+ private readonly indicators: Indicators;
16
+
17
+ constructor({ indicators, ...rest }: CliTaskParameters) {
18
+ super(rest);
19
+ this.indicators = indicators;
20
+ }
21
+
22
+ protected async runChildProcess(command: string, args: string[]) {
23
+ const childProcess = execa(
24
+ command,
25
+ args,
26
+ this.watch
27
+ ? {
28
+ stdio: ['inherit', 'pipe', 'inherit'],
29
+ }
30
+ : {
31
+ stdio: 'inherit',
32
+ }
33
+ );
34
+
35
+ if (!this.watch) {
36
+ try {
37
+ await childProcess;
38
+ } finally {
39
+ this.logCompletionResults(this.taskTimer.end());
40
+ }
41
+ return;
42
+ }
43
+
44
+ childProcess.stdout!.on('data', this.stdoutDataHandler);
45
+ await childProcess;
46
+ }
47
+
48
+ private readonly stdoutDataHandler = (data: Uint8Array | string) => {
49
+ process.stdout.write(data);
50
+
51
+ const dataStr = data.toString();
52
+
53
+ if (dataStr.includes(this.indicators.watchStart)) {
54
+ this.taskTimer.start();
55
+ }
56
+
57
+ if (
58
+ dataStr.includes(this.indicators.end) ||
59
+ (this.indicators.watchEnd && dataStr.includes(this.indicators.watchEnd))
60
+ ) {
61
+ this.logCompletionResults(this.taskTimer.end());
62
+ }
63
+ };
64
+ }
@@ -0,0 +1,3 @@
1
+ declare module '@swc/cli' {
2
+ function swcDir(options: any): void;
3
+ }
@@ -0,0 +1,70 @@
1
+ import { swcDir } from '@swc/cli';
2
+ import { compilerOptions as baseCompilerOptions } from '../../../tsconfig/base.json';
3
+
4
+ import { Task } from './task';
5
+ import { getFolders, log, readJson } from '../../utils';
6
+
7
+ interface Args {
8
+ [key: string]: unknown;
9
+ watch?: boolean;
10
+ }
11
+
12
+ export class SwcCompilePackage extends Task {
13
+ constructor({ watch }: Args) {
14
+ super({ name: 'swc-compile-package', global: false, watch });
15
+ }
16
+
17
+ description() {
18
+ return 'Compiles TypeScript files to JavaScript with SWC within package folder';
19
+ }
20
+
21
+ async execute(): Promise<void> {
22
+ const { source, destination } = getFolders();
23
+ const compilerOptions = readJson('tsconfig.json').compilerOptions ?? {};
24
+
25
+ return new Promise((resolve, reject) => {
26
+ swcDir({
27
+ cliOptions: {
28
+ watch: this.watch,
29
+ outDir: destination,
30
+ stripLeadingPaths: true,
31
+ extensions: ['.ts', '.tsx'],
32
+ filenames: [source],
33
+ },
34
+ swcOptions: {
35
+ jsc: {
36
+ parser: {
37
+ syntax: 'typescript',
38
+ tsx: true,
39
+ decorators: true,
40
+ },
41
+ target: baseCompilerOptions.target,
42
+ transform: {
43
+ legacyDecorator: baseCompilerOptions.experimentalDecorators,
44
+ decoratorMetadata: baseCompilerOptions.emitDecoratorMetadata,
45
+ },
46
+ },
47
+ sourceMaps: baseCompilerOptions.sourceMap,
48
+ module: {
49
+ type: compilerOptions.module === 'commonjs' ? 'commonjs' : 'es6',
50
+ },
51
+ },
52
+ callbacks: {
53
+ onSuccess: (e: { duration: number }) => {
54
+ this.logCompletionResults(this.taskTimer.add(e.duration));
55
+ if (!this.watch) {
56
+ resolve();
57
+ }
58
+ },
59
+ onFail: (e: { duration: number; reasons: Map<string, string> }) => {
60
+ log.text([...e.reasons.values()][0]);
61
+ this.logCompletionResults(this.taskTimer.add(e.duration));
62
+ if (!this.watch) {
63
+ reject(new Error('Compilation failed'));
64
+ }
65
+ },
66
+ },
67
+ });
68
+ });
69
+ }
70
+ }
@@ -0,0 +1,112 @@
1
+ import { log, readJsonSafe } from '../../utils';
2
+
3
+ class TaskTimer {
4
+ private counter = 0;
5
+
6
+ private get currentMarkName() {
7
+ return `${this.name}[${this.counter}]`;
8
+ }
9
+
10
+ private get currentMarkList() {
11
+ return globalThis.performance.getEntriesByName(this.currentMarkName, 'mark');
12
+ }
13
+
14
+ constructor(private readonly name: string) {
15
+ this.start();
16
+ }
17
+
18
+ start() {
19
+ if (this.currentMarkList.length > 0) {
20
+ this.cancel();
21
+ }
22
+
23
+ globalThis.performance.mark(this.currentMarkName);
24
+ }
25
+
26
+ end() {
27
+ if (this.currentMarkList.length === 0) {
28
+ throw new Error(`There are no tasks in progress for ${this.currentMarkName}`);
29
+ }
30
+
31
+ const measure = globalThis.performance.measure(this.currentMarkName, this.currentMarkName);
32
+ return { measure, counter: this.counter++ };
33
+ }
34
+
35
+ cancel() {
36
+ if (this.currentMarkList.length === 0) {
37
+ throw new Error(`There are no tasks in progress for ${this.currentMarkName}`);
38
+ }
39
+
40
+ const measure = globalThis.performance.measure(this.currentMarkName, {
41
+ start: this.currentMarkName,
42
+ detail: {
43
+ status: 'cancelled',
44
+ },
45
+ });
46
+
47
+ return { measure, counter: this.counter++ };
48
+ }
49
+
50
+ add(duration: number) {
51
+ const measure = globalThis.performance.measure(this.currentMarkName, {
52
+ duration,
53
+ end: globalThis.performance.now(),
54
+ });
55
+ return { measure, counter: this.counter++ };
56
+ }
57
+ }
58
+
59
+ export interface TaskParameters {
60
+ name: string;
61
+ watch?: boolean;
62
+ global: boolean;
63
+ }
64
+
65
+ export abstract class Task {
66
+ protected readonly watch: boolean;
67
+ protected readonly taskTimer: TaskTimer;
68
+ private readonly name: string;
69
+ private readonly global: boolean;
70
+
71
+ constructor({ name, watch, global }: TaskParameters) {
72
+ this.name = name;
73
+ this.watch = !!watch;
74
+ this.global = global;
75
+ this.taskTimer = new TaskTimer(name);
76
+ this.checkRunLocation();
77
+ }
78
+
79
+ protected logCompletionResults({
80
+ measure,
81
+ counter,
82
+ }: {
83
+ measure: PerformanceMeasure;
84
+ counter: number;
85
+ }) {
86
+ const enumerator = counter === 0 ? 'Initial ' : 'Subsequent ';
87
+ log.info(
88
+ `${this.watch ? enumerator : ''}${this.name} task completed in ${this.formatDuration(measure.duration)}`
89
+ );
90
+ }
91
+
92
+ private formatDuration(duration: number) {
93
+ if (duration < 1000) {
94
+ return `${duration.toFixed(0)}ms`;
95
+ }
96
+ return `${(duration / 1000).toFixed(2)}s`;
97
+ }
98
+
99
+ private checkRunLocation() {
100
+ const packageJson = readJsonSafe('package.json');
101
+ if (!packageJson?.workspaces && this.global) {
102
+ throw new Error(`Task ${this.name} has to run inside the workspace root directory`);
103
+ }
104
+ if ((!packageJson || packageJson.workspaces) && !this.global) {
105
+ throw new Error(`Task ${this.name} has to run inside the package directory`);
106
+ }
107
+ }
108
+
109
+ abstract description(): string;
110
+
111
+ abstract execute(): Promise<void>;
112
+ }
@@ -0,0 +1,47 @@
1
+ import { CliTask } from './cli-task';
2
+ import { getTsConfig } from '../../utils';
3
+ interface Args {
4
+ [key: string]: unknown;
5
+ watch?: boolean;
6
+ }
7
+
8
+ export class TscCompilePackage extends CliTask {
9
+ constructor({ watch }: Args) {
10
+ super({
11
+ name: 'tsc-compile-package',
12
+ global: false,
13
+ indicators: {
14
+ end: 'Watching for file changes.',
15
+ watchStart: 'Starting incremental compilation.',
16
+ },
17
+ watch,
18
+ });
19
+ }
20
+
21
+ description() {
22
+ return 'Compiles TypeScript files to JavaScript with TSC within package folder';
23
+ }
24
+
25
+ async execute() {
26
+ const tsConfig = getTsConfig();
27
+ await this.runChildProcess(
28
+ 'tsc',
29
+ [
30
+ this.watch ? '-w' : undefined,
31
+ '--pretty',
32
+ '--preserveWatchOutput',
33
+ '--noCheck',
34
+ '--composite',
35
+ 'false',
36
+ '--declaration',
37
+ 'false',
38
+ '--declarationMap',
39
+ 'false',
40
+ '--tsBuildInfoFile',
41
+ 'tsconfig.compile.tsbuildinfo',
42
+ '--project',
43
+ tsConfig,
44
+ ].filter(i => i !== undefined)
45
+ );
46
+ }
47
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ getPackages,
3
+ getPackagesGraph,
4
+ getTsConfig,
5
+ Package,
6
+ PackageType,
7
+ splitPackagesByType,
8
+ } from '../../utils';
9
+ import { CliTask } from './cli-task';
10
+
11
+ interface Args {
12
+ [key: string]: unknown;
13
+ ignore?: string | string[];
14
+ scope?: string | string[];
15
+ watch?: boolean;
16
+ typeCheckOnly?: boolean;
17
+ }
18
+
19
+ export class TscCompile extends CliTask {
20
+ constructor(private readonly args: Args) {
21
+ super({
22
+ name: 'tsc-compile',
23
+ watch: args.watch,
24
+ global: true,
25
+ indicators: {
26
+ end: 'Watching for file changes.',
27
+ watchStart: 'Starting incremental compilation.',
28
+ },
29
+ });
30
+ }
31
+
32
+ description() {
33
+ return 'Compiles one or more TypeScript projects with optional type checking';
34
+ }
35
+
36
+ async execute() {
37
+ const packages = splitPackagesByType(
38
+ getPackages({ scope: this.args.scope, ignore: this.args.ignore })
39
+ )[PackageType.TSC];
40
+ const tsConfigs = collapsePackages(packages).map(({ location }) => getTsConfig(location));
41
+ await this.runChildProcess(
42
+ 'tsc',
43
+ [
44
+ '-b',
45
+ this.watch ? '-w' : undefined,
46
+ '--pretty',
47
+ '--preserveWatchOutput',
48
+ this.args.typeCheckOnly ? '--emitDeclarationOnly' : undefined,
49
+ ...tsConfigs,
50
+ ].filter(i => i !== undefined)
51
+ );
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Exclude dependant packages as they will be built through project references
57
+ */
58
+ function collapsePackages(packages: Package[]) {
59
+ const dependencies = new Set(
60
+ Object.values(getPackagesGraph({ scope: packages.map(({ name }) => name) })).flat()
61
+ );
62
+
63
+ return packages.filter(({ name }) => !dependencies.has(name));
64
+ }
@@ -38,7 +38,7 @@ describe('[startup] Cli Utils (Assets)', () => {
38
38
  test('copies assets from source to destination in watch mode', async () => {
39
39
  try {
40
40
  await subject();
41
- } catch (error: any) {
41
+ } catch {
42
42
  // ignore rejected promise that terminates watch()
43
43
  }
44
44
 
@@ -106,7 +106,7 @@ describe('[startup] Cli Utils', () => {
106
106
 
107
107
  test('returns errors', () => {
108
108
  const toStringSpy = jest.spyOn(stats, 'toString');
109
- expect(subject).rejects.toBe(stats.toString());
109
+ expect(subject).rejects.toThrow(stats.toString());
110
110
  expect(toStringSpy).toHaveBeenCalledWith('errors-only');
111
111
  });
112
112
  });
@@ -1,11 +1,10 @@
1
1
  import { runCommand, runCommandOutput } from '../cli-os';
2
2
 
3
3
  import {
4
- npmGetPackageVersionDates,
5
4
  npmGetPackageVersions,
5
+ npmGetPackageVersionsDetails,
6
6
  npmPackageSet,
7
7
  npmPublish,
8
- npmPublishDry,
9
8
  npmTagVersion,
10
9
  npmUnpublish,
11
10
  } from '../cli-npm';
@@ -48,7 +47,7 @@ describe('[startup] Cli Utils (Npm)', () => {
48
47
  });
49
48
  }
50
49
 
51
- describe(`${npmGetPackageVersions.name}`, () => {
50
+ describe(npmGetPackageVersions.name, () => {
52
51
  let args: Parameters<typeof npmGetPackageVersions>;
53
52
 
54
53
  beforeEach(() => (args = ['{registry}', '{packageName}']));
@@ -82,30 +81,42 @@ describe('[startup] Cli Utils (Npm)', () => {
82
81
  });
83
82
  });
84
83
 
85
- describe(`${npmGetPackageVersionDates.name}`, () => {
86
- let args: Parameters<typeof npmGetPackageVersionDates>;
84
+ describe(npmGetPackageVersionsDetails.name, () => {
85
+ let args: Parameters<typeof npmGetPackageVersionsDetails>;
87
86
 
88
- beforeEach(() => (args = ['{registry}', '{packageName}']));
87
+ beforeEach(() => {
88
+ jest.useFakeTimers();
89
+ args = ['{registry}', '{packageName}'];
90
+ });
89
91
 
90
- const subject = () => npmGetPackageVersionDates(...args);
92
+ afterEach(() => jest.useRealTimers());
91
93
 
92
- itRunsCommandOutput(subject, 'npm view {packageName} time --registry {registry} --json', {
93
- timeout: 10000,
94
- });
94
+ const subject = () => npmGetPackageVersionsDetails(...args);
95
+
96
+ itRunsCommandOutput(
97
+ subject,
98
+ 'npm view {packageName} time dist-tags --registry {registry} --json',
99
+ { timeout: 10000 }
100
+ );
95
101
 
96
- test('converts results to Array<[string, Date]>', () => {
102
+ test('converts results to Version[]', () => {
97
103
  function dayAgo(count: number) {
98
104
  const date = new Date();
99
105
  date.setDate(-count);
100
106
  return date;
101
107
  }
102
- const result = { '1.0.1': dayAgo(3), '2.1.0': dayAgo(2), '3.0.0': dayAgo(1) };
108
+ const result = {
109
+ 'time': { '1.0.1': dayAgo(3), '2.1.0': dayAgo(2), '3.0.0': dayAgo(1) },
110
+ 'dist-tags': { latest: '3.0.0' },
111
+ };
103
112
 
104
113
  jest.mocked(runCommandOutput).mockReturnValue(JSON.stringify(result));
105
114
 
106
- expect(subject()).toEqual(
107
- Object.entries(result).map(([version, date]) => [version, date])
108
- );
115
+ expect(subject()).toEqual([
116
+ { name: '1.0.1', date: dayAgo(3) },
117
+ { name: '2.1.0', date: dayAgo(2) },
118
+ { name: '3.0.0', date: dayAgo(1), tag: 'latest' },
119
+ ]);
109
120
  });
110
121
 
111
122
  describe('when an error occurs', () => {
@@ -121,7 +132,7 @@ describe('[startup] Cli Utils (Npm)', () => {
121
132
  });
122
133
  });
123
134
 
124
- describe(`${npmPackageSet.name}`, () => {
135
+ describe(npmPackageSet.name, () => {
125
136
  let args: Parameters<typeof npmPackageSet>;
126
137
 
127
138
  beforeEach(() => (args = ['{key}', '{value}']));
@@ -131,27 +142,29 @@ describe('[startup] Cli Utils (Npm)', () => {
131
142
  itRunsCommand(subject, 'npm pkg set {key}={value}');
132
143
  });
133
144
 
134
- describe(`${npmPublish.name}`, () => {
135
- let args: Parameters<typeof npmPublish>;
145
+ describe(npmPublish.name, () => {
146
+ let options: Parameters<typeof npmPublish>[0];
136
147
 
137
- beforeEach(() => (args = []));
148
+ beforeEach(() => (options = {}));
138
149
 
139
- const subject = async () => npmPublish(...args);
150
+ const subject = async () => npmPublish(options);
140
151
 
141
152
  itRunsCommand(subject, 'npm publish');
142
153
 
143
154
  describe('with a tag', () => {
144
- beforeEach(() => args.push('{tag}'));
155
+ beforeEach(() => (options.tag = '{tag}'));
145
156
 
146
157
  itRunsCommand(subject, 'npm publish --tag {tag}');
147
158
  });
148
- });
149
159
 
150
- describe(`${npmPublishDry.name}`, () => {
151
- itRunsCommand(npmPublishDry, 'npm publish --dry-run');
160
+ describe('with {dry: true}', () => {
161
+ beforeEach(() => (options.dry = true));
162
+
163
+ itRunsCommand(subject, 'npm publish --dry-run');
164
+ });
152
165
  });
153
166
 
154
- describe(`${npmUnpublish.name}`, () => {
167
+ describe(npmUnpublish.name, () => {
155
168
  let args: Parameters<typeof npmUnpublish>;
156
169
 
157
170
  beforeEach(() => (args = ['{registry}', '{packageName}', '{packageVersion}']));
@@ -165,7 +178,7 @@ describe('[startup] Cli Utils (Npm)', () => {
165
178
  );
166
179
  });
167
180
 
168
- describe(`${npmTagVersion.name}`, () => {
181
+ describe(npmTagVersion.name, () => {
169
182
  let args: Parameters<typeof npmTagVersion>[0];
170
183
 
171
184
  beforeEach(() => {
@@ -57,13 +57,13 @@ describe('[startup] Cli Utils (OS)', () => {
57
57
  beforeEach(() => (exitCode = 2));
58
58
 
59
59
  test('rejects promise', () => {
60
- expect(subject('foo')).rejects.toBeUndefined();
60
+ expect(subject('foo')).rejects.toThrow(`command exited with code: ${exitCode}`);
61
61
  });
62
62
  });
63
63
 
64
64
  ['', [], ['']].forEach(command =>
65
65
  test(`rejects promise when called with ${JSON.stringify(command)}`, () => {
66
- expect(subject(command)).rejects.toBeUndefined();
66
+ expect(subject(command)).rejects.toThrow('invalid command');
67
67
  })
68
68
  );
69
69