@servicetitan/startup 27.2.1 → 27.4.0-canary.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 (135) hide show
  1. package/dist/cli/commands/get-branch-configs.d.ts +4 -0
  2. package/dist/cli/commands/get-branch-configs.d.ts.map +1 -0
  3. package/dist/cli/commands/get-branch-configs.js +13 -0
  4. package/dist/cli/commands/get-branch-configs.js.map +1 -0
  5. package/dist/cli/commands/get-command.d.ts.map +1 -1
  6. package/dist/cli/commands/get-command.js +4 -2
  7. package/dist/cli/commands/get-command.js.map +1 -1
  8. package/dist/cli/commands/init.d.ts +0 -1
  9. package/dist/cli/commands/init.d.ts.map +1 -1
  10. package/dist/cli/commands/init.js +39 -9
  11. package/dist/cli/commands/init.js.map +1 -1
  12. package/dist/cli/commands/mfe-package-clean.d.ts +13 -0
  13. package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -0
  14. package/dist/cli/commands/mfe-package-clean.js +113 -0
  15. package/dist/cli/commands/mfe-package-clean.js.map +1 -0
  16. package/dist/cli/commands/mfe-package-publish.d.ts +20 -0
  17. package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -0
  18. package/dist/cli/commands/mfe-package-publish.js +153 -0
  19. package/dist/cli/commands/mfe-package-publish.js.map +1 -0
  20. package/dist/cli/commands/mfe-publish.d.ts +4 -29
  21. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  22. package/dist/cli/commands/mfe-publish.js +1 -213
  23. package/dist/cli/commands/mfe-publish.js.map +1 -1
  24. package/dist/cli/utils/assets-copy.d.ts.map +1 -1
  25. package/dist/cli/utils/assets-copy.js +3 -3
  26. package/dist/cli/utils/assets-copy.js.map +1 -1
  27. package/dist/cli/utils/bundle.d.ts.map +1 -1
  28. package/dist/cli/utils/bundle.js +10 -11
  29. package/dist/cli/utils/bundle.js.map +1 -1
  30. package/dist/cli/utils/cli-os.d.ts +9 -2
  31. package/dist/cli/utils/cli-os.d.ts.map +1 -1
  32. package/dist/cli/utils/cli-os.js +16 -8
  33. package/dist/cli/utils/cli-os.js.map +1 -1
  34. package/dist/cli/utils/styles-copy.d.ts.map +1 -1
  35. package/dist/cli/utils/styles-copy.js +3 -3
  36. package/dist/cli/utils/styles-copy.js.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +4 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/utils/get-configuration.d.ts +2 -1
  42. package/dist/utils/get-configuration.d.ts.map +1 -1
  43. package/dist/utils/get-configuration.js +5 -0
  44. package/dist/utils/get-configuration.js.map +1 -1
  45. package/dist/utils/get-exposed-dependencies.d.ts +7 -0
  46. package/dist/utils/get-exposed-dependencies.d.ts.map +1 -0
  47. package/dist/utils/get-exposed-dependencies.js +10 -0
  48. package/dist/utils/get-exposed-dependencies.js.map +1 -0
  49. package/dist/utils/get-folders.js +2 -2
  50. package/dist/utils/get-folders.js.map +1 -1
  51. package/dist/utils/index.d.ts +1 -0
  52. package/dist/utils/index.d.ts.map +1 -1
  53. package/dist/utils/index.js +1 -0
  54. package/dist/utils/index.js.map +1 -1
  55. package/dist/webpack/configs/dev-server-config.d.ts.map +1 -1
  56. package/dist/webpack/configs/dev-server-config.js +4 -1
  57. package/dist/webpack/configs/dev-server-config.js.map +1 -1
  58. package/dist/webpack/configs/plugins/define-exposed-dependencies-plugin.d.ts.map +1 -1
  59. package/dist/webpack/configs/plugins/define-exposed-dependencies-plugin.js +2 -9
  60. package/dist/webpack/configs/plugins/define-exposed-dependencies-plugin.js.map +1 -1
  61. package/dist/webpack/configs/plugins/virtual-modules-plugin.d.ts.map +1 -1
  62. package/dist/webpack/configs/plugins/virtual-modules-plugin.js +15 -5
  63. package/dist/webpack/configs/plugins/virtual-modules-plugin.js.map +1 -1
  64. package/dist/webpack/configs/types.d.ts +16 -0
  65. package/dist/webpack/configs/types.d.ts.map +1 -1
  66. package/dist/webpack/configs/utils/generate-metadata.d.ts.map +1 -1
  67. package/dist/webpack/configs/utils/generate-metadata.js +3 -3
  68. package/dist/webpack/configs/utils/generate-metadata.js.map +1 -1
  69. package/package.json +15 -20
  70. package/src/cli/commands/__tests__/init.test.ts +108 -28
  71. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +1 -1
  72. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +77 -7
  73. package/src/cli/commands/get-branch-configs.ts +8 -0
  74. package/src/cli/commands/get-command.ts +3 -1
  75. package/src/cli/commands/init.ts +40 -10
  76. package/src/cli/commands/mfe-package-clean.ts +132 -0
  77. package/src/cli/commands/mfe-package-publish.ts +189 -0
  78. package/src/cli/commands/mfe-publish.ts +5 -294
  79. package/src/cli/utils/__tests__/assets-copy.test.ts +3 -7
  80. package/src/cli/utils/__tests__/bundle.test.ts +45 -6
  81. package/src/cli/utils/__tests__/cli-os.test.ts +41 -6
  82. package/src/cli/utils/__tests__/styles-copy.test.ts +3 -7
  83. package/src/cli/utils/assets-copy.ts +3 -3
  84. package/src/cli/utils/bundle.ts +14 -20
  85. package/src/cli/utils/cli-os.ts +20 -8
  86. package/src/cli/utils/styles-copy.ts +3 -3
  87. package/src/index.ts +2 -0
  88. package/src/utils/__tests__/get-configuration.test.ts +6 -0
  89. package/src/utils/get-configuration.ts +6 -1
  90. package/src/utils/get-exposed-dependencies.ts +19 -0
  91. package/src/utils/get-folders.ts +1 -1
  92. package/src/utils/index.ts +1 -0
  93. package/src/webpack/__tests__/create-webpack-config-web-component.test.ts +47 -13
  94. package/src/webpack/__tests__/create-webpack-config.test.ts +10 -0
  95. package/src/webpack/configs/dev-server-config.ts +7 -2
  96. package/src/webpack/configs/plugins/define-exposed-dependencies-plugin.ts +6 -16
  97. package/src/webpack/configs/plugins/virtual-modules-plugin.ts +17 -5
  98. package/src/webpack/configs/types.ts +19 -0
  99. package/src/webpack/configs/utils/generate-metadata.ts +5 -5
  100. package/template/.eslintrc.json +0 -3
  101. package/template/.gitignore +0 -21
  102. package/template/.npmrc +0 -3
  103. package/template/.prettierrc +0 -9
  104. package/template/.stylelintignore +0 -1
  105. package/template/.stylelintrc.json +0 -3
  106. package/template/.vscode/extensions.json +0 -18
  107. package/template/.vscode/settings.json +0 -4
  108. package/template/lerna.json +0 -4
  109. package/template/package.json +0 -32
  110. package/template/packages/application/package.json +0 -35
  111. package/template/packages/application/src/__tests__/app.test.tsx +0 -33
  112. package/template/packages/application/src/app.css +0 -3
  113. package/template/packages/application/src/app.tsx +0 -45
  114. package/template/packages/application/src/design-system.css +0 -3
  115. package/template/packages/application/src/index.tsx +0 -8
  116. package/template/packages/application/src/main-page.tsx +0 -5
  117. package/template/packages/application/src/second-page.tsx +0 -5
  118. package/template/packages/application/tsconfig.json +0 -13
  119. package/template/packages/feature-a/package.json +0 -19
  120. package/template/packages/feature-a/src/index.ts +0 -0
  121. package/template/packages/feature-a/tsconfig.json +0 -9
  122. package/template/packages/feature-b/package.json +0 -19
  123. package/template/packages/feature-b/src/index.ts +0 -0
  124. package/template/packages/feature-b/tsconfig.json +0 -9
  125. package/template/packages/feature-c/package.json +0 -19
  126. package/template/packages/feature-c/src/index.ts +0 -0
  127. package/template/packages/feature-c/tsconfig.json +0 -9
  128. package/template/setupTests.ts +0 -27
  129. package/template/tsconfig.test.json +0 -5
  130. package/template-react18/packages/application/package.json +0 -35
  131. package/template-react18/packages/application/src/index.tsx +0 -9
  132. package/template-react18/packages/feature-a/package.json +0 -19
  133. package/template-react18/packages/feature-b/package.json +0 -19
  134. package/template-react18/packages/feature-c/package.json +0 -19
  135. package/tsconfig.json +0 -13
@@ -1,45 +1,14 @@
1
1
  import path from 'path';
2
2
 
3
- import {
4
- getPackages,
5
- logErrors,
6
- PackageType,
7
- readJson,
8
- splitPackagesByType,
9
- isWebComponent,
10
- log,
11
- } from '../../utils';
3
+ import { getPackages, logErrors, PackageType, readJson, splitPackagesByType } from '../../utils';
4
+ import { ArgsPackageClean } from './mfe-package-clean';
5
+ import { ArgsPackagePublish } from './mfe-package-publish';
6
+ import { Command } from './types';
12
7
  import { lernaExec } from '../utils';
13
- import { gitGetBranch, gitGetCommitHash } from '../utils/cli-git';
14
- import {
15
- npmGetPackageVersionDates,
16
- npmGetPackageVersions,
17
- npmPackageSet,
18
- npmPublish,
19
- npmPublishDry,
20
- npmTagVersion,
21
- npmUnpublish,
22
- } from '../utils/cli-npm';
23
- import { getDefaultBuildVersion } from '../utils/publish';
24
- import { Command } from '.';
25
-
26
- interface ArgsPackagePublish {
27
- branch?: string;
28
- build?: string;
29
- concurrency?: number;
30
- dry?: boolean;
31
- force?: boolean;
32
- noTag?: string;
33
- registry?: string;
34
- tag?: string | false;
35
- }
36
-
37
- interface ArgsPackageClean {
38
- count?: number;
39
- }
40
8
 
41
9
  interface Args extends ArgsPackagePublish, ArgsPackageClean {
42
10
  clean?: boolean;
11
+ concurrency?: number;
43
12
  scope?: string | string[];
44
13
  }
45
14
 
@@ -94,261 +63,3 @@ export class MFEPublish implements Command {
94
63
  ].filter(item => !!item) as string[];
95
64
  }
96
65
  }
97
-
98
- export class MFEPackagePublish implements Command {
99
- constructor(private args: ArgsPackagePublish) {}
100
-
101
- description() {
102
- return undefined;
103
- }
104
-
105
- @logErrors
106
- async execute() {
107
- if (!isWebComponent()) {
108
- throw new Error('only web-components can be published');
109
- }
110
-
111
- const packageJson = readJson('package.json');
112
-
113
- if (packageJson.private) {
114
- log.info('package is private, skipping publish');
115
- return;
116
- }
117
-
118
- const data = this.getPublishData();
119
- const packageName = packageJson.name;
120
-
121
- if (!data.version) {
122
- log.info('no build version found, skipping publish');
123
- return;
124
- }
125
-
126
- if (!data.isBranchConfigured && !data.force) {
127
- log.info(
128
- 'branch is not configured for publishing, use --force flag to publish if needed'
129
- );
130
- return;
131
- }
132
-
133
- const versions = npmGetPackageVersions(data.registry, packageName);
134
- const dryRunPrefix = data.dry ? '(dry-run) ' : '';
135
-
136
- if (versions.includes(data.version)) {
137
- log.info(
138
- `${dryRunPrefix}${packageName} version ${data.version} is already published, skipping publish`
139
- );
140
-
141
- if (data.tag) {
142
- log.info(
143
- `${dryRunPrefix}adding tag "${data.tag}" to ${packageName} version ${data.version}`
144
- );
145
- if (!data.dry) {
146
- await npmTagVersion({
147
- packageName,
148
- packageVersion: data.version,
149
- registry: data.registry,
150
- tag: data.tag,
151
- });
152
- }
153
- }
154
- return;
155
- }
156
-
157
- await npmPackageSet('version', data.version);
158
- await npmPackageSet('publishConfig.registry', data.registry);
159
-
160
- if (!packageJson.files) {
161
- await npmPackageSet('files[0]', 'dist');
162
- await npmPackageSet('files[1]', 'package.json');
163
- }
164
-
165
- if (data.dry) {
166
- await npmPublishDry();
167
- } else {
168
- await npmPublish(data.tag);
169
- }
170
-
171
- log.info(`${dryRunPrefix}published ${packageName} version ${data.version}`);
172
- }
173
-
174
- private getPublishData(): {
175
- version: string;
176
- buildVersion: string;
177
- tag?: string;
178
- registry: string;
179
- dry: boolean;
180
- force: boolean;
181
- isBranchConfigured: boolean;
182
- } {
183
- const cli = this.args;
184
- const branch = cli.branch ?? gitGetBranch();
185
- const branchConfig = getBranchConfigs()[branch];
186
- let buildVersion = cli.build;
187
-
188
- if (!buildVersion) {
189
- buildVersion = getDefaultBuildVersion(branch, gitGetCommitHash());
190
- }
191
-
192
- if (!buildVersion) {
193
- throw new Error('build version is not set');
194
- }
195
-
196
- let tag: string;
197
-
198
- if (cli.tag === false) {
199
- tag = '';
200
- } else if (cli.tag) {
201
- tag = cli.tag;
202
- } else {
203
- tag = branchConfig?.tag ?? '';
204
- }
205
-
206
- const registry = cli.registry ?? 'https://verdaccio.servicetitan.com';
207
-
208
- return {
209
- tag,
210
- version: '0.0.0-' + buildVersion,
211
- buildVersion,
212
- registry,
213
- dry: !!cli.dry,
214
- force: !!cli.force,
215
- isBranchConfigured: !!branchConfig,
216
- };
217
- }
218
- }
219
-
220
- export class MFEPackageClean implements Command {
221
- constructor(private args: ArgsPackageClean) {}
222
-
223
- description() {
224
- return undefined;
225
- }
226
-
227
- @logErrors
228
- async execute() {
229
- if (!isWebComponent()) {
230
- throw new Error('only web-components can be cleaned');
231
- }
232
-
233
- const data = this.getCleanData();
234
- const packageJson = readJson('package.json');
235
- const packageName = packageJson.name;
236
- const branchedVersions = this.getBranchedVersions(packageName, data.registry);
237
-
238
- log.info(
239
- `branched versions (${data.count}):`,
240
- JSON.stringify(branchedVersions, undefined, 4)
241
- );
242
-
243
- const branchedVersionsToClean: Record<string, [string, Date][]> = {};
244
-
245
- for (const branch of Object.keys(branchedVersions)) {
246
- // limit branches for now
247
- if (!branchedVersions[branch] || !data.branches.includes(branch)) {
248
- continue;
249
- }
250
-
251
- branchedVersions[branch].sort(([, adt], [, bdt]) => (adt > bdt ? -1 : 1));
252
- branchedVersionsToClean[branch] = branchedVersions[branch].slice(data.count);
253
- }
254
-
255
- log.info(
256
- 'found versions for unpublish:',
257
- JSON.stringify(branchedVersionsToClean, undefined, 4)
258
- );
259
-
260
- const unVersions = Object.keys(branchedVersionsToClean).reduce(
261
- (out, br) => [...out, ...branchedVersionsToClean[br].map(([v]) => v)],
262
- []
263
- );
264
-
265
- for (const version of unVersions) {
266
- try {
267
- // eslint-disable-next-line no-await-in-loop
268
- await npmUnpublish(data.registry, packageName, version);
269
- } catch {
270
- log.error(`error while removing ${packageName} version ${version}`);
271
- }
272
- }
273
- }
274
-
275
- private getCleanData(): {
276
- count: number;
277
- registry: string;
278
- branches: string[];
279
- } {
280
- let count = this.args.count;
281
-
282
- if (!count) {
283
- count = 5;
284
- }
285
-
286
- const registry = 'https://verdaccio.servicetitan.com';
287
-
288
- return { count, registry, branches: Object.keys(getBranchConfigs()) };
289
- }
290
-
291
- private getBranchedVersions(packageName: string, registry: string) {
292
- const versions = npmGetPackageVersionDates(registry, packageName);
293
- const branchedVersions: Record<string, [string, Date][]> = {};
294
- const unknownVersions: string[] = [];
295
-
296
- const addVersion = (branch: string, version: string, dt: Date) => {
297
- if (!branchedVersions[branch]) {
298
- branchedVersions[branch] = [];
299
- }
300
-
301
- branchedVersions[branch].push([version, dt]);
302
- };
303
-
304
- for (const [version, dt] of versions) {
305
- if (!version.startsWith('0.0.0-')) {
306
- continue;
307
- }
308
-
309
- const buildVersion = version.replace('0.0.0-', '');
310
-
311
- if (/^(\d+)\.(\d+)\.(\d+)$/.test(buildVersion)) {
312
- // master version generated by nerdbank versioning
313
- addVersion('master', version, dt);
314
- continue;
315
- }
316
-
317
- const match1 = buildVersion.match(
318
- /^(\d+)\.(\d+)\.(\d+)-([\dA-Za-z\-]+).([\dA-Za-z]+)$/
319
- );
320
-
321
- if (match1?.length) {
322
- // branch version generated by nerdbank versioning
323
- addVersion(match1[4], version, dt);
324
- continue;
325
- }
326
-
327
- const match2 = buildVersion.match(/^([\dA-Za-z\-]+).([\dA-Za-z]+)$/);
328
-
329
- if (match2?.length) {
330
- // branch version generated by mfe-publisher versioning
331
- addVersion(match2[1], version, dt);
332
- continue;
333
- }
334
-
335
- unknownVersions.push(version);
336
- }
337
-
338
- if (unknownVersions.length) {
339
- log.info('unknown versions:', unknownVersions.join());
340
- }
341
-
342
- return branchedVersions;
343
- }
344
- }
345
-
346
- const getBranchConfigs = (): Record<string, { tag?: string }> => {
347
- // ToDo: add ability to configure it in a package.json
348
- return {
349
- develop: { tag: 'dev' },
350
- dev: { tag: 'dev' },
351
- next: { tag: 'next' },
352
- master: { tag: 'prod' },
353
- };
354
- };
@@ -5,10 +5,7 @@ import { assetsCopy, assetsCopyWatch } from '../assets-copy';
5
5
  import { assetExtensions } from '../index';
6
6
 
7
7
  jest.mock('cpx2', () => ({
8
- copy: jest.fn().mockImplementation((...args: any[]) => {
9
- const callback = args[args.length - 1];
10
- callback(null);
11
- }),
8
+ copySync: jest.fn(),
12
9
  watch: jest.fn(() => ({ on: (_eventName: string, listener: Function) => listener() })),
13
10
  }));
14
11
  jest.mock('../../../utils', () => ({
@@ -28,10 +25,9 @@ describe('[startup] Cli Utils (Assets)', () => {
28
25
  test('copies assets from source to destination', async () => {
29
26
  await subject();
30
27
 
31
- expect(cpx.copy).toHaveBeenCalledWith(
28
+ expect(cpx.copySync).toHaveBeenCalledWith(
32
29
  `${source}/**/*.{${assetExtensions.join()}}`,
33
- destination,
34
- expect.anything()
30
+ destination
35
31
  );
36
32
  });
37
33
  });
@@ -2,7 +2,12 @@ import { fs, vol } from 'memfs';
2
2
  import path from 'path';
3
3
  import webpack from 'webpack';
4
4
  import WebpackDevServer from 'webpack-dev-server';
5
- import { getFolders, getPackageData, loadSharedDependencies } from '../../../utils';
5
+ import {
6
+ getConfiguration,
7
+ getFolders,
8
+ getPackageData,
9
+ loadSharedDependencies,
10
+ } from '../../../utils';
6
11
  import { createWebpackConfig } from '../../../webpack';
7
12
  import { createPackage } from '../../../__mocks__';
8
13
 
@@ -44,12 +49,12 @@ describe('[startup] Cli Utils', () => {
44
49
  return expect.stringContaining(name);
45
50
  }
46
51
 
47
- function packageFS() {
48
- return { 'package.json': JSON.stringify(pkg) };
52
+ function packageFS(configuration: ReturnType<typeof getConfiguration> = {} as any) {
53
+ return { 'package.json': JSON.stringify({ ...pkg, cli: configuration }) };
49
54
  }
50
55
 
51
- function webComponentFS() {
52
- return { 'package.json': JSON.stringify({ ...pkg, cli: { 'web-component': true } }) };
56
+ function webComponentFS(configuration: ReturnType<typeof getConfiguration> = {} as any) {
57
+ return packageFS({ 'web-component': true, ...configuration });
53
58
  }
54
59
 
55
60
  describe(`${bundle.name}`, () => {
@@ -287,6 +292,19 @@ describe('[startup] Cli Utils', () => {
287
292
  );
288
293
  });
289
294
 
295
+ describe('when webpack.devServer is set to false in package.json', () => {
296
+ beforeEach(() => {
297
+ vol.fromJSON(packageFS({ webpack: { devServer: false } } as any));
298
+ });
299
+
300
+ test('runs webpack and not webpack development server', async () => {
301
+ await subject();
302
+
303
+ expect(webpack).toHaveBeenCalledWith(createWebpackResult);
304
+ expect(WebpackDevServer).not.toHaveBeenCalled();
305
+ });
306
+ });
307
+
290
308
  function itUsesConfig(params: { name: string; config: () => Record<string, any> }) {
291
309
  test(`uses ${params.name}`, async () => {
292
310
  const { devServer, ...config } = params.config();
@@ -351,7 +369,8 @@ describe('[startup] Cli Utils', () => {
351
369
  ...options,
352
370
  embed: true,
353
371
  });
354
- expect(webpack).toHaveBeenCalledWith(createWebpackResult);
372
+ expect(webpack).toHaveBeenNthCalledWith(1, createWebpackResult);
373
+ expect(webpack).toHaveBeenNthCalledWith(2, createWebpackResult);
355
374
  expect(compiler.watch).toHaveBeenCalled();
356
375
  expect(WebpackDevServer).toHaveBeenCalledWith(
357
376
  { host: 'localhost', port: 8080 },
@@ -359,6 +378,26 @@ describe('[startup] Cli Utils', () => {
359
378
  );
360
379
  });
361
380
 
381
+ describe('when webpack.devServer is set to false in package.json', () => {
382
+ beforeEach(() => {
383
+ vol.fromJSON(webComponentFS({ webpack: { devServer: false } } as any));
384
+ });
385
+
386
+ test('runs webpack and not webpack development server', async () => {
387
+ const options = { name: expectPackageName() };
388
+ const overrides = { configuration: { mode: 'development' } };
389
+
390
+ await subject();
391
+
392
+ expect(createWebpackConfig).toHaveBeenCalledWith(overrides, options);
393
+ expect(createWebpackConfig).toHaveBeenCalledWith(overrides, {
394
+ ...options,
395
+ embed: true,
396
+ });
397
+ expect(WebpackDevServer).not.toHaveBeenCalled();
398
+ });
399
+ });
400
+
362
401
  function itUsesConfig(params: { name: string; config: () => Record<string, any> }) {
363
402
  test(`uses ${params.name}`, async () => {
364
403
  const options = { name: expectPackageName() };
@@ -1,6 +1,7 @@
1
1
  import { execSync, spawn } from 'child_process';
2
2
 
3
3
  import { runCommand, runCommandOutput } from '../cli-os';
4
+ import { log } from '../../../utils';
4
5
 
5
6
  jest.mock('child_process', () => ({ execSync: jest.fn(), spawn: jest.fn() }));
6
7
  jest.mock('../../../utils', () => ({ log: { info: jest.fn() } })); // suppress log output
@@ -11,6 +12,7 @@ describe('[startup] Cli Utils (OS)', () => {
11
12
  let childProcess: ReturnType<typeof spawn>;
12
13
 
13
14
  beforeEach(() => {
15
+ jest.clearAllMocks();
14
16
  exitCode = 0;
15
17
  childProcess = {
16
18
  stderr: { pipe: jest.fn() },
@@ -25,7 +27,7 @@ describe('[startup] Cli Utils (OS)', () => {
25
27
  test('runs command', () => {
26
28
  expect(subject('foo bar')).resolves.toBe(undefined);
27
29
 
28
- expect(spawn).toHaveBeenCalledWith('foo', ['bar'], undefined);
30
+ expect(spawn).toHaveBeenCalledWith('foo', ['bar'], {});
29
31
  });
30
32
 
31
33
  test('runs command with options', async () => {
@@ -37,7 +39,7 @@ describe('[startup] Cli Utils (OS)', () => {
37
39
  test('runs command array', async () => {
38
40
  await subject(['foo', 'bar', '--baz']);
39
41
 
40
- expect(spawn).toHaveBeenCalledWith('foo', ['bar', '--baz'], undefined);
42
+ expect(spawn).toHaveBeenCalledWith('foo', ['bar', '--baz'], {});
41
43
  });
42
44
 
43
45
  test("pipes stdout and stderr to parent's streams", async () => {
@@ -60,10 +62,28 @@ describe('[startup] Cli Utils (OS)', () => {
60
62
  expect(subject(command)).rejects.toBeUndefined();
61
63
  })
62
64
  );
65
+
66
+ test('logs command and exit status', async () => {
67
+ const logInfoSpy = jest.spyOn(log, 'info');
68
+ await subject('foo');
69
+
70
+ expect(logInfoSpy).toHaveBeenCalledWith('run command foo');
71
+ expect(logInfoSpy).toHaveBeenCalledWith('command finished with code 0', 'foo');
72
+ });
73
+
74
+ test('quiet option suppresses output', async () => {
75
+ const logInfoSpy = jest.spyOn(log, 'info');
76
+ await subject('foo', { quiet: true });
77
+
78
+ expect(logInfoSpy).not.toHaveBeenCalled();
79
+ });
63
80
  });
64
81
 
65
82
  describe(`${runCommandOutput.name}`, () => {
66
- beforeEach(() => jest.mocked(execSync).mockReturnValue(''));
83
+ beforeEach(() => {
84
+ jest.clearAllMocks();
85
+ jest.mocked(execSync).mockReturnValue('');
86
+ });
67
87
 
68
88
  const subject = (...args: Parameters<typeof runCommandOutput>) => runCommandOutput(...args);
69
89
 
@@ -73,7 +93,7 @@ describe('[startup] Cli Utils (OS)', () => {
73
93
 
74
94
  expect(subject('foo bar')).toBe(result);
75
95
 
76
- expect(execSync).toHaveBeenCalledWith('foo bar', undefined);
96
+ expect(execSync).toHaveBeenCalledWith('foo bar', {});
77
97
  });
78
98
 
79
99
  test('runs command with options', () => {
@@ -85,13 +105,13 @@ describe('[startup] Cli Utils (OS)', () => {
85
105
  test('runs command array', () => {
86
106
  subject(['foo', 'bar']);
87
107
 
88
- expect(execSync).toHaveBeenCalledWith('foo bar', undefined);
108
+ expect(execSync).toHaveBeenCalledWith('foo bar', {});
89
109
  });
90
110
 
91
111
  test('ignores false and "" array values', () => {
92
112
  subject(['', 'foo', false, '', 'bar', false]);
93
113
 
94
- expect(execSync).toHaveBeenCalledWith('foo bar', undefined);
114
+ expect(execSync).toHaveBeenCalledWith('foo bar', {});
95
115
  });
96
116
 
97
117
  ['', [], ['']].forEach(command =>
@@ -99,5 +119,20 @@ describe('[startup] Cli Utils (OS)', () => {
99
119
  expect(() => subject(command)).toThrow();
100
120
  })
101
121
  );
122
+
123
+ test('logs command and result', () => {
124
+ const logInfoSpy = jest.spyOn(log, 'info');
125
+ subject('foo');
126
+
127
+ expect(logInfoSpy).toHaveBeenCalledWith('run command foo');
128
+ expect(logInfoSpy).toHaveBeenCalledWith('command finished', '');
129
+ });
130
+
131
+ test('quiet option suppresses output', () => {
132
+ const logInfoSpy = jest.spyOn(log, 'info');
133
+ subject('foo', { quiet: true });
134
+
135
+ expect(logInfoSpy).not.toHaveBeenCalled();
136
+ });
102
137
  });
103
138
  });
@@ -5,10 +5,7 @@ import { stylesCopy, stylesCopyWatch } from '../styles-copy';
5
5
  import { styleExtensions } from '../index';
6
6
 
7
7
  jest.mock('cpx2', () => ({
8
- copy: jest.fn().mockImplementation((...args: any[]) => {
9
- const callback = args[args.length - 1];
10
- callback(null);
11
- }),
8
+ copySync: jest.fn(),
12
9
  watch: jest.fn(() => ({ on: (_eventName: string, listener: Function) => listener() })),
13
10
  }));
14
11
  jest.mock('../../../utils', () => ({
@@ -28,10 +25,9 @@ describe('[startup] Cli Utils (Styles)', () => {
28
25
  test('copies styles from source to destination', async () => {
29
26
  await subject();
30
27
 
31
- expect(cpx.copy).toHaveBeenCalledWith(
28
+ expect(cpx.copySync).toHaveBeenCalledWith(
32
29
  `${source}/**/*.{${styleExtensions.join()}}`,
33
- destination,
34
- expect.anything()
30
+ destination
35
31
  );
36
32
  });
37
33
  });
@@ -1,15 +1,15 @@
1
1
  import cpx from 'cpx2';
2
- import util from 'util';
3
2
 
4
3
  import { getFolders, log } from '../../utils';
5
4
  import { assetExtensions } from '.';
6
5
 
7
- export async function assetsCopy() {
6
+ export function assetsCopy() {
8
7
  const { source, destination } = getFolders();
9
8
 
10
9
  log.info('Copying asset files...');
11
10
 
12
- await util.promisify(cpx.copy)(`${source}/**/*.{${assetExtensions.join()}}`, destination);
11
+ cpx.copySync(`${source}/**/*.{${assetExtensions.join()}}`, destination);
12
+ return Promise.resolve();
13
13
  }
14
14
 
15
15
  export async function assetsCopyWatch() {
@@ -6,7 +6,7 @@ import { getPortPromise } from 'portfinder';
6
6
  import webpack, { Configuration } from 'webpack';
7
7
  import WebpackDevServer from 'webpack-dev-server';
8
8
 
9
- import { getPackageName, isWebComponent, log } from '../../utils';
9
+ import { getPackageName, isDevServerDisabled, isWebComponent, log } from '../../utils';
10
10
  import { Overrides, createWebpackConfig } from '../../webpack';
11
11
 
12
12
  interface Options {
@@ -62,6 +62,7 @@ export async function bundleWatch(options: Options = {}) {
62
62
  const mode = 'development';
63
63
  const config = readWebpackConfig({ ...options, fallback: `./${webpackDevConfigFileName}` });
64
64
  const { codeCoverage, esbuild, experimentalBundlers } = options;
65
+ const configOverrides = { codeCoverage, esbuild, experimentalBundlers, name };
65
66
 
66
67
  if (isWebComponent()) {
67
68
  const webpackConfig: Overrides = {
@@ -72,31 +73,24 @@ export async function bundleWatch(options: Options = {}) {
72
73
  return Promise.all([
73
74
  runWatch(
74
75
  createWebpackConfig(webpackConfig, {
75
- codeCoverage,
76
76
  embed: true,
77
- name,
78
- esbuild,
79
- experimentalBundlers,
80
- })
81
- ),
82
- runServe(
83
- createWebpackConfig(webpackConfig, {
84
- name,
85
- codeCoverage,
86
- esbuild,
87
- experimentalBundlers,
77
+ ...configOverrides,
88
78
  })
89
79
  ),
80
+ isDevServerDisabled()
81
+ ? runWatch(createWebpackConfig(webpackConfig, configOverrides))
82
+ : runServe(createWebpackConfig(webpackConfig, configOverrides)),
90
83
  ]);
91
84
  }
92
85
 
93
- return runServe(
94
- config ??
95
- createWebpackConfig(
96
- { configuration: { mode } },
97
- { name, codeCoverage, esbuild, experimentalBundlers }
98
- )
99
- );
86
+ const webpackConfig =
87
+ config ?? createWebpackConfig({ configuration: { mode } }, configOverrides);
88
+
89
+ if (isDevServerDisabled()) {
90
+ return runWatch(webpackConfig);
91
+ }
92
+
93
+ return runServe(webpackConfig);
100
94
  }
101
95
 
102
96
  function readWebpackConfig({ config, fallback }: Options & { fallback: string }) {