@servicetitan/startup 31.3.2 → 31.5.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 (234) hide show
  1. package/dist/cli/commands/build.d.ts +0 -7
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +28 -17
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/bundle-package.d.ts +1 -1
  6. package/dist/cli/commands/bundle-package.d.ts.map +1 -1
  7. package/dist/cli/commands/bundle-package.js +13 -21
  8. package/dist/cli/commands/bundle-package.js.map +1 -1
  9. package/dist/cli/commands/clean.js +1 -1
  10. package/dist/cli/commands/clean.js.map +1 -1
  11. package/dist/cli/commands/eslint.d.ts +0 -1
  12. package/dist/cli/commands/eslint.d.ts.map +1 -1
  13. package/dist/cli/commands/eslint.js +0 -3
  14. package/dist/cli/commands/eslint.js.map +1 -1
  15. package/dist/cli/commands/get-command.d.ts.map +1 -1
  16. package/dist/cli/commands/get-command.js +3 -1
  17. package/dist/cli/commands/get-command.js.map +1 -1
  18. package/dist/cli/commands/get-user-commands.js +3 -2
  19. package/dist/cli/commands/get-user-commands.js.map +1 -1
  20. package/dist/cli/commands/init.js.map +1 -1
  21. package/dist/cli/commands/install.js.map +1 -1
  22. package/dist/cli/commands/lint.js.map +1 -1
  23. package/dist/cli/commands/mfe-package-clean.d.ts +3 -4
  24. package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -1
  25. package/dist/cli/commands/mfe-package-clean.js +0 -3
  26. package/dist/cli/commands/mfe-package-clean.js.map +1 -1
  27. package/dist/cli/commands/mfe-package-publish.d.ts +9 -4
  28. package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -1
  29. package/dist/cli/commands/mfe-package-publish.js +70 -47
  30. package/dist/cli/commands/mfe-package-publish.js.map +1 -1
  31. package/dist/cli/commands/mfe-publish.d.ts +3 -3
  32. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  33. package/dist/cli/commands/mfe-publish.js +7 -1
  34. package/dist/cli/commands/mfe-publish.js.map +1 -1
  35. package/dist/cli/commands/prepare-package.d.ts +0 -1
  36. package/dist/cli/commands/prepare-package.d.ts.map +1 -1
  37. package/dist/cli/commands/prepare-package.js +0 -3
  38. package/dist/cli/commands/prepare-package.js.map +1 -1
  39. package/dist/cli/commands/review/review.js.map +1 -1
  40. package/dist/cli/commands/review/rules/require-one-anvil-uikit-contrib-version.js.map +1 -1
  41. package/dist/cli/commands/review/rules/require-one-collection-version.js.map +1 -1
  42. package/dist/cli/commands/review/rules/require-one-uikit-version.js.map +1 -1
  43. package/dist/cli/commands/run-task.d.ts +0 -1
  44. package/dist/cli/commands/run-task.d.ts.map +1 -1
  45. package/dist/cli/commands/run-task.js +0 -3
  46. package/dist/cli/commands/run-task.js.map +1 -1
  47. package/dist/cli/commands/start.d.ts +0 -8
  48. package/dist/cli/commands/start.d.ts.map +1 -1
  49. package/dist/cli/commands/start.js +28 -16
  50. package/dist/cli/commands/start.js.map +1 -1
  51. package/dist/cli/commands/styles-check.d.ts +0 -1
  52. package/dist/cli/commands/styles-check.d.ts.map +1 -1
  53. package/dist/cli/commands/styles-check.js +40 -99
  54. package/dist/cli/commands/styles-check.js.map +1 -1
  55. package/dist/cli/commands/tests.js.map +1 -1
  56. package/dist/cli/commands/types.d.ts +1 -1
  57. package/dist/cli/commands/types.d.ts.map +1 -1
  58. package/dist/cli/commands/upload-sourcemaps.d.ts +22 -0
  59. package/dist/cli/commands/upload-sourcemaps.d.ts.map +1 -0
  60. package/dist/cli/commands/upload-sourcemaps.js +179 -0
  61. package/dist/cli/commands/upload-sourcemaps.js.map +1 -0
  62. package/dist/cli/tasks/cli-task.js.map +1 -1
  63. package/dist/cli/tasks/swc-compile-package.js.map +1 -1
  64. package/dist/cli/tasks/task.js.map +1 -1
  65. package/dist/cli/tasks/tsc-compile-package.js.map +1 -1
  66. package/dist/cli/tasks/tsc-compile.js.map +1 -1
  67. package/dist/cli/utils/bundle.d.ts +4 -1
  68. package/dist/cli/utils/bundle.d.ts.map +1 -1
  69. package/dist/cli/utils/bundle.js +67 -74
  70. package/dist/cli/utils/bundle.js.map +1 -1
  71. package/dist/cli/utils/cli-os.js +2 -2
  72. package/dist/cli/utils/cli-os.js.map +1 -1
  73. package/dist/cli/utils/ts-config.js.map +1 -1
  74. package/dist/utils/find-packages.d.ts.map +1 -1
  75. package/dist/utils/find-packages.js +3 -4
  76. package/dist/utils/find-packages.js.map +1 -1
  77. package/dist/utils/find-up.d.ts +2 -0
  78. package/dist/utils/find-up.d.ts.map +1 -0
  79. package/dist/utils/find-up.js +28 -0
  80. package/dist/utils/find-up.js.map +1 -0
  81. package/dist/utils/get-configuration.d.ts +3 -1
  82. package/dist/utils/get-configuration.d.ts.map +1 -1
  83. package/dist/utils/get-configuration.js +1 -0
  84. package/dist/utils/get-configuration.js.map +1 -1
  85. package/dist/utils/index.d.ts +1 -0
  86. package/dist/utils/index.d.ts.map +1 -1
  87. package/dist/utils/index.js +1 -0
  88. package/dist/utils/index.js.map +1 -1
  89. package/dist/utils/log.js.map +1 -1
  90. package/dist/webpack/configs/cache-config.d.ts +6 -0
  91. package/dist/webpack/configs/cache-config.d.ts.map +1 -0
  92. package/dist/webpack/configs/cache-config.js +52 -0
  93. package/dist/webpack/configs/cache-config.js.map +1 -0
  94. package/dist/webpack/configs/dev-server-config.js +1 -1
  95. package/dist/webpack/configs/dev-server-config.js.map +1 -1
  96. package/dist/webpack/configs/entry.config.d.ts.map +1 -1
  97. package/dist/webpack/configs/entry.config.js +15 -6
  98. package/dist/webpack/configs/entry.config.js.map +1 -1
  99. package/dist/webpack/configs/externals-config.d.ts.map +1 -1
  100. package/dist/webpack/configs/externals-config.js +6 -2
  101. package/dist/webpack/configs/externals-config.js.map +1 -1
  102. package/dist/webpack/configs/index.d.ts +1 -0
  103. package/dist/webpack/configs/index.d.ts.map +1 -1
  104. package/dist/webpack/configs/index.js +1 -0
  105. package/dist/webpack/configs/index.js.map +1 -1
  106. package/dist/webpack/configs/optimization-config.d.ts.map +1 -1
  107. package/dist/webpack/configs/optimization-config.js +7 -11
  108. package/dist/webpack/configs/optimization-config.js.map +1 -1
  109. package/dist/webpack/configs/output-config.d.ts.map +1 -1
  110. package/dist/webpack/configs/output-config.js +25 -4
  111. package/dist/webpack/configs/output-config.js.map +1 -1
  112. package/dist/webpack/configs/plugins/assets-manifest-plugin.d.ts +6 -0
  113. package/dist/webpack/configs/plugins/assets-manifest-plugin.d.ts.map +1 -1
  114. package/dist/webpack/configs/plugins/assets-manifest-plugin.js +50 -8
  115. package/dist/webpack/configs/plugins/assets-manifest-plugin.js.map +1 -1
  116. package/dist/webpack/configs/plugins/bundle-analyser-plugin.d.ts.map +1 -1
  117. package/dist/webpack/configs/plugins/bundle-analyser-plugin.js +3 -7
  118. package/dist/webpack/configs/plugins/bundle-analyser-plugin.js.map +1 -1
  119. package/dist/webpack/configs/plugins/define-exposed-dependencies-plugin.d.ts.map +1 -1
  120. package/dist/webpack/configs/plugins/define-exposed-dependencies-plugin.js +3 -2
  121. package/dist/webpack/configs/plugins/define-exposed-dependencies-plugin.js.map +1 -1
  122. package/dist/webpack/configs/plugins/define-exposed-instance-dependencies-plugin.js +2 -2
  123. package/dist/webpack/configs/plugins/define-exposed-instance-dependencies-plugin.js.map +1 -1
  124. package/dist/webpack/configs/plugins/html-plugin.d.ts +1 -1
  125. package/dist/webpack/configs/plugins/html-plugin.d.ts.map +1 -1
  126. package/dist/webpack/configs/plugins/html-plugin.js +2 -3
  127. package/dist/webpack/configs/plugins/html-plugin.js.map +1 -1
  128. package/dist/webpack/configs/plugins/html-tags-plugin.d.ts +4 -0
  129. package/dist/webpack/configs/plugins/html-tags-plugin.d.ts.map +1 -0
  130. package/dist/webpack/configs/plugins/html-tags-plugin.js +49 -0
  131. package/dist/webpack/configs/plugins/html-tags-plugin.js.map +1 -0
  132. package/dist/webpack/configs/plugins/index.d.ts +2 -0
  133. package/dist/webpack/configs/plugins/index.d.ts.map +1 -1
  134. package/dist/webpack/configs/plugins/index.js +2 -0
  135. package/dist/webpack/configs/plugins/index.js.map +1 -1
  136. package/dist/webpack/configs/plugins/remove-empty-scripts-plugin.d.ts +4 -0
  137. package/dist/webpack/configs/plugins/remove-empty-scripts-plugin.d.ts.map +1 -0
  138. package/dist/webpack/configs/plugins/remove-empty-scripts-plugin.js +25 -0
  139. package/dist/webpack/configs/plugins/remove-empty-scripts-plugin.js.map +1 -0
  140. package/dist/webpack/configs/plugins/virtual-modules-plugin.d.ts +1 -0
  141. package/dist/webpack/configs/plugins/virtual-modules-plugin.d.ts.map +1 -1
  142. package/dist/webpack/configs/plugins/virtual-modules-plugin.js +23 -14
  143. package/dist/webpack/configs/plugins/virtual-modules-plugin.js.map +1 -1
  144. package/dist/webpack/configs/plugins-config.d.ts.map +1 -1
  145. package/dist/webpack/configs/plugins-config.js +2 -0
  146. package/dist/webpack/configs/plugins-config.js.map +1 -1
  147. package/dist/webpack/configs/rules/css-rules.d.ts.map +1 -1
  148. package/dist/webpack/configs/rules/css-rules.js +13 -18
  149. package/dist/webpack/configs/rules/css-rules.js.map +1 -1
  150. package/dist/webpack/configs/utils/get-bundle-type.d.ts +3 -0
  151. package/dist/webpack/configs/utils/get-bundle-type.d.ts.map +1 -0
  152. package/dist/webpack/configs/utils/get-bundle-type.js +24 -0
  153. package/dist/webpack/configs/utils/get-bundle-type.js.map +1 -0
  154. package/dist/webpack/configs/utils/index.d.ts +1 -0
  155. package/dist/webpack/configs/utils/index.d.ts.map +1 -1
  156. package/dist/webpack/configs/utils/index.js +1 -0
  157. package/dist/webpack/configs/utils/index.js.map +1 -1
  158. package/dist/webpack/create-webpack-config.d.ts.map +1 -1
  159. package/dist/webpack/create-webpack-config.js +37 -48
  160. package/dist/webpack/create-webpack-config.js.map +1 -1
  161. package/dist/webpack/types.d.ts +4 -0
  162. package/dist/webpack/types.d.ts.map +1 -1
  163. package/dist/webpack/utils/index.d.ts +1 -0
  164. package/dist/webpack/utils/index.d.ts.map +1 -1
  165. package/dist/webpack/utils/index.js +1 -0
  166. package/dist/webpack/utils/index.js.map +1 -1
  167. package/dist/webpack/utils/stringify-config.d.ts +2 -0
  168. package/dist/webpack/utils/stringify-config.d.ts.map +1 -0
  169. package/dist/webpack/utils/stringify-config.js +35 -0
  170. package/dist/webpack/utils/stringify-config.js.map +1 -0
  171. package/package.json +17 -15
  172. package/src/cli/commands/__tests__/build.test.ts +19 -2
  173. package/src/cli/commands/__tests__/bundle-package.test.ts +29 -8
  174. package/src/cli/commands/__tests__/clean.test.ts +2 -0
  175. package/src/cli/commands/__tests__/get-user-commands.test.ts +1 -1
  176. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +91 -15
  177. package/src/cli/commands/__tests__/mfe-publish.test.ts +2 -0
  178. package/src/cli/commands/__tests__/start.test.ts +15 -1
  179. package/src/cli/commands/__tests__/styles-check.test.ts +27 -80
  180. package/src/cli/commands/__tests__/upload-sourcemaps.test.ts +127 -0
  181. package/src/cli/commands/build.ts +33 -17
  182. package/src/cli/commands/bundle-package.ts +10 -19
  183. package/src/cli/commands/clean.ts +1 -1
  184. package/src/cli/commands/eslint.ts +0 -4
  185. package/src/cli/commands/get-command.ts +2 -0
  186. package/src/cli/commands/get-user-commands.ts +1 -1
  187. package/src/cli/commands/mfe-package-clean.ts +2 -6
  188. package/src/cli/commands/mfe-package-publish.ts +104 -70
  189. package/src/cli/commands/mfe-publish.ts +8 -5
  190. package/src/cli/commands/prepare-package.ts +0 -4
  191. package/src/cli/commands/run-task.ts +0 -4
  192. package/src/cli/commands/start.ts +22 -5
  193. package/src/cli/commands/styles-check.ts +28 -131
  194. package/src/cli/commands/types.ts +1 -1
  195. package/src/cli/commands/upload-sourcemaps.ts +108 -0
  196. package/src/cli/utils/__tests__/bundle.test.ts +119 -9
  197. package/src/cli/utils/__tests__/cli-os.test.ts +2 -2
  198. package/src/cli/utils/__tests__/compile.test.ts +2 -0
  199. package/src/cli/utils/__tests__/type-check.test.ts +2 -0
  200. package/src/cli/utils/bundle.ts +76 -54
  201. package/src/cli/utils/cli-os.ts +2 -2
  202. package/src/utils/__tests__/get-configuration.test.ts +1 -1
  203. package/src/utils/find-packages.ts +3 -5
  204. package/src/utils/find-up.ts +12 -0
  205. package/src/utils/get-configuration.ts +2 -0
  206. package/src/utils/index.ts +1 -0
  207. package/src/webpack/__mocks__/style-rules.ts +1 -1
  208. package/src/webpack/__tests__/create-webpack-config-shared-dependencies.test.ts +274 -45
  209. package/src/webpack/__tests__/create-webpack-config-web-component.test.ts +25 -1
  210. package/src/webpack/__tests__/create-webpack-config.test.ts +9 -57
  211. package/src/webpack/configs/cache-config.ts +37 -0
  212. package/src/webpack/configs/dev-server-config.ts +1 -1
  213. package/src/webpack/configs/entry.config.ts +18 -8
  214. package/src/webpack/configs/externals-config.ts +7 -2
  215. package/src/webpack/configs/index.ts +1 -0
  216. package/src/webpack/configs/optimization-config.ts +7 -11
  217. package/src/webpack/configs/output-config.ts +23 -7
  218. package/src/webpack/configs/plugins/assets-manifest-plugin.ts +46 -10
  219. package/src/webpack/configs/plugins/bundle-analyser-plugin.ts +1 -6
  220. package/src/webpack/configs/plugins/define-exposed-dependencies-plugin.ts +3 -2
  221. package/src/webpack/configs/plugins/define-exposed-instance-dependencies-plugin.ts +2 -2
  222. package/src/webpack/configs/plugins/html-plugin.ts +2 -3
  223. package/src/webpack/configs/plugins/html-tags-plugin.ts +28 -0
  224. package/src/webpack/configs/plugins/index.ts +2 -0
  225. package/src/webpack/configs/plugins/remove-empty-scripts-plugin.ts +11 -0
  226. package/src/webpack/configs/plugins/virtual-modules-plugin.ts +27 -16
  227. package/src/webpack/configs/plugins-config.ts +4 -0
  228. package/src/webpack/configs/rules/css-rules.ts +19 -20
  229. package/src/webpack/configs/utils/get-bundle-type.ts +22 -0
  230. package/src/webpack/configs/utils/index.ts +1 -0
  231. package/src/webpack/create-webpack-config.ts +46 -52
  232. package/src/webpack/types.ts +4 -0
  233. package/src/webpack/utils/index.ts +1 -0
  234. package/src/webpack/utils/stringify-config.ts +19 -0
@@ -196,7 +196,6 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
196
196
  expect(subject().plugins).toContainEqual(
197
197
  new HtmlWebpackPlugin({
198
198
  title: 'ServiceTitan',
199
- hash: true,
200
199
  templateParameters: { splitByEntry },
201
200
  ...overrides.plugins?.HtmlWebpackPlugin,
202
201
  inject: false,
@@ -307,6 +306,17 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
307
306
  );
308
307
  });
309
308
 
309
+ test('configures "externals"', () => {
310
+ expect(subject().externals).toEqual(
311
+ Object.fromEntries(
312
+ Object.entries(sharedDependencies).map(([name, value]) => [
313
+ name,
314
+ `${value}['${options!.name}']`,
315
+ ])
316
+ )
317
+ );
318
+ });
319
+
310
320
  describe('when package does not share design system', () => {
311
321
  beforeEach(() => delete sharedDependencies['@servicetitan/design-system']);
312
322
 
@@ -335,6 +345,12 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
335
345
  })
336
346
  );
337
347
  });
348
+
349
+ test('appends "/headless" to output path', () => {
350
+ expect(subject().output!.path).toEqual(
351
+ path.join(process.cwd(), destination, 'bundle', 'headless')
352
+ );
353
+ });
338
354
  });
339
355
 
340
356
  describe.each([webpackDevConfigFileName, webpackProdConfigFileName])(
@@ -383,6 +399,14 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
383
399
  test('configures "output.uniqueName"', () => {
384
400
  expect(subject().output?.uniqueName).toEqual(options.name);
385
401
  });
402
+
403
+ describe('when headless option is set to true', () => {
404
+ beforeEach(() => (options.headless = true));
405
+
406
+ test('omits optimization.runtimeChunk', () => {
407
+ expect(subject().optimization?.runtimeChunk).toBeUndefined();
408
+ });
409
+ });
386
410
  });
387
411
  });
388
412
  });
@@ -446,15 +446,6 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
446
446
  itConfiguresCodeCoverage();
447
447
  });
448
448
 
449
- test('configures ".css" rule to exclude .module.css files', () => {
450
- const cssRule: any = subject().module?.rules?.find(({ test: regexp }: { test: RegExp }) =>
451
- regexp.test('.css')
452
- );
453
- expect(
454
- ['design-system.css', 'foo.module.css', 'foo.css'].filter(path => cssRule.exclude(path))
455
- ).toEqual(['foo.module.css']);
456
- });
457
-
458
449
  test('configures MomentLocalesPlugin plugin', () => {
459
450
  expect(subject().plugins).toContainEqual(
460
451
  new MomentLocalesPlugin({ localesToKeep: ['en-au', 'en-ca', 'en-gb'] })
@@ -473,7 +464,6 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
473
464
  expect(subject().plugins).toContainEqual(
474
465
  new HtmlWebpackPlugin({
475
466
  title: 'ServiceTitan',
476
- hash: true,
477
467
  templateParameters: { splitByEntry },
478
468
  ...overrides.plugins?.HtmlWebpackPlugin,
479
469
  })
@@ -547,52 +537,6 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
547
537
  );
548
538
  });
549
539
  });
550
-
551
- describe('headless option is true', () => {
552
- beforeEach(() => setOptions({ headless: true }));
553
-
554
- test('configures "output.path"', () => {
555
- expect(subject().output!.path).toEqual(
556
- path.join(process.cwd(), destination, 'bundle', 'headless')
557
- );
558
- });
559
-
560
- describe('when mode is "production"', () => {
561
- beforeEach(() => {
562
- overrides.configuration ??= {};
563
- Object.assign(overrides.configuration, { mode: 'production' });
564
- });
565
-
566
- test('omits optimization.runtimeChunk', () => {
567
- expect(subject().optimization?.runtimeChunk).toBeUndefined();
568
- });
569
- });
570
- });
571
- });
572
- });
573
-
574
- describe('when embed option is set to true', () => {
575
- beforeEach(() => setOptions({ embed: true, name: packageName }));
576
-
577
- test('configures "externals"', () => {
578
- expect(subject().externals).toEqual(
579
- Object.fromEntries(
580
- Object.entries(sharedDependencies).map(([name, value]) => [
581
- name,
582
- `${value}['${options!.name}']`,
583
- ])
584
- )
585
- );
586
- });
587
-
588
- describe('when package is web component', () => {
589
- beforeEach(() => jest.mocked(isWebComponent).mockReturnValue(true));
590
-
591
- test('configures "output.path"', () => {
592
- expect(subject().output!.path).toEqual(
593
- path.join(process.cwd(), destination, 'bundle', 'light')
594
- );
595
- });
596
540
  });
597
541
  });
598
542
 
@@ -622,7 +566,7 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
622
566
  expect(subject().output?.filename).toBe('[name].[contenthash].bundle.js');
623
567
  });
624
568
 
625
- test('configures "output.clean"', () => {
569
+ test('configures "output.clean" to true', () => {
626
570
  expect(subject().output?.clean).toBe(true);
627
571
  });
628
572
 
@@ -712,4 +656,12 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
712
656
  });
713
657
  });
714
658
  });
659
+
660
+ describe('with "emitExposedDependencies"', () => {
661
+ beforeEach(() => (options = { emitExposedDependencies: true }));
662
+
663
+ test('throws error', () => {
664
+ expect(subject).toThrow('package does not expose shared dependencies');
665
+ });
666
+ });
715
667
  });
@@ -0,0 +1,37 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import { Configuration } from 'webpack';
4
+ import { findUp } from '../../utils';
5
+ import { Context, Overrides } from './types';
6
+
7
+ type Config = Configuration['cache'];
8
+ type Result = Pick<Configuration, 'cache' | 'infrastructureLogging'> | undefined;
9
+
10
+ const CACHE_VERSION = '1';
11
+
12
+ export function cacheConfig(context: Context, _overrides: Overrides): Result {
13
+ const { emitExposedDependencies } = context;
14
+ if (!emitExposedDependencies) {
15
+ return;
16
+ }
17
+
18
+ const packageLock = findPackageLock();
19
+ const cache: Config = {
20
+ type: 'filesystem',
21
+ version: CACHE_VERSION,
22
+ ...(packageLock ? { buildDependencies: { packageLock: [packageLock] } } : {}),
23
+ };
24
+
25
+ return {
26
+ cache,
27
+ // Suppress "Serializing big strings impacts deserialization performance" warnings
28
+ infrastructureLogging: { level: 'error' },
29
+ };
30
+ }
31
+
32
+ function findPackageLock() {
33
+ return findUp(directory => {
34
+ const lockFile = path.resolve(path.join(directory), 'package-lock.json');
35
+ return fs.existsSync(lockFile) ? lockFile : undefined;
36
+ });
37
+ }
@@ -15,7 +15,7 @@ type DevServerConfig = NonNullable<Configuration['devServer']>;
15
15
  type Result = Pick<Configuration, 'devServer'> | undefined;
16
16
 
17
17
  export function devServerConfig(context: Context, overrides: Overrides): Result {
18
- if (context.isProduction || isDevServerDisabled()) {
18
+ if (context.isProduction || context.emitExposedDependencies || isDevServerDisabled()) {
19
19
  return;
20
20
  }
21
21
 
@@ -1,23 +1,33 @@
1
1
  import fs from 'fs';
2
2
  import { Configuration } from 'webpack';
3
3
  import { getModuleEntryPath } from '../utils';
4
+ import { getDesignSystemPath } from './plugins';
4
5
  import { Context, Overrides } from './types';
5
6
 
6
7
  type Config = Configuration['entry'];
7
8
  type Result = Pick<Configuration, 'entry'>;
8
9
 
9
10
  export function entryConfig(context: Context, _: Overrides): Result {
10
- const { destination, isExposeSharedDependencies, sharedDependencies, source } = context;
11
+ const entry: Config = context.emitExposedDependencies
12
+ ? getSharedEntryPoints(context)
13
+ : getIndexEntryPoint(context);
11
14
 
15
+ return { entry };
16
+ }
17
+
18
+ function getIndexEntryPoint({ source, destination }: Context): Config {
12
19
  const index = fs.existsSync(`./${source}/index.js`)
13
20
  ? `./${source}/index`
14
21
  : `./${destination}/index`;
22
+ return { main: [index] };
23
+ }
15
24
 
16
- const entry: Config = { main: [index] };
17
-
18
- if (isExposeSharedDependencies) {
19
- (entry.main as string[]).push(...Object.keys(sharedDependencies).map(getModuleEntryPath));
20
- }
21
-
22
- return { entry };
25
+ function getSharedEntryPoints(context: Context): Config {
26
+ const { sharedDependencies } = context;
27
+ return {
28
+ shared: Object.keys(sharedDependencies).map(getModuleEntryPath),
29
+ ...(sharedDependencies['@servicetitan/design-system']
30
+ ? { 'design-system': getDesignSystemPath(context) }
31
+ : {}),
32
+ };
23
33
  }
@@ -5,11 +5,12 @@ type Config = Configuration['externals'];
5
5
  type Result = Pick<Configuration, 'externals'> | undefined;
6
6
 
7
7
  export function externalsConfig(context: Context, _: Overrides): Result {
8
- const { embed, name, sharedDependencies } = context;
9
- if (!embed) {
8
+ if (!needsExternals(context)) {
10
9
  return;
11
10
  }
12
11
 
12
+ const { sharedDependencies } = context;
13
+ const name = context.name || context.packageData.name;
13
14
  const externals: Config = Object.fromEntries(
14
15
  Object.entries(sharedDependencies).map(([dependency, variable]) => [
15
16
  dependency,
@@ -19,3 +20,7 @@ export function externalsConfig(context: Context, _: Overrides): Result {
19
20
 
20
21
  return { externals };
21
22
  }
23
+
24
+ function needsExternals({ embed, emitExposedDependencies, isExposeSharedDependencies }: Context) {
25
+ return !!embed || (isExposeSharedDependencies && !emitExposedDependencies);
26
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './amd-config';
2
+ export * from './cache-config';
2
3
  export * from './dev-server-config';
3
4
  export * from './devtool-config';
4
5
  export * from './entry.config';
@@ -8,7 +8,7 @@ type ConfigWithCacheGroups = Config & { splitChunks: { cacheGroups: Record<strin
8
8
  type Result = Pick<Configuration, 'optimization'>;
9
9
 
10
10
  export function optimizationConfig(context: Context, _: Overrides): Result {
11
- const { headless, isProduction } = context;
11
+ const { emitExposedDependencies, headless, isProduction } = context;
12
12
 
13
13
  const optimization: ConfigWithCacheGroups = {
14
14
  chunkIds: isProduction ? 'deterministic' : 'named',
@@ -17,7 +17,9 @@ export function optimizationConfig(context: Context, _: Overrides): Result {
17
17
  splitChunks: { cacheGroups: {} },
18
18
  };
19
19
 
20
- if (!headless && isProduction) {
20
+ if (emitExposedDependencies) {
21
+ optimization.runtimeChunk = false;
22
+ } else if (!headless && isProduction) {
21
23
  optimization.runtimeChunk = 'single';
22
24
  }
23
25
 
@@ -58,19 +60,13 @@ function minimizeConfig(optimization: ConfigWithCacheGroups, context: Context) {
58
60
  }
59
61
 
60
62
  function sharedDependenciesConfig(optimization: ConfigWithCacheGroups, context: Context) {
61
- const { headless, isExposeSharedDependencies } = context;
62
- if (!isExposeSharedDependencies || headless) {
63
+ const { emitExposedDependencies } = context;
64
+ if (!emitExposedDependencies) {
63
65
  return;
64
66
  }
65
67
 
66
68
  Object.assign(optimization.splitChunks.cacheGroups, {
67
- 'design-system': {
68
- test: /[\\/]node_modules[\\/]@servicetitan[\\/](tokens|anvil-fonts|design-system)[\\/].*\.css$/,
69
- name: 'design-system',
70
- chunks: 'all',
71
- enforce: true,
72
- },
73
- 'anvil2': {
69
+ anvil2: {
74
70
  test: /@servicetitan[\\/]anvil2[\\/].*\.css$/,
75
71
  name: 'anvil2',
76
72
  chunks: 'all',
@@ -1,27 +1,29 @@
1
1
  import path from 'path';
2
2
  import { Configuration } from 'webpack';
3
3
  import { Context, Overrides } from './types';
4
+ import { getBundleType } from './utils';
4
5
 
5
6
  type Config = Configuration['output'];
6
7
  type Result = Pick<Configuration, 'output'>;
7
8
 
8
9
  export function outputConfig(context: Context, _: Overrides): Result {
9
- const { destination, embed, headless, isProduction, isWebComponent, name } = context;
10
-
11
- const bundleDir = headless ? 'headless' : embed ? 'light' : 'full';
10
+ const { emitExposedDependencies, isProduction, isWebComponent, name } = context;
12
11
 
13
12
  const output: Config = {
14
13
  filename: '[name].bundle.js',
15
- path: isWebComponent
16
- ? path.join(process.cwd(), `${destination}/bundle/${bundleDir}`)
17
- : path.join(process.cwd(), `${destination}/bundle`),
14
+ path: getOutputPath(context),
15
+ ...(emitExposedDependencies ? { chunkLoadingGlobal: `sharedChunk${name}` } : {}),
18
16
  };
19
17
 
20
18
  if (isProduction) {
21
19
  const cdnPath = process.env.CLIENT_CDN_PATH;
20
+ const exposedDependenciesDir = getBundleType({ ...context, emitExposedDependencies: true });
22
21
  Object.assign(output, {
23
22
  filename: '[name].[contenthash].bundle.js',
24
- clean: true,
23
+ clean:
24
+ exposedDependenciesDir && !emitExposedDependencies
25
+ ? { keep: new RegExp(exposedDependenciesDir) }
26
+ : true,
25
27
  ...(isWebComponent ? { uniqueName: name } : {}),
26
28
  ...(cdnPath ? { publicPath: cdnPath } : {}),
27
29
  });
@@ -29,3 +31,17 @@ export function outputConfig(context: Context, _: Overrides): Result {
29
31
 
30
32
  return { output };
31
33
  }
34
+
35
+ function getOutputPath(context: Context) {
36
+ const { destination, emitExposedDependencies } = context;
37
+
38
+ const subdir = getBundleType(context) ?? '';
39
+ if (typeof emitExposedDependencies === 'object') {
40
+ const { output } = emitExposedDependencies;
41
+ if (output?.path) {
42
+ return path.join(output.path, subdir);
43
+ }
44
+ }
45
+
46
+ return path.join(process.cwd(), destination, 'bundle', subdir);
47
+ }
@@ -1,23 +1,32 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
1
3
  import { WebpackAssetsManifest } from 'webpack-assets-manifest';
2
4
  import { Context, Overrides } from '../types';
3
5
  import { generateMetadata } from '../utils';
6
+ import { readJsonSafe } from '../../../utils';
7
+
8
+ interface Metadata {
9
+ entryPointsPath: string;
10
+ }
11
+
12
+ interface EntryPoints {
13
+ css: string[];
14
+ js: string[];
15
+ }
4
16
 
5
17
  export function assetsManifestPlugin(context: Context, _: Overrides) {
6
- if (!context.isWebComponent) {
18
+ if (!(context.isWebComponent || context.emitExposedDependencies)) {
7
19
  return;
8
20
  }
9
21
 
10
22
  return new WebpackAssetsManifest({
11
23
  entrypoints: true,
12
24
  output: 'entrypoints.json',
13
- transform(assets) {
14
- const entrypoints = assets.entrypoints as Record<
15
- string,
16
- { assets: Record<string, string[]> }
17
- >;
18
-
25
+ transform(assets: {
26
+ entrypoints: Record<string, { assets: Record<string, string[]> }>;
27
+ }): EntryPoints {
19
28
  const getAssetsByType = (type: string) => {
20
- return Object.values(entrypoints)
29
+ return Object.values(assets.entrypoints)
21
30
  .map(({ assets }) => assets[type] ?? [])
22
31
  .flat();
23
32
  };
@@ -28,8 +37,35 @@ export function assetsManifestPlugin(context: Context, _: Overrides) {
28
37
  };
29
38
  },
30
39
  // eslint-disable-next-line @typescript-eslint/require-await
31
- done: async (_manifest: WebpackAssetsManifest) => {
32
- generateMetadata(context);
40
+ done: async (manifest: WebpackAssetsManifest) => {
41
+ if (context.emitExposedDependencies) {
42
+ generateExposedDependenciesMetadata(context, manifest);
43
+ } else {
44
+ generateMetadata(context);
45
+ }
33
46
  },
34
47
  });
35
48
  }
49
+
50
+ export function getExposedDependenciesEntryPoints(context: Context) {
51
+ const metadataPath = getExposedDependenciesMetadataPath(context);
52
+ const metadata = readJsonSafe<Metadata>(metadataPath);
53
+ if (metadata?.entryPointsPath) {
54
+ return readJsonSafe<EntryPoints>(metadata.entryPointsPath);
55
+ }
56
+ }
57
+
58
+ function generateExposedDependenciesMetadata(context: Context, manifest: WebpackAssetsManifest) {
59
+ const { destination } = context;
60
+ if (!fs.existsSync(destination)) {
61
+ fs.mkdirSync(destination, { recursive: true });
62
+ }
63
+
64
+ const metadataPath = getExposedDependenciesMetadataPath(context);
65
+ const metadata: Metadata = { entryPointsPath: manifest.getOutputPath() };
66
+ fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
67
+ }
68
+
69
+ function getExposedDependenciesMetadataPath({ destination }: Context) {
70
+ return path.join(destination, 'exposed-dependencies-metadata.json');
71
+ }
@@ -2,6 +2,7 @@ import path from 'path';
2
2
  import os from 'os';
3
3
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
4
4
  import { Context, Overrides } from '../types';
5
+ import { getBundleType } from '../utils';
5
6
 
6
7
  export function bundleAnalyzerPlugin(context: Context, _: Overrides) {
7
8
  if (!context.buildStat) {
@@ -15,12 +16,6 @@ export function bundleAnalyzerPlugin(context: Context, _: Overrides) {
15
16
  });
16
17
  }
17
18
 
18
- function getBundleType({ embed, isWebComponent }: Context) {
19
- if (isWebComponent) {
20
- return embed ? 'light' : 'full';
21
- }
22
- }
23
-
24
19
  function getReportFileName(context: Context) {
25
20
  const type = getBundleType(context);
26
21
  const qualifier = type ? `-${type}` : '';
@@ -3,11 +3,12 @@ import { getPackageDependencyVersion } from '../../../utils';
3
3
  import { Context, Overrides } from '../types';
4
4
 
5
5
  export function defineExposedDependenciesPlugin(context: Context, _: Overrides) {
6
- const { isExposeSharedDependencies, packageData, sharedDependencies } = context;
7
- if (!isExposeSharedDependencies) {
6
+ const { emitExposedDependencies, isExposeSharedDependencies } = context;
7
+ if (!isExposeSharedDependencies || emitExposedDependencies) {
8
8
  return;
9
9
  }
10
10
 
11
+ const { packageData, sharedDependencies } = context;
11
12
  return new DefinePlugin({
12
13
  // eslint-disable-next-line @typescript-eslint/naming-convention
13
14
  EXPOSED_DEPENDENCIES: JSON.stringify(
@@ -3,8 +3,8 @@ import { Context, Overrides } from '../types';
3
3
  import { getLaunchDarklySdkVersion } from '../utils/get-launchdarkly-sdk-version';
4
4
 
5
5
  export function defineExposedInstanceDependenciesPlugin(context: Context, _: Overrides) {
6
- const { isExposeSharedDependencies } = context;
7
- if (!isExposeSharedDependencies) {
6
+ const { emitExposedDependencies, isExposeSharedDependencies } = context;
7
+ if (!isExposeSharedDependencies || emitExposedDependencies) {
8
8
  return;
9
9
  }
10
10
 
@@ -4,12 +4,12 @@ import { splitByEntry } from '../../utils';
4
4
  import { Context, Overrides } from '../types';
5
5
 
6
6
  export function htmlPlugin(
7
- { embed, headless, isWebComponent, name }: Context,
7
+ { embed, emitExposedDependencies, headless, isWebComponent, name }: Context,
8
8
  { plugins }: Overrides
9
9
  ) {
10
10
  const { HtmlWebpackPlugin: htmlWebpackPluginOptions = {} } = plugins ?? {};
11
11
 
12
- if (embed || headless) {
12
+ if (embed || headless || emitExposedDependencies) {
13
13
  return;
14
14
  }
15
15
 
@@ -17,7 +17,6 @@ export function htmlPlugin(
17
17
  deepmerge<HtmlWebpackPluginOptions>(
18
18
  {
19
19
  title: 'ServiceTitan',
20
- hash: true,
21
20
  ...(isWebComponent
22
21
  ? {
23
22
  inject: false,
@@ -0,0 +1,28 @@
1
+ import HtmlWebpackTagsPlugin from 'html-webpack-tags-plugin';
2
+ import { Context, Overrides } from '../types';
3
+ import { getBundleType } from '../utils';
4
+ import { getExposedDependenciesEntryPoints } from './assets-manifest-plugin';
5
+
6
+ export function htmlTagsPlugin(context: Context, _overrides: Overrides) {
7
+ const { emitExposedDependencies, isExposeSharedDependencies } = context;
8
+ if (!isExposeSharedDependencies || emitExposedDependencies) {
9
+ return;
10
+ }
11
+
12
+ const sharedEntryPoints = getSharedEntryPoints(context);
13
+ if (!sharedEntryPoints.length) {
14
+ return;
15
+ }
16
+
17
+ return new HtmlWebpackTagsPlugin({ tags: sharedEntryPoints, append: false });
18
+ }
19
+
20
+ function getSharedEntryPoints(context: Context) {
21
+ const dirname = getBundleType({ ...context, emitExposedDependencies: true });
22
+ if (!dirname) {
23
+ return [];
24
+ }
25
+
26
+ const { css = [], js = [] } = getExposedDependenciesEntryPoints(context) ?? {};
27
+ return [...css, ...js].map(name => `${dirname}/${name}`);
28
+ }
@@ -5,8 +5,10 @@ export * from './define-exposed-instance-dependencies-plugin';
5
5
  export * from './define-web-component-name-plugin';
6
6
  export * from './filter-warnings-plugin';
7
7
  export * from './html-plugin';
8
+ export * from './html-tags-plugin';
8
9
  export * from './ignore-plugin';
9
10
  export * from './mini-css-extract-plugin';
10
11
  export * from './moment-locales-plugin';
12
+ export * from './remove-empty-scripts-plugin';
11
13
  export * from './virtual-modules-plugin';
12
14
  export * from './watch-run-plugin';
@@ -0,0 +1,11 @@
1
+ import RemoveEmptyScriptsPlugin from 'webpack-remove-empty-scripts';
2
+ import { Context, Overrides } from '../types';
3
+
4
+ export function removeEmptyScriptsPlugin(context: Context, _overrides: Overrides) {
5
+ const { emitExposedDependencies } = context;
6
+ if (!emitExposedDependencies) {
7
+ return;
8
+ }
9
+
10
+ return new RemoveEmptyScriptsPlugin();
11
+ }
@@ -4,25 +4,27 @@ import VirtualModulesPlugin from 'webpack-virtual-modules';
4
4
  import { Context, Overrides } from '../types';
5
5
 
6
6
  export function virtualModulesPlugin(context: Context, _: Overrides) {
7
- if (!context.isWebComponent) {
8
- return;
9
- }
10
-
11
7
  const { destination } = context;
12
8
 
13
- const indexPath = path.join(process.cwd(), `${destination}/index`);
14
- const modules: ConstructorParameters<typeof VirtualModulesPlugin>[0] = {
15
- [indexPath]: indexCode(context),
16
- };
9
+ const modules: ConstructorParameters<typeof VirtualModulesPlugin>[0] = {};
10
+
11
+ if (context.isWebComponent) {
12
+ const indexPath = path.join(process.cwd(), destination, 'index');
13
+ modules[indexPath] = indexCode(context);
14
+ }
17
15
 
18
16
  if (needsToIncludeDesignSystem(context)) {
19
- const designSystemPath = path.join(process.cwd(), `${destination}/design-system.css`);
17
+ const designSystemPath = getDesignSystemPath(context);
20
18
  if (!fs.existsSync(designSystemPath)) {
21
19
  modules[designSystemPath] = designSystemCode();
22
20
  }
23
21
  }
24
22
 
25
- return new VirtualModulesPlugin(modules);
23
+ return Object.keys(modules).length ? new VirtualModulesPlugin(modules) : undefined;
24
+ }
25
+
26
+ export function getDesignSystemPath({ destination }: Context) {
27
+ return path.join(process.cwd(), destination, 'design-system.css');
26
28
  }
27
29
 
28
30
  function designSystemCode() {
@@ -50,13 +52,22 @@ function indexCode(context: Context) {
50
52
  ].join('\n');
51
53
  }
52
54
 
53
- function needsToIncludeDesignSystem({ embed, headless, packageData, sharedDependencies }: Context) {
55
+ function needsToIncludeDesignSystem({
56
+ embed,
57
+ emitExposedDependencies,
58
+ headless,
59
+ isWebComponent,
60
+ packageData,
61
+ sharedDependencies,
62
+ }: Context) {
54
63
  return (
55
- !headless &&
56
- // Depends on design system
57
- !!packageData.dependencies['@servicetitan/design-system'] &&
58
- // ... and is not light bundle with private copy of design system
59
- !(embed && !!sharedDependencies['@servicetitan/design-system'])
64
+ (!!emitExposedDependencies && !!sharedDependencies['@servicetitan/design-system']) ||
65
+ (isWebComponent &&
66
+ !headless &&
67
+ // Depends on design system
68
+ !!packageData.dependencies['@servicetitan/design-system'] &&
69
+ // ... and is not light bundle with private copy of design system
70
+ !(embed && !!sharedDependencies['@servicetitan/design-system']))
60
71
  );
61
72
  }
62
73
 
@@ -7,9 +7,11 @@ import {
7
7
  defineWebComponentNamePlugin,
8
8
  filterWarningsPlugin,
9
9
  htmlPlugin,
10
+ htmlTagsPlugin,
10
11
  ignorePlugin,
11
12
  miniCssExtractPlugin,
12
13
  momentLocalesPlugin,
14
+ removeEmptyScriptsPlugin,
13
15
  virtualModulesPlugin,
14
16
  watchRunPlugin,
15
17
  } from './plugins';
@@ -27,9 +29,11 @@ export function pluginsConfig(context: Context, overrides: Overrides): Result {
27
29
  defineWebComponentNamePlugin,
28
30
  filterWarningsPlugin,
29
31
  htmlPlugin,
32
+ htmlTagsPlugin,
30
33
  ignorePlugin,
31
34
  miniCssExtractPlugin,
32
35
  momentLocalesPlugin,
36
+ removeEmptyScriptsPlugin,
33
37
  virtualModulesPlugin,
34
38
  watchRunPlugin,
35
39
  ]