@servicetitan/startup 22.14.0 → 22.16.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 (112) hide show
  1. package/dist/cli/__mocks__/create-package.d.ts +3 -0
  2. package/dist/cli/__mocks__/create-package.d.ts.map +1 -0
  3. package/dist/cli/__mocks__/create-package.js +9 -0
  4. package/dist/cli/__mocks__/create-package.js.map +1 -0
  5. package/dist/cli/__mocks__/index.d.ts +2 -0
  6. package/dist/cli/__mocks__/index.d.ts.map +1 -0
  7. package/dist/cli/__mocks__/index.js +18 -0
  8. package/dist/cli/__mocks__/index.js.map +1 -0
  9. package/dist/cli/commands/eslint.d.ts +12 -0
  10. package/dist/cli/commands/eslint.d.ts.map +1 -0
  11. package/dist/cli/commands/eslint.js +47 -0
  12. package/dist/cli/commands/eslint.js.map +1 -0
  13. package/dist/cli/commands/index.d.ts +1 -0
  14. package/dist/cli/commands/index.d.ts.map +1 -1
  15. package/dist/cli/commands/index.js +1 -0
  16. package/dist/cli/commands/index.js.map +1 -1
  17. package/dist/cli/commands/lint.d.ts +5 -0
  18. package/dist/cli/commands/lint.d.ts.map +1 -1
  19. package/dist/cli/commands/lint.js +22 -30
  20. package/dist/cli/commands/lint.js.map +1 -1
  21. package/dist/cli/index.js +20 -6
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/utils/eslint.d.ts +7 -0
  24. package/dist/cli/utils/eslint.d.ts.map +1 -0
  25. package/dist/cli/utils/eslint.js +57 -0
  26. package/dist/cli/utils/eslint.js.map +1 -0
  27. package/dist/cli/utils/index.d.ts +2 -0
  28. package/dist/cli/utils/index.d.ts.map +1 -1
  29. package/dist/cli/utils/index.js +2 -0
  30. package/dist/cli/utils/index.js.map +1 -1
  31. package/dist/cli/utils/set-node-options.d.ts +7 -0
  32. package/dist/cli/utils/set-node-options.d.ts.map +1 -0
  33. package/dist/cli/utils/set-node-options.js +46 -0
  34. package/dist/cli/utils/set-node-options.js.map +1 -0
  35. package/dist/cli/utils/tcm.d.ts.map +1 -1
  36. package/dist/cli/utils/tcm.js +3 -1
  37. package/dist/cli/utils/tcm.js.map +1 -1
  38. package/dist/utils/get-configuration.d.ts +5 -1
  39. package/dist/utils/get-configuration.d.ts.map +1 -1
  40. package/dist/utils/get-configuration.js +2 -2
  41. package/dist/utils/get-configuration.js.map +1 -1
  42. package/dist/utils/get-jest-config.d.ts +1 -1
  43. package/dist/utils/get-jest-config.d.ts.map +1 -1
  44. package/dist/utils/get-jest-config.js +8 -6
  45. package/dist/utils/get-jest-config.js.map +1 -1
  46. package/dist/utils/get-package-data.js +3 -3
  47. package/dist/utils/get-package-data.js.map +1 -1
  48. package/dist/utils/get-package-name.d.ts.map +1 -1
  49. package/dist/utils/get-package-name.js +2 -2
  50. package/dist/utils/get-package-name.js.map +1 -1
  51. package/dist/utils/get-packages.js +8 -7
  52. package/dist/utils/get-packages.js.map +1 -1
  53. package/dist/webpack/shared.config.d.ts.map +1 -1
  54. package/dist/webpack/shared.config.js +1 -0
  55. package/dist/webpack/shared.config.js.map +1 -1
  56. package/package.json +4 -4
  57. package/src/cli/__mocks__/create-package.ts +12 -0
  58. package/src/cli/__mocks__/index.ts +1 -0
  59. package/src/cli/commands/__tests__/build.test.ts +135 -0
  60. package/src/cli/commands/__tests__/bundle-package.test.ts +72 -0
  61. package/src/cli/commands/__tests__/eslint.test.ts +19 -0
  62. package/src/cli/commands/__tests__/init.test.ts +42 -0
  63. package/src/cli/commands/__tests__/lint.test.ts +114 -0
  64. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +193 -0
  65. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +192 -0
  66. package/src/cli/commands/__tests__/mfe-publish.test.ts +172 -0
  67. package/src/cli/commands/__tests__/prepare-package.test.ts +75 -0
  68. package/src/cli/commands/__tests__/start.test.ts +111 -0
  69. package/src/cli/commands/__tests__/styles-check.test.ts +119 -0
  70. package/src/cli/commands/__tests__/tests.test.ts +43 -0
  71. package/src/cli/commands/eslint.ts +19 -0
  72. package/src/cli/commands/index.ts +1 -0
  73. package/src/cli/commands/lint.ts +29 -42
  74. package/src/cli/index.ts +17 -5
  75. package/src/cli/utils/__tests__/assets-copy.test.ts +48 -52
  76. package/src/cli/utils/__tests__/bundle.test.ts +361 -0
  77. package/src/cli/utils/__tests__/cli-git.test.ts +28 -0
  78. package/src/cli/utils/__tests__/cli-npm.test.ts +166 -0
  79. package/src/cli/utils/__tests__/cli-os.test.ts +103 -0
  80. package/src/cli/utils/__tests__/eslint.test.ts +91 -0
  81. package/src/cli/utils/__tests__/get-module-type.test.ts +56 -0
  82. package/src/cli/utils/__tests__/is-module-installed.test.ts +24 -0
  83. package/src/cli/utils/__tests__/set-node-options.test.ts +131 -0
  84. package/src/cli/utils/__tests__/styles-copy.test.ts +48 -44
  85. package/src/cli/utils/__tests__/tcm.test.ts +101 -192
  86. package/src/cli/utils/__tests__/tsc.test.ts +85 -0
  87. package/src/cli/utils/eslint.ts +50 -0
  88. package/src/cli/utils/index.ts +2 -0
  89. package/src/cli/utils/set-node-options.ts +45 -0
  90. package/src/cli/utils/tcm.ts +3 -1
  91. package/src/utils/__tests__/get-configuration.test.ts +215 -16
  92. package/src/utils/__tests__/get-destination-folders.test.ts +65 -0
  93. package/src/utils/__tests__/get-jest-config.test.ts +134 -0
  94. package/src/utils/__tests__/get-package-data.test.ts +90 -0
  95. package/src/utils/__tests__/get-packages.test.ts +165 -0
  96. package/src/utils/__tests__/get-tsconfig.test.ts +48 -0
  97. package/src/utils/__tests__/log.test.ts +123 -0
  98. package/src/utils/__tests__/to-array.test.ts +19 -0
  99. package/src/utils/get-configuration.ts +8 -3
  100. package/src/utils/get-jest-config.ts +3 -1
  101. package/src/utils/get-package-data.ts +1 -1
  102. package/src/utils/get-package-name.ts +1 -2
  103. package/src/utils/get-packages.ts +2 -2
  104. package/src/webpack/shared.config.ts +1 -0
  105. package/template/package.json +8 -1
  106. package/template/packages/application/package.json +5 -0
  107. package/template/packages/application/src/__tests__/app.test.tsx +33 -0
  108. package/template/packages/application/src/app.tsx +9 -6
  109. package/template/packages/application/src/main-page.tsx +5 -0
  110. package/template/packages/application/src/second-page.tsx +5 -0
  111. package/template/setupTests.ts +27 -0
  112. package/template/tsconfig.test.json +1 -1
@@ -1,226 +1,135 @@
1
- jest.mock('../../../utils/get-folders');
2
-
1
+ import chokidar from 'chokidar';
3
2
  import fs from 'fs';
4
- import mock from 'mock-fs';
5
- import { mocked } from 'ts-jest/utils';
3
+ import mockFS from 'mock-fs';
6
4
 
7
- import { getFolders } from '../../../utils';
8
- import { tcm } from '..';
5
+ import { getFolders, log } from '../../../utils';
6
+ import * as sassModule from '../compile-sass';
9
7
 
10
- describe('[Startup] utils:tcm', () => {
11
- beforeAll(() => {
12
- mocked(getFolders).mockReturnValue({ source: 'src', destination: 'dist' });
13
- });
8
+ import { styleExtensions } from '..';
9
+ import { tcm, tcmWatch } from '../tcm';
10
+
11
+ jest.mock('../../../utils', () => ({
12
+ ...jest.requireActual('../../../utils'),
13
+ getFolders: jest.fn(),
14
+ log: { error: jest.fn(), info: jest.fn() }, // suppress test output
15
+ }));
14
16
 
15
- afterAll(() => {
16
- mock.restore();
17
+ describe('[startup] Cli Utils', () => {
18
+ const source = 'src';
19
+
20
+ beforeEach(() => {
21
+ jest.resetAllMocks();
22
+ jest.mocked(getFolders).mockReturnValue({ source, destination: undefined });
17
23
  });
18
24
 
19
- xtest('tcm ignores global selector for css modules', async () => {
20
- mock({
21
- src: {
22
- 'styles.module.css': `
23
- .a :global(.b) .c {
24
- display: none;
25
- }
26
- `,
27
- },
28
- });
25
+ afterEach(() => mockFS.restore());
29
26
 
30
- await tcm();
27
+ function mockStylesModule(module: string, id = 'foo') {
28
+ mockFS({ [source]: { [module]: `.${id} { display: none; }` } });
29
+ }
31
30
 
32
- expect(fs.readFileSync('./src/styles.module.css.d.ts', 'utf8')).toContain(
33
- 'export const a: string;'
34
- );
35
- expect(fs.readFileSync('./src/styles.module.css.d.ts', 'utf8')).not.toContain(
36
- 'export const b: string;'
31
+ function expectTypeDefinitions(module: string, id = 'foo') {
32
+ expect(fs.readFileSync(`${source}/${module}.d.ts`, 'utf8')).toContain(
33
+ `export const ${id}: string;`
37
34
  );
38
- expect(fs.readFileSync('./src/styles.module.css.d.ts', 'utf8')).toContain(
39
- 'export const c: string;'
40
- );
41
- });
35
+ }
42
36
 
43
- test('tcm ignores global selector for sass modules', async () => {
44
- mock({
45
- src: {
46
- 'styles.module.scss': `
47
- .a :global(.b) .c {
48
- display: none;
49
- }
50
- `,
51
- },
52
- });
37
+ describe(`${tcm.name}`, () => {
38
+ const subject = async () => tcm();
53
39
 
54
- await tcm();
40
+ describe.each(styleExtensions)('with %s module', extension => {
41
+ const module = `foo.module.${extension}`;
55
42
 
56
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).toContain(
57
- 'export const a: string;'
58
- );
59
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).not.toContain(
60
- 'export const b: string;'
61
- );
62
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).toContain(
63
- 'export const c: string;'
64
- );
65
- });
43
+ beforeEach(() => mockStylesModule(module));
44
+
45
+ test(`generates Typescript definitions for ${extension} module`, async () => {
46
+ await subject();
66
47
 
67
- test('tcm ignores global selector for less modules', async () => {
68
- mock({
69
- src: {
70
- 'styles.module.less': `
71
- .a :global(.b) .c {
72
- display: none;
73
- }
74
- `,
75
- },
48
+ expectTypeDefinitions(module);
49
+ });
76
50
  });
77
51
 
78
- await tcm();
52
+ describe('when an error occurs', () => {
53
+ beforeEach(() => {
54
+ mockStylesModule('foo.module.scss');
55
+ jest.spyOn(sassModule, 'compileSass').mockImplementation(() => {
56
+ throw new Error('Oops!');
57
+ });
58
+ });
79
59
 
80
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).toContain(
81
- 'export const a: string;'
82
- );
83
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).not.toContain(
84
- 'export const b: string;'
85
- );
86
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).toContain(
87
- 'export const c: string;'
88
- );
89
- });
60
+ test('logs error', async () => {
61
+ await subject();
90
62
 
91
- test('tcm handles nested selectors for sass modules', async () => {
92
- mock({
93
- src: {
94
- 'styles.module.scss': `
95
- .a :global(.b) {
96
- .c {
97
- display: none;
98
- }
99
- }
100
- `,
101
- },
63
+ expect(log.error).toHaveBeenCalledWith(expect.stringContaining('Oops!'));
64
+ });
102
65
  });
66
+ });
103
67
 
104
- await tcm();
68
+ describe(`${tcmWatch.name}`, () => {
69
+ type Listener = (path: string) => Promise<void>;
70
+ let watchSpy: jest.SpyInstance;
71
+ let fsWatcher: Partial<ReturnType<typeof chokidar.watch>>;
105
72
 
106
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).toContain(
107
- 'export const a: string;'
108
- );
109
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).not.toContain(
110
- 'export const b: string;'
111
- );
112
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).toContain(
113
- 'export const c: string;'
114
- );
115
- });
73
+ beforeAll(() => {
74
+ process.env.NODE_ENV ??= 'test'; // workaround issue with Jest not setting NODE_ENV=test
75
+ });
116
76
 
117
- test('tcm handles nested selectors for less. modules', async () => {
118
- mock({
119
- src: {
120
- 'styles.module.less': `
121
- .a :global(.b) {
122
- .c {
123
- display: none;
124
- }
125
- }
126
- `,
127
- },
77
+ beforeEach(() => {
78
+ fsWatcher = { on: jest.fn() };
79
+ watchSpy = jest.spyOn(chokidar, 'watch').mockReturnValue(fsWatcher as any);
80
+ mockFS({ [source]: {} });
128
81
  });
129
82
 
130
- await tcm();
83
+ const subject = () => tcmWatch();
131
84
 
132
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).toContain(
133
- 'export const a: string;'
134
- );
135
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).not.toContain(
136
- 'export const b: string;'
137
- );
138
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).toContain(
139
- 'export const c: string;'
140
- );
141
- });
85
+ test('watches for new and changed style modules', async () => {
86
+ await subject();
142
87
 
143
- xtest('tcm handles imports for sass modules', async () => {
144
- mock({
145
- // eslint-disable-next-line @typescript-eslint/naming-convention
146
- node_modules: {
147
- package: {
148
- 'shared.scss': `
149
- .a {
150
- display: none;
151
- }
152
- `,
153
- },
154
- },
155
- src: {
156
- 'styles.scss': `
157
- .b {
158
- display: none;
159
- }
160
- `,
161
- 'styles.module.scss': `
162
- @import 'package/shared.scss';
163
- @import './styles.scss';
164
-
165
- .c {
166
- display: none;
167
- }
168
- `,
169
- },
88
+ expect(watchSpy).toHaveBeenCalledWith(
89
+ [`${source}/**/*.module.{${styleExtensions.join()}}`],
90
+ { ignoreInitial: true }
91
+ );
92
+ expect(fsWatcher.on).toHaveBeenCalledWith('add', expect.any(Function));
93
+ expect(fsWatcher.on).toHaveBeenCalledWith('change', expect.any(Function));
170
94
  });
171
95
 
172
- await tcm();
96
+ function getListener(name: string) {
97
+ const { calls } = jest.mocked(fsWatcher.on)!.mock;
98
+ return calls.find(([event]) => event === name)![1] as Listener;
99
+ }
173
100
 
174
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).toContain(
175
- 'export const a: string;'
176
- );
177
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).toContain(
178
- 'export const b: string;'
179
- );
180
- expect(fs.readFileSync('./src/styles.module.scss.d.ts', 'utf8')).toContain(
181
- 'export const c: string;'
182
- );
183
- });
101
+ describe('when module is added', () => {
102
+ const module = 'bar.module.scss';
103
+
104
+ const setup = async () => {
105
+ await subject();
106
+ mockStylesModule(module, 'bar');
107
+ await getListener('add')(`${source}/${module}`);
108
+ };
109
+
110
+ test('generates Typescript definitions for new module', async () => {
111
+ await setup();
184
112
 
185
- xtest('tcm handles imports for less modules', async () => {
186
- mock({
187
- // eslint-disable-next-line @typescript-eslint/naming-convention
188
- node_modules: {
189
- package: {
190
- 'shared.less': `
191
- .a {
192
- display: none;
193
- }
194
- `,
195
- },
196
- },
197
- src: {
198
- 'styles.less': `
199
- .b {
200
- display: none;
201
- }
202
- `,
203
- 'styles.module.less': `
204
- @import 'package/shared.less';
205
- @import './styles.less';
206
-
207
- .c {
208
- display: none;
209
- }
210
- `,
211
- },
113
+ expectTypeDefinitions(module, 'bar');
114
+ });
212
115
  });
213
116
 
214
- await tcm();
117
+ describe('when module is changed', () => {
118
+ const module = 'baz.module.less';
215
119
 
216
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).toContain(
217
- 'export const a: string;'
218
- );
219
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).toContain(
220
- 'export const b: string;'
221
- );
222
- expect(fs.readFileSync('./src/styles.module.less.d.ts', 'utf8')).toContain(
223
- 'export const c: string;'
224
- );
120
+ const setup = async () => {
121
+ mockStylesModule(module, 'foo');
122
+ await subject();
123
+
124
+ mockStylesModule(module, 'baz');
125
+ await getListener('change')(`${source}/${module}`);
126
+ };
127
+
128
+ test('generates Typescript definitions for changed module', async () => {
129
+ await setup();
130
+
131
+ expectTypeDefinitions(module, 'baz');
132
+ });
133
+ });
225
134
  });
226
135
  });
@@ -0,0 +1,85 @@
1
+ import execa from 'execa';
2
+ import mockFS from 'mock-fs';
3
+ import { Package, getPackagesGraph, getTsConfig } from '../../../utils';
4
+ import { createPackage } from '../../__mocks__';
5
+
6
+ import { tsc, tscWatch } from '../tsc';
7
+
8
+ jest.mock('execa', () => jest.fn());
9
+ jest.mock('../../../utils', () => ({
10
+ ...jest.requireActual('../../../utils'),
11
+ log: { info: jest.fn() }, // suppress test output
12
+ getPackagesGraph: jest.fn(),
13
+ }));
14
+
15
+ describe('[startup] Cli Utils', () => {
16
+ let packages: Package[];
17
+
18
+ beforeEach(() => {
19
+ jest.resetAllMocks();
20
+ jest.mocked(getPackagesGraph).mockReturnValue({});
21
+ packages = [
22
+ createPackage({ name: 'foo', location: 'packages/foo' }),
23
+ createPackage({ name: 'bar', location: 'packages/bar' }),
24
+ ];
25
+ mockFS({
26
+ packages: {
27
+ foo: { 'tsconfig.json': JSON.stringify({}) },
28
+ bar: { 'tsconfig.json': JSON.stringify({}) },
29
+ },
30
+ });
31
+ });
32
+
33
+ afterEach(() => mockFS.restore());
34
+
35
+ describe(`${tsc.name}`, () => {
36
+ const subject = async () => tsc(packages);
37
+
38
+ test('transpiles packages', async () => {
39
+ await subject();
40
+
41
+ expect(execa).toHaveBeenCalledWith(
42
+ 'tsc',
43
+ ['-b', ...packages.map(({ location }) => getTsConfig(location))],
44
+ { stdio: 'inherit' }
45
+ );
46
+ });
47
+
48
+ describe('when package is dependency of another', () => {
49
+ beforeEach(() => {
50
+ jest.mocked(getPackagesGraph).mockReturnValue({
51
+ [packages[0].name]: [packages[1].name], // packages[0] depends on packages[1]
52
+ });
53
+ });
54
+
55
+ test('transpiles only parent packages', async () => {
56
+ await subject();
57
+
58
+ expect(execa).toHaveBeenCalledWith(
59
+ expect.anything(),
60
+ ['-b', getTsConfig(packages[0].location)],
61
+ expect.anything()
62
+ );
63
+ });
64
+ });
65
+ });
66
+
67
+ describe(`${tscWatch.name}`, () => {
68
+ const subject = async () => tscWatch(packages);
69
+
70
+ test('transpiles packages in watch mode', async () => {
71
+ await subject();
72
+
73
+ expect(execa).toHaveBeenCalledWith(
74
+ 'tsc',
75
+ [
76
+ '-b',
77
+ '-w',
78
+ '--preserveWatchOutput',
79
+ ...packages.map(({ location }) => getTsConfig(location)),
80
+ ],
81
+ { stdio: 'inherit' }
82
+ );
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,50 @@
1
+ import { ESLint } from 'eslint';
2
+
3
+ import { getDestinationFolders, getESLintConfiguration } from '../../utils';
4
+
5
+ interface Args {
6
+ fix?: boolean;
7
+ paths: string[];
8
+ }
9
+
10
+ export async function eslint({ fix, paths }: Args) {
11
+ const { disabled, ...config } = getESLintConfiguration();
12
+
13
+ if (disabled) {
14
+ return;
15
+ }
16
+
17
+ const eslint = new ESLint({
18
+ errorOnUnmatchedPattern: false,
19
+ extensions: ['.ts', '.tsx'],
20
+ baseConfig: {
21
+ ignorePatterns: [
22
+ 'node_modules',
23
+ '*.css.d.ts',
24
+ '*.scss.d.ts',
25
+ '*.less.d.ts',
26
+ ...getDestinationFolders(),
27
+ ],
28
+ },
29
+ fix,
30
+ ...config,
31
+ });
32
+
33
+ let files = paths;
34
+ if (files.length === 0) {
35
+ files = [process.cwd()];
36
+ }
37
+
38
+ const results = await eslint.lintFiles(files);
39
+
40
+ if (fix) {
41
+ await ESLint.outputFixes(results);
42
+ }
43
+
44
+ const formatter = await eslint.loadFormatter('stylish');
45
+ process.stdout.write(formatter.format(results) as string);
46
+
47
+ if (ESLint.getErrorResults(results).length) {
48
+ process.exitCode = 1;
49
+ }
50
+ }
@@ -5,8 +5,10 @@ export * from './assets-copy';
5
5
  export * from './bundle';
6
6
  export * from './compile-less';
7
7
  export * from './compile-sass';
8
+ export * from './eslint';
8
9
  export * from './get-module-type';
9
10
  export * from './is-module-installed';
11
+ export * from './set-node-options';
10
12
  export * from './styles-copy';
11
13
  export * from './tcm';
12
14
  export * from './tsc';
@@ -0,0 +1,45 @@
1
+ import os from 'os';
2
+ import { NodeConfiguration, getConfiguration } from '../../utils';
3
+
4
+ const MAX_OLD_SPACE_SIZE_OPTION = 'max_old_space_size';
5
+ const DEFAULT_MAX_OLD_SPACE_SIZE_MB = 8192;
6
+ const MIN_REMAINING_MEMORY_MB = 512;
7
+
8
+ /**
9
+ * Apply global defaults and custom CLI configuration to process.env.NODE_OPTIONS
10
+ * @param command - the command being executed
11
+ * @returns whether process's NODE_OPTIONS were changed
12
+ */
13
+ export function setNodeOptions(command: string) {
14
+ const cliConfig = getConfiguration();
15
+ const cmdConfig: NodeConfiguration = cliConfig[command];
16
+
17
+ const cmdNodeOptions = cmdConfig?.NODE_OPTIONS?.join(' ') ?? '';
18
+ const cliNodeOptions = cliConfig?.NODE_OPTIONS?.join(' ') ?? '';
19
+ const oldNodeOptions = process.env.NODE_OPTIONS ?? '';
20
+
21
+ const mergedOptions = [cmdNodeOptions, cliNodeOptions]
22
+ .reduce((result, options) => {
23
+ return result.includes(options) ? result : `${options} ${result}`;
24
+ }, oldNodeOptions)
25
+ .trim();
26
+
27
+ const newNodeOptions = ensureMaxOldSpaceSize(mergedOptions);
28
+ if (newNodeOptions !== oldNodeOptions) {
29
+ process.env.NODE_OPTIONS = newNodeOptions;
30
+ return true;
31
+ }
32
+
33
+ return false;
34
+ }
35
+
36
+ function ensureMaxOldSpaceSize(nodeOptions: string) {
37
+ return !nodeOptions.includes(MAX_OLD_SPACE_SIZE_OPTION)
38
+ ? `--${MAX_OLD_SPACE_SIZE_OPTION}=${getMaxOldSpaceSize()} ${nodeOptions}`.trim()
39
+ : nodeOptions;
40
+ }
41
+
42
+ function getMaxOldSpaceSize() {
43
+ const totalMB = os.totalmem() / (1024 * 1024);
44
+ return Math.min(DEFAULT_MAX_OLD_SPACE_SIZE_MB, Math.floor(totalMB - MIN_REMAINING_MEMORY_MB));
45
+ }
@@ -49,7 +49,9 @@ async function run({ watch }: RunOptions = {}) {
49
49
  watcher.on('add', writeFile);
50
50
  watcher.on('change', writeFile);
51
51
 
52
- await new Promise<void>(() => {});
52
+ if (process.env.NODE_ENV !== 'test') {
53
+ await new Promise<void>(() => {});
54
+ }
53
55
  }
54
56
  }
55
57