@servicetitan/startup 27.4.0 → 28.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/cli/commands/get-command.d.ts.map +1 -1
  2. package/dist/cli/commands/get-command.js +4 -2
  3. package/dist/cli/commands/get-command.js.map +1 -1
  4. package/dist/cli/commands/init.d.ts +0 -1
  5. package/dist/cli/commands/init.d.ts.map +1 -1
  6. package/dist/cli/commands/init.js +39 -9
  7. package/dist/cli/commands/init.js.map +1 -1
  8. package/dist/cli/commands/mfe-package-clean.d.ts +14 -0
  9. package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -0
  10. package/dist/cli/commands/mfe-package-clean.js +124 -0
  11. package/dist/cli/commands/mfe-package-clean.js.map +1 -0
  12. package/dist/cli/commands/mfe-package-publish.d.ts +20 -0
  13. package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -0
  14. package/dist/cli/commands/mfe-package-publish.js +153 -0
  15. package/dist/cli/commands/mfe-package-publish.js.map +1 -0
  16. package/dist/cli/commands/mfe-publish.d.ts +6 -30
  17. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  18. package/dist/cli/commands/mfe-publish.js +14 -226
  19. package/dist/cli/commands/mfe-publish.js.map +1 -1
  20. package/dist/cli/utils/assets-copy.d.ts.map +1 -1
  21. package/dist/cli/utils/assets-copy.js +3 -3
  22. package/dist/cli/utils/assets-copy.js.map +1 -1
  23. package/dist/cli/utils/cli-os.d.ts +9 -2
  24. package/dist/cli/utils/cli-os.d.ts.map +1 -1
  25. package/dist/cli/utils/cli-os.js +16 -8
  26. package/dist/cli/utils/cli-os.js.map +1 -1
  27. package/dist/cli/utils/styles-copy.d.ts.map +1 -1
  28. package/dist/cli/utils/styles-copy.js +3 -3
  29. package/dist/cli/utils/styles-copy.js.map +1 -1
  30. package/dist/utils/get-branch-configs.d.ts +3 -0
  31. package/dist/utils/get-branch-configs.d.ts.map +1 -0
  32. package/dist/utils/get-branch-configs.js +18 -0
  33. package/dist/utils/get-branch-configs.js.map +1 -0
  34. package/dist/utils/get-configuration.d.ts +13 -3
  35. package/dist/utils/get-configuration.d.ts.map +1 -1
  36. package/dist/utils/get-configuration.js +28 -5
  37. package/dist/utils/get-configuration.js.map +1 -1
  38. package/dist/utils/get-folders.js +2 -2
  39. package/dist/utils/get-folders.js.map +1 -1
  40. package/dist/utils/get-jest-config.d.ts.map +1 -1
  41. package/dist/utils/get-jest-config.js +2 -10
  42. package/dist/utils/get-jest-config.js.map +1 -1
  43. package/dist/webpack/configs/dev-server-config.d.ts.map +1 -1
  44. package/dist/webpack/configs/dev-server-config.js +1 -1
  45. package/dist/webpack/configs/dev-server-config.js.map +1 -1
  46. package/dist/webpack/configs/plugins/ignore-plugin/is-optional-anvil-peer-dependency.js +1 -1
  47. package/dist/webpack/configs/plugins/ignore-plugin/is-optional-anvil-peer-dependency.js.map +1 -1
  48. package/dist/webpack/configs/plugins/index.d.ts +1 -0
  49. package/dist/webpack/configs/plugins/index.d.ts.map +1 -1
  50. package/dist/webpack/configs/plugins/index.js +1 -0
  51. package/dist/webpack/configs/plugins/index.js.map +1 -1
  52. package/dist/webpack/configs/plugins/virtual-modules-plugin.d.ts.map +1 -1
  53. package/dist/webpack/configs/plugins/virtual-modules-plugin.js +15 -5
  54. package/dist/webpack/configs/plugins/virtual-modules-plugin.js.map +1 -1
  55. package/dist/webpack/configs/plugins/watch-run-plugin.d.ts +8 -0
  56. package/dist/webpack/configs/plugins/watch-run-plugin.d.ts.map +1 -0
  57. package/dist/webpack/configs/plugins/watch-run-plugin.js +26 -0
  58. package/dist/webpack/configs/plugins/watch-run-plugin.js.map +1 -0
  59. package/dist/webpack/configs/plugins-config.d.ts.map +1 -1
  60. package/dist/webpack/configs/plugins-config.js +1 -0
  61. package/dist/webpack/configs/plugins-config.js.map +1 -1
  62. package/dist/webpack/configs/types.d.ts +16 -0
  63. package/dist/webpack/configs/types.d.ts.map +1 -1
  64. package/dist/webpack/configs/utils/generate-metadata.d.ts.map +1 -1
  65. package/dist/webpack/configs/utils/generate-metadata.js +3 -3
  66. package/dist/webpack/configs/utils/generate-metadata.js.map +1 -1
  67. package/jest/jest-preset.js +9 -0
  68. package/package.json +18 -23
  69. package/src/cli/commands/__tests__/init.test.ts +108 -28
  70. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +45 -6
  71. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +77 -7
  72. package/src/cli/commands/__tests__/mfe-publish.test.ts +19 -1
  73. package/src/cli/commands/__tests__/tests.test.ts +4 -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 +143 -0
  77. package/src/cli/commands/mfe-package-publish.ts +189 -0
  78. package/src/cli/commands/mfe-publish.ts +18 -298
  79. package/src/cli/utils/__tests__/assets-copy.test.ts +3 -7
  80. package/src/cli/utils/__tests__/cli-os.test.ts +41 -6
  81. package/src/cli/utils/__tests__/eslint.test.ts +4 -0
  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/cli-os.ts +20 -8
  85. package/src/cli/utils/styles-copy.ts +3 -3
  86. package/src/utils/__tests__/get-branch-configs.test.ts +36 -0
  87. package/src/utils/__tests__/get-configuration.test.ts +65 -0
  88. package/src/utils/__tests__/get-jest-config.test.ts +1 -7
  89. package/src/utils/__tests__/load-shared-dependencies.test.ts +82 -88
  90. package/src/utils/get-branch-configs.ts +17 -0
  91. package/src/utils/get-configuration.ts +45 -7
  92. package/src/utils/get-folders.ts +1 -1
  93. package/src/utils/get-jest-config.ts +2 -10
  94. package/src/webpack/__tests__/create-webpack-config-shared-dependencies.test.ts +0 -1
  95. package/src/webpack/__tests__/create-webpack-config-web-component.test.ts +47 -13
  96. package/src/webpack/__tests__/create-webpack-config.test.ts +3 -2
  97. package/src/webpack/configs/dev-server-config.ts +2 -1
  98. package/src/webpack/configs/plugins/ignore-plugin/is-optional-anvil-peer-dependency.ts +1 -1
  99. package/src/webpack/configs/plugins/index.ts +1 -0
  100. package/src/webpack/configs/plugins/virtual-modules-plugin.ts +17 -5
  101. package/src/webpack/configs/plugins/watch-run-plugin.ts +23 -0
  102. package/src/webpack/configs/plugins-config.ts +2 -0
  103. package/src/webpack/configs/types.ts +19 -0
  104. package/src/webpack/configs/utils/generate-metadata.ts +5 -5
  105. package/tsconfig/base.json +1 -1
  106. package/template/.eslintrc.json +0 -3
  107. package/template/.gitignore +0 -21
  108. package/template/.npmrc +0 -3
  109. package/template/.prettierrc +0 -9
  110. package/template/.stylelintignore +0 -1
  111. package/template/.stylelintrc.json +0 -3
  112. package/template/.vscode/extensions.json +0 -18
  113. package/template/.vscode/settings.json +0 -4
  114. package/template/lerna.json +0 -4
  115. package/template/package.json +0 -32
  116. package/template/packages/application/package.json +0 -35
  117. package/template/packages/application/src/__tests__/app.test.tsx +0 -33
  118. package/template/packages/application/src/app.css +0 -3
  119. package/template/packages/application/src/app.tsx +0 -45
  120. package/template/packages/application/src/design-system.css +0 -3
  121. package/template/packages/application/src/index.tsx +0 -8
  122. package/template/packages/application/src/main-page.tsx +0 -5
  123. package/template/packages/application/src/second-page.tsx +0 -5
  124. package/template/packages/application/tsconfig.json +0 -13
  125. package/template/packages/feature-a/package.json +0 -19
  126. package/template/packages/feature-a/src/index.ts +0 -0
  127. package/template/packages/feature-a/tsconfig.json +0 -9
  128. package/template/packages/feature-b/package.json +0 -19
  129. package/template/packages/feature-b/src/index.ts +0 -0
  130. package/template/packages/feature-b/tsconfig.json +0 -9
  131. package/template/packages/feature-c/package.json +0 -19
  132. package/template/packages/feature-c/src/index.ts +0 -0
  133. package/template/packages/feature-c/tsconfig.json +0 -9
  134. package/template/setupTests.ts +0 -27
  135. package/template/tsconfig.test.json +0 -5
  136. package/template-react18/packages/application/package.json +0 -35
  137. package/template-react18/packages/application/src/index.tsx +0 -9
  138. package/template-react18/packages/feature-a/package.json +0 -19
  139. package/template-react18/packages/feature-b/package.json +0 -19
  140. package/template-react18/packages/feature-c/package.json +0 -19
  141. package/tsconfig.json +0 -13
@@ -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,9 +6,11 @@ import {
6
6
  } from 'child_process';
7
7
  import { log } from '../../utils';
8
8
 
9
+ type RunCommandOptions = SpawnOptionsWithoutStdio & { quiet?: boolean };
10
+
9
11
  export const runCommand = (
10
12
  command: string | (string | false)[],
11
- opts?: SpawnOptionsWithoutStdio
13
+ { quiet, ...spawnOptions }: RunCommandOptions = {}
12
14
  ): Promise<void> => {
13
15
  return new Promise((resolve, reject) => {
14
16
  const commandArray: string[] = Array.isArray(command)
@@ -28,15 +30,19 @@ export const runCommand = (
28
30
  return;
29
31
  }
30
32
 
31
- log.info(`run command ${fullCommand}`);
33
+ if (!quiet) {
34
+ log.info(`run command ${fullCommand}`);
35
+ }
32
36
 
33
- const proc = spawn(commandName, commandArray, opts);
37
+ const proc = spawn(commandName, commandArray, spawnOptions);
34
38
 
35
39
  proc.stdout.pipe(process.stdout);
36
40
  proc.stderr.pipe(process.stderr);
37
41
 
38
42
  proc.on('exit', function (code: any) {
39
- log.info(`command finished with code ${code}`, fullCommand);
43
+ if (!quiet) {
44
+ log.info(`command finished with code ${code}`, fullCommand);
45
+ }
40
46
 
41
47
  if (code) {
42
48
  reject();
@@ -47,9 +53,11 @@ export const runCommand = (
47
53
  });
48
54
  };
49
55
 
56
+ type RunCommandOutputOptions = ExecSyncOptionsWithBufferEncoding & { quiet?: boolean };
57
+
50
58
  export const runCommandOutput = (
51
59
  command: string | (string | false)[],
52
- options?: ExecSyncOptionsWithBufferEncoding
60
+ { quiet, ...execSyncOptions }: RunCommandOutputOptions = {}
53
61
  ): string => {
54
62
  const commandArray: string[] = Array.isArray(command)
55
63
  ? command
@@ -66,11 +74,15 @@ export const runCommandOutput = (
66
74
  throw new Error();
67
75
  }
68
76
 
69
- log.info(`run command ${fullCommand}`);
77
+ if (!quiet) {
78
+ log.info(`run command ${fullCommand}`);
79
+ }
70
80
 
71
- const result = execSync(fullCommand, options).toString();
81
+ const result = execSync(fullCommand, execSyncOptions).toString();
72
82
 
73
- log.info('command finished', result);
83
+ if (!quiet) {
84
+ log.info('command finished', result);
85
+ }
74
86
 
75
87
  return result;
76
88
  };
@@ -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 { styleExtensions } from '.';
6
5
 
7
- export async function stylesCopy() {
6
+ export function stylesCopy() {
8
7
  const { source, destination } = getFolders();
9
8
 
10
9
  log.info('Copying style files...');
11
10
 
12
- await util.promisify(cpx.copy)(`${source}/**/*.{${styleExtensions.join()}}`, destination);
11
+ cpx.copySync(`${source}/**/*.{${styleExtensions.join()}}`, destination);
12
+ return Promise.resolve();
13
13
  }
14
14
 
15
15
  export async function stylesCopyWatch() {
@@ -0,0 +1,36 @@
1
+ import { fs, vol } from 'memfs';
2
+
3
+ import { getBranchesConfigs } from '../get-branch-configs';
4
+
5
+ jest.mock('fs', () => fs);
6
+
7
+ describe(`[Startup] ${getBranchesConfigs.name}`, () => {
8
+ beforeEach(() => {
9
+ vol.reset();
10
+ });
11
+
12
+ const subject = () => getBranchesConfigs();
13
+
14
+ test('returns default configs', () => {
15
+ expect(subject()).toEqual({
16
+ develop: { publishTag: 'dev' },
17
+ dev: { publishTag: 'dev' },
18
+ next: { publishTag: 'next' },
19
+ master: { publishTag: 'prod' },
20
+ });
21
+ });
22
+
23
+ describe('when "branches" configs', () => {
24
+ beforeEach(() => {
25
+ vol.fromJSON({
26
+ 'package.json': JSON.stringify({
27
+ cli: { 'web-component': { branches: { qa: { publishTag: 'qs' } } } },
28
+ }),
29
+ });
30
+ });
31
+
32
+ test('returns configs', () => {
33
+ expect(subject()).toEqual({ qa: { publishTag: 'qs' } });
34
+ });
35
+ });
36
+ });
@@ -1,4 +1,5 @@
1
1
  import { fs, vol } from 'memfs';
2
+ import path from 'path';
2
3
 
3
4
  import {
4
5
  getConfiguration,
@@ -6,6 +7,8 @@ import {
6
7
  getESLintConfiguration,
7
8
  getJestConfiguration,
8
9
  getStylelintConfiguration,
10
+ getWebComponentBranchConfigs,
11
+ getWebComponentConfiguration,
9
12
  isBundle,
10
13
  isCustomStyleRules,
11
14
  isDevServerDisabled,
@@ -183,6 +186,67 @@ describe('[startup] Utils', () => {
183
186
  });
184
187
  });
185
188
 
189
+ describe(`${getWebComponentConfiguration.name}`, () => {
190
+ const subject = () => getWebComponentConfiguration();
191
+
192
+ itReturns(subject, undefined);
193
+
194
+ test('returns undefined', () => {
195
+ expect(subject()).toEqual(undefined);
196
+ });
197
+
198
+ describe('with "cli.web-component true"', () => {
199
+ beforeEach(() => mockPackageJson({ cli: { 'web-component': true } }));
200
+
201
+ test('returns empty object', () => {
202
+ expect(subject()).toEqual({});
203
+ });
204
+ });
205
+
206
+ describe('with "cli.web-component.{}"', () => {
207
+ beforeEach(() => {
208
+ mockPackageJson({ cli: { 'web-component': { branches: { foo: 'bar' } } } });
209
+ });
210
+
211
+ test('returns web-component config', () => {
212
+ expect(subject()).toEqual({ branches: { foo: 'bar' } });
213
+ });
214
+ });
215
+
216
+ describe('with "cli.web-component.path"', () => {
217
+ beforeEach(() => {
218
+ vol.fromJSON({
219
+ ...packageJsonFS({ cli: { 'web-component': './config.json' } }),
220
+ 'config.json': '',
221
+ });
222
+
223
+ jest.doMock(path.resolve('./config.json'), () => ({ branches: { foo: 'bar' } }), {
224
+ virtual: true,
225
+ });
226
+ });
227
+
228
+ test('returns web-component config', () => {
229
+ expect(subject()).toEqual({ branches: { foo: 'bar' } });
230
+ });
231
+ });
232
+ });
233
+
234
+ describe(`${getWebComponentBranchConfigs.name}`, () => {
235
+ const subject = () => getWebComponentBranchConfigs();
236
+
237
+ itReturns(subject, undefined);
238
+
239
+ describe('with "cli.web-component.branches"', () => {
240
+ const content = { cli: { 'web-component': { branches: { foo: 'bar' } } } };
241
+
242
+ beforeEach(() => mockPackageJson(content));
243
+
244
+ test('returns "cli.web-component.branches"', () => {
245
+ expect(subject()).toEqual(content.cli['web-component'].branches);
246
+ });
247
+ });
248
+ });
249
+
186
250
  describe.each([
187
251
  {
188
252
  fn: isBundle,
@@ -221,6 +285,7 @@ describe('[startup] Utils', () => {
221
285
  { cli: { 'web-component': true } },
222
286
  { cli: { 'web-component': { legacyRoot: true } } },
223
287
  { cli: { 'web-component': { legacyRoot: false } } },
288
+ { cli: { 'web-component': { branches: { develop: { publishTag: 'qa' } } } } },
224
289
  ],
225
290
  },
226
291
  {
@@ -19,13 +19,6 @@ describe('[startup] Utils', () => {
19
19
  testRunner: 'jest-circus/runner',
20
20
  transformIgnorePatterns: ['node_modules/(?!(@servicetitan|@react-hook|nanoid|axios)/)'],
21
21
  modulePathIgnorePatterns: ['<rootDir>/.*/__mocks__'],
22
- transform: {
23
- '^.+\\.jsx?$': [
24
- 'babel-jest',
25
- { presets: [['@babel/preset-env', { targets: { node: 'current' } }]] },
26
- ],
27
- '^.+\\.tsx?$': ['ts-jest', { tsconfig: './tsconfig.test.json' }],
28
- },
29
22
  moduleNameMapper: {
30
23
  '\\.(css|scss|less|png|svg|jpg|jpeg|gif|woff|woff2|eot|ttf|otf)$': 'identity-obj-proxy',
31
24
  },
@@ -34,6 +27,7 @@ describe('[startup] Utils', () => {
34
27
  coveragePathIgnorePatterns: ['^.+\\.d\\.ts$'],
35
28
  coverageReporters: ['html-spa', 'text', 'json', 'cobertura', 'lcov'],
36
29
  collectCoverageFrom: ['**/*.{ts,tsx}'],
30
+ preset: expect.stringMatching(/startup[\\\/]+jest/),
37
31
  };
38
32
 
39
33
  beforeEach(() => {
@@ -1,10 +1,25 @@
1
1
  import { loadSharedDependencies } from '../load-shared-dependencies';
2
2
 
3
- describe('[Startup] utils:loadSharedDependencies', () => {
4
- const getPackageDependencies = (...excluded: string[]) =>
5
- Object.entries({
6
- '@servicetitan/confirm': '^0.0.0',
3
+ describe(`[startup] utils:${loadSharedDependencies.name}`, () => {
4
+ const defaultSharedDependencies = {
5
+ '@servicetitan/design-system': 'SharedDependencies.ServiceTitan.DesignSystem',
6
+ '@servicetitan/anvil2': 'SharedDependencies.ServiceTitan.Anvil2',
7
+ 'classnames': 'SharedDependencies.ClassNames',
8
+ 'formstate': 'SharedDependencies.FormState',
9
+ 'mobx': 'SharedDependencies.MobX',
10
+ 'mobx-react': 'SharedDependencies.MobXReact',
11
+ 'mobx-utils': 'SharedDependencies.MobXUtils',
12
+ 'react': 'SharedDependencies.React',
13
+ 'react-dom': 'SharedDependencies.ReactDOM',
14
+ };
15
+ let packageDependencies: Record<string, string> | undefined;
16
+ let sharedDependencies: Record<string, string> | undefined;
17
+
18
+ beforeEach(() => {
19
+ packageDependencies = {
20
+ // Default shared dependencies
7
21
  '@servicetitan/design-system': '^0.0.0',
22
+ '@servicetitan/anvil2': '^0.0.0',
8
23
  'classnames': '^0.0.0',
9
24
  'formstate': '^0.0.0',
10
25
  'mobx': '^0.0.0',
@@ -12,103 +27,82 @@ describe('[Startup] utils:loadSharedDependencies', () => {
12
27
  'mobx-utils': '^0.0.0',
13
28
  'react': '^0.0.0',
14
29
  'react-dom': '^0.0.0',
30
+ // Other dependencies
31
+ '@servicetitan/confirm': '^0.0.0',
15
32
  'react-input-mask': '^0.0.0',
16
- }).reduce(
17
- (out, [d, v]) => ({
18
- ...out,
19
- ...(excluded.includes(d) ? {} : { [d]: v }),
20
- }),
21
- {}
22
- );
23
-
24
- test('return default dependencies when manual are not configured', () => {
25
- expect(loadSharedDependencies(getPackageDependencies(), undefined)).toEqual({
26
- '@servicetitan/design-system': 'SharedDependencies.ServiceTitan.DesignSystem',
27
- 'classnames': 'SharedDependencies.ClassNames',
28
- 'formstate': 'SharedDependencies.FormState',
29
- 'mobx': 'SharedDependencies.MobX',
30
- 'mobx-react': 'SharedDependencies.MobXReact',
31
- 'mobx-utils': 'SharedDependencies.MobXUtils',
32
- 'react': 'SharedDependencies.React',
33
- 'react-dom': 'SharedDependencies.ReactDOM',
34
- });
33
+ };
34
+ sharedDependencies = undefined;
35
35
  });
36
36
 
37
- test('return default dependencies except not used when manual are not configured', () => {
38
- expect(loadSharedDependencies(getPackageDependencies('mobx-utils'), undefined)).toEqual({
39
- '@servicetitan/design-system': 'SharedDependencies.ServiceTitan.DesignSystem',
40
- 'classnames': 'SharedDependencies.ClassNames',
41
- 'formstate': 'SharedDependencies.FormState',
42
- 'mobx': 'SharedDependencies.MobX',
43
- 'mobx-react': 'SharedDependencies.MobXReact',
44
- 'react': 'SharedDependencies.React',
45
- 'react-dom': 'SharedDependencies.ReactDOM',
46
- });
37
+ const subject = () => loadSharedDependencies(packageDependencies, sharedDependencies);
38
+
39
+ function omit(obj: Record<string, string>, ...exclude: string[]) {
40
+ return Object.fromEntries(Object.entries(obj).filter(([key]) => !exclude.includes(key)));
41
+ }
42
+
43
+ test('returns default shared dependencies', () => {
44
+ expect(subject()).toEqual(defaultSharedDependencies);
47
45
  });
48
46
 
49
- test('return manual dependencies if set', () => {
50
- expect(
51
- loadSharedDependencies(getPackageDependencies(), {
52
- '@servicetitan/confirm': 'SharedDependencies.ServiceTitan.Confirm',
53
- 'react-input-mask': 'SharedDependencies.ReactInputMask',
54
- })
55
- ).toEqual({
56
- '@servicetitan/confirm': 'SharedDependencies.ServiceTitan.Confirm',
57
- 'react-input-mask': 'SharedDependencies.ReactInputMask',
47
+ describe('when package does not depend on default shared dependency', () => {
48
+ beforeEach(() => delete packageDependencies!['@servicetitan/design-system']);
49
+
50
+ test('omits the default shared dependency', () => {
51
+ expect(subject()).toEqual(
52
+ omit(defaultSharedDependencies, '@servicetitan/design-system')
53
+ );
58
54
  });
59
55
  });
60
56
 
61
- test('return default dependencies within manual', () => {
62
- expect(
63
- loadSharedDependencies(getPackageDependencies(), {
64
- defaults: '',
65
- })
66
- ).toEqual({
67
- '@servicetitan/design-system': 'SharedDependencies.ServiceTitan.DesignSystem',
68
- 'classnames': 'SharedDependencies.ClassNames',
69
- 'formstate': 'SharedDependencies.FormState',
70
- 'mobx': 'SharedDependencies.MobX',
71
- 'mobx-react': 'SharedDependencies.MobXReact',
72
- 'mobx-utils': 'SharedDependencies.MobXUtils',
73
- 'react': 'SharedDependencies.React',
74
- 'react-dom': 'SharedDependencies.ReactDOM',
57
+ describe('with explicit shared dependencies', () => {
58
+ beforeEach(() => (sharedDependencies = {}));
59
+
60
+ test('returns specified dependencies', () => {
61
+ expect(subject()).toEqual(sharedDependencies);
75
62
  });
76
- });
77
63
 
78
- test('clear default dependencies when configured', () => {
79
- expect(
80
- loadSharedDependencies(getPackageDependencies(), {
81
- 'defaults': '',
82
- 'mobx-utils': '',
83
- })
84
- ).toEqual({
85
- '@servicetitan/design-system': 'SharedDependencies.ServiceTitan.DesignSystem',
86
- 'classnames': 'SharedDependencies.ClassNames',
87
- 'formstate': 'SharedDependencies.FormState',
88
- 'mobx': 'SharedDependencies.MobX',
89
- 'mobx-react': 'SharedDependencies.MobXReact',
90
- 'react': 'SharedDependencies.React',
91
- 'react-dom': 'SharedDependencies.ReactDOM',
64
+ describe('when explicit shared dependencies includes "defaults"', () => {
65
+ beforeEach(() => (sharedDependencies!.defaults = ''));
66
+
67
+ test('returns default shared dependencies', () => {
68
+ expect(subject()).toEqual(defaultSharedDependencies);
69
+ });
70
+
71
+ describe('when explicit shared dependency has falsy value', () => {
72
+ beforeEach(() => {
73
+ Object.assign(sharedDependencies!, { 'classnames': false, 'mobx-utils': '' });
74
+ });
75
+
76
+ test('omits the falsy dependency', () => {
77
+ expect(subject()).toEqual(
78
+ omit(defaultSharedDependencies, 'classnames', 'mobx-utils')
79
+ );
80
+ });
81
+ });
82
+
83
+ describe('when explicit shared dependency has non-default packages', () => {
84
+ beforeEach(() => {
85
+ Object.assign(sharedDependencies!, {
86
+ '@servicetitan/confirm': 'SharedDependencies.ServiceTitan.Confirm',
87
+ 'react-input-mask': 'SharedDependencies.ReactInputMask',
88
+ });
89
+ });
90
+
91
+ test('adds the non-default packages', () => {
92
+ expect(subject()).toEqual({
93
+ ...defaultSharedDependencies,
94
+ ...omit(sharedDependencies!, 'defaults'),
95
+ });
96
+ });
97
+ });
92
98
  });
93
99
  });
94
100
 
95
- test('mixes default dependencies and configured', () => {
96
- expect(
97
- loadSharedDependencies(getPackageDependencies(), {
98
- 'defaults': '',
99
- 'mobx-utils': '',
100
- 'react-input-mask': 'SharedDependencies.ReactInputMask',
101
- 'react-something': 'SharedDependencies.ReactSomething',
102
- })
103
- ).toEqual({
104
- '@servicetitan/design-system': 'SharedDependencies.ServiceTitan.DesignSystem',
105
- 'classnames': 'SharedDependencies.ClassNames',
106
- 'formstate': 'SharedDependencies.FormState',
107
- 'mobx': 'SharedDependencies.MobX',
108
- 'mobx-react': 'SharedDependencies.MobXReact',
109
- 'react': 'SharedDependencies.React',
110
- 'react-dom': 'SharedDependencies.ReactDOM',
111
- 'react-input-mask': 'SharedDependencies.ReactInputMask',
101
+ describe('with no package dependencies', () => {
102
+ beforeEach(() => (packageDependencies = undefined));
103
+
104
+ test('returns empty object', () => {
105
+ expect(subject()).toEqual({});
112
106
  });
113
107
  });
114
108
  });
@@ -0,0 +1,17 @@
1
+ import { getWebComponentBranchConfigs, WebComponentBranchConfigs } from './index';
2
+
3
+ function getDefaultConfigs(): Record<string, WebComponentBranchConfigs> {
4
+ return {
5
+ develop: { publishTag: 'dev' },
6
+ dev: { publishTag: 'dev' },
7
+ next: { publishTag: 'next' },
8
+ master: { publishTag: 'prod' },
9
+ };
10
+ }
11
+
12
+ export function getBranchesConfigs(): Record<string, WebComponentBranchConfigs> {
13
+ const packageConfigs = getWebComponentBranchConfigs();
14
+ const defaultConfigs = getDefaultConfigs();
15
+
16
+ return packageConfigs ?? defaultConfigs;
17
+ }
@@ -1,3 +1,4 @@
1
+ import fs from 'fs';
1
2
  import path from 'path';
2
3
 
3
4
  import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
@@ -5,6 +6,7 @@ import { ESLint } from 'eslint';
5
6
  import { LinterOptions } from 'stylelint';
6
7
  import { Config } from '@jest/types';
7
8
  import { readJson, readJsonSafe } from './read-json';
9
+ import { log } from './log';
8
10
 
9
11
  export const allowedWebpackDevServerOptions = ['headers', 'port', 'proxy', 'static'] as const;
10
12
 
@@ -71,15 +73,24 @@ export enum CommandName {
71
73
  }
72
74
  /* eslint-enable @typescript-eslint/naming-convention */
73
75
 
76
+ export interface WebComponentBranchConfigs {
77
+ publishTag?: string;
78
+ }
79
+
74
80
  interface WebComponentOptions {
75
- legacyRoot: boolean;
81
+ legacyRoot?: boolean;
82
+ /**
83
+ * mapping of git branches to configs.
84
+ * Used to separate configs (ex publish tag) depending on current branch
85
+ */
86
+ branches?: Record<string, WebComponentBranchConfigs>;
76
87
  }
77
88
 
78
89
  type Configuration = {
79
90
  'legacy'?: boolean;
80
91
  'lint'?: { eslint: ESLintConfiguration; stylelint: StylelintConfiguration };
81
92
  'test'?: JestConfiguration;
82
- 'web-component'?: boolean | WebComponentOptions;
93
+ 'web-component'?: boolean | string | WebComponentOptions;
83
94
  'webpack'?: false | WebpackConfiguration;
84
95
  } & {
85
96
  [key in CommandName]: NodeConfiguration;
@@ -120,6 +131,35 @@ export function getWebpackConfiguration(locationOrJson?: LocationOrJson) {
120
131
  return typeof webpack === 'object' ? webpack : {};
121
132
  }
122
133
 
134
+ export function getWebComponentConfiguration(
135
+ locationOrJson: LocationOrJson = './'
136
+ ): WebComponentOptions | undefined {
137
+ const config = getConfigurationSafe(locationOrJson)['web-component'];
138
+
139
+ if (config === true) {
140
+ return {};
141
+ }
142
+
143
+ if (typeof config === 'object') {
144
+ return config;
145
+ }
146
+
147
+ if (typeof config === 'string') {
148
+ if (fs.existsSync(config)) {
149
+ return require(path.resolve(config));
150
+ }
151
+ log.warning(`could not find web-component configuration: "${config}"`);
152
+
153
+ return undefined;
154
+ }
155
+ }
156
+
157
+ export function getWebComponentBranchConfigs(locationOrJson: LocationOrJson = './') {
158
+ const config = getWebComponentConfiguration(locationOrJson);
159
+
160
+ return config?.branches;
161
+ }
162
+
123
163
  export function isBundle(locationOrJson?: LocationOrJson) {
124
164
  return getConfiguration(locationOrJson).webpack !== false;
125
165
  }
@@ -142,15 +182,13 @@ export function isLegacy(locationOrJson?: LocationOrJson) {
142
182
  }
143
183
 
144
184
  export function isLegacyRoot() {
145
- const config = getConfiguration()['web-component'];
146
- return typeof config === 'object' && config.legacyRoot === true;
185
+ return getWebComponentConfiguration()?.legacyRoot === true;
147
186
  }
148
187
 
149
188
  export function isStyleCheckDisabled() {
150
189
  return getWebpackConfiguration()['disable-style-check'] === true;
151
190
  }
152
191
 
153
- export function isWebComponent() {
154
- const config = getConfiguration()['web-component'];
155
- return config === true || typeof config === 'object';
192
+ export function isWebComponent(locationOrJson: LocationOrJson = './') {
193
+ return !!getWebComponentConfiguration(locationOrJson);
156
194
  }
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
2
 
3
- import { readJson } from '.';
3
+ import { readJson } from './read-json';
4
4
 
5
5
  /**
6
6
  * Returns folders of sources and compilation target
@@ -13,13 +13,6 @@ const getJestConfigBase = ({
13
13
  setupFiles?: string[];
14
14
  testPathIgnorePatterns?: string[];
15
15
  }) => {
16
- const transform = {
17
- '^.+\\.jsx?$': [
18
- 'babel-jest',
19
- { presets: [['@babel/preset-env', { targets: { node: 'current' } }]] },
20
- ],
21
- '^.+\\.tsx?$': ['ts-jest', { tsconfig: './tsconfig.test.json' }],
22
- };
23
16
  const moduleNameMapper = {
24
17
  '\\.(css|scss|less|png|svg|jpg|jpeg|gif|woff|woff2|eot|ttf|otf)$': 'identity-obj-proxy',
25
18
  };
@@ -30,7 +23,6 @@ const getJestConfigBase = ({
30
23
  testRunner: 'jest-circus/runner',
31
24
  transformIgnorePatterns: ['node_modules/(?!(@servicetitan|@react-hook|nanoid|axios)/)'],
32
25
  modulePathIgnorePatterns: ['<rootDir>/.*/__mocks__'],
33
- transform,
34
26
  moduleNameMapper,
35
27
  testPathIgnorePatterns: [
36
28
  '\\.yalc',
@@ -41,6 +33,7 @@ const getJestConfigBase = ({
41
33
  coveragePathIgnorePatterns: ['^.+\\.d\\.ts$', ...coveragePathIgnorePatterns],
42
34
  coverageReporters: ['html-spa', 'text', 'json', 'cobertura', 'lcov'],
43
35
  collectCoverageFrom: ['**/*.{ts,tsx}'],
36
+ preset: path.join(__dirname, '../../jest'),
44
37
  };
45
38
  };
46
39
 
@@ -70,7 +63,7 @@ export const getJestConfigCLI = (args: Config.Argv): Config.Argv => {
70
63
  ...args,
71
64
  };
72
65
 
73
- const { collectCoverageFrom, moduleNameMapper, transform, ...rest } = getJestConfigBase({
66
+ const { collectCoverageFrom, moduleNameMapper, ...rest } = getJestConfigBase({
74
67
  coveragePathIgnorePatterns,
75
68
  setupFiles,
76
69
  testPathIgnorePatterns,
@@ -80,7 +73,6 @@ export const getJestConfigCLI = (args: Config.Argv): Config.Argv => {
80
73
  collectCoverageFrom: JSON.stringify(collectCoverageFrom),
81
74
  globals: JSON.stringify(globals),
82
75
  moduleNameMapper: JSON.stringify(moduleNameMapper),
83
- transform: JSON.stringify(transform),
84
76
  ...rest,
85
77
  ...config,
86
78
  };
@@ -33,7 +33,6 @@ jest.mock('../../utils', () => ({
33
33
  isCustomStyleRules: jest.fn(),
34
34
  isExposeSharedDependencies: jest.fn(),
35
35
  loadSharedDependencies: jest.fn(),
36
- log: { debug: jest.fn(), info: jest.fn() },
37
36
  }));
38
37
  jest.mock('../utils', () => ({
39
38
  ...jest.requireActual('../utils'),