@servicetitan/startup 22.18.0 → 22.20.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 (70) hide show
  1. package/dist/cli/commands/build.d.ts +1 -0
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +1 -0
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/bundle-package.d.ts +5 -3
  6. package/dist/cli/commands/bundle-package.d.ts.map +1 -1
  7. package/dist/cli/commands/bundle-package.js +7 -2
  8. package/dist/cli/commands/bundle-package.js.map +1 -1
  9. package/dist/cli/commands/mfe-publish.d.ts +1 -0
  10. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  11. package/dist/cli/commands/mfe-publish.js +3 -2
  12. package/dist/cli/commands/mfe-publish.js.map +1 -1
  13. package/dist/cli/commands/start.d.ts +5 -4
  14. package/dist/cli/commands/start.d.ts.map +1 -1
  15. package/dist/cli/commands/start.js +3 -0
  16. package/dist/cli/commands/start.js.map +1 -1
  17. package/dist/cli/utils/bundle.d.ts +2 -0
  18. package/dist/cli/utils/bundle.d.ts.map +1 -1
  19. package/dist/cli/utils/bundle.js +14 -7
  20. package/dist/cli/utils/bundle.js.map +1 -1
  21. package/dist/webpack/configs/plugins/provide-react-plugin.d.ts +1 -1
  22. package/dist/webpack/configs/plugins/provide-react-plugin.d.ts.map +1 -1
  23. package/dist/webpack/configs/plugins/provide-react-plugin.js +2 -2
  24. package/dist/webpack/configs/plugins/provide-react-plugin.js.map +1 -1
  25. package/dist/webpack/configs/rules/tsx-rules.d.ts +1 -1
  26. package/dist/webpack/configs/rules/tsx-rules.d.ts.map +1 -1
  27. package/dist/webpack/configs/rules/tsx-rules.js +27 -7
  28. package/dist/webpack/configs/rules/tsx-rules.js.map +1 -1
  29. package/dist/webpack/configs/types.d.ts +1 -0
  30. package/dist/webpack/configs/types.d.ts.map +1 -1
  31. package/dist/webpack/create-webpack-config.d.ts.map +1 -1
  32. package/dist/webpack/create-webpack-config.js +2 -1
  33. package/dist/webpack/create-webpack-config.js.map +1 -1
  34. package/dist/webpack/types.d.ts +2 -0
  35. package/dist/webpack/types.d.ts.map +1 -1
  36. package/package.json +7 -5
  37. package/src/cli/commands/__tests__/build.test.ts +14 -0
  38. package/src/cli/commands/__tests__/bundle-package.test.ts +8 -4
  39. package/src/cli/commands/__tests__/init.test.ts +5 -4
  40. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +4 -3
  41. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +15 -4
  42. package/src/cli/commands/__tests__/mfe-publish.test.ts +5 -3
  43. package/src/cli/commands/__tests__/start.test.ts +14 -0
  44. package/src/cli/commands/__tests__/styles-check.test.ts +12 -11
  45. package/src/cli/commands/build.ts +2 -0
  46. package/src/cli/commands/bundle-package.ts +13 -5
  47. package/src/cli/commands/mfe-publish.ts +3 -1
  48. package/src/cli/commands/start.ts +8 -4
  49. package/src/cli/utils/__tests__/bundle.test.ts +15 -14
  50. package/src/cli/utils/__tests__/eslint.test.ts +11 -4
  51. package/src/cli/utils/__tests__/get-module-type.test.ts +10 -15
  52. package/src/cli/utils/__tests__/is-module-installed.test.ts +6 -7
  53. package/src/cli/utils/__tests__/tcm.test.ts +6 -5
  54. package/src/cli/utils/__tests__/tsc.test.ts +6 -7
  55. package/src/cli/utils/bundle.ts +25 -7
  56. package/src/utils/__tests__/get-configuration.test.ts +20 -14
  57. package/src/utils/__tests__/get-destination-folders.test.ts +11 -13
  58. package/src/utils/__tests__/get-folders.test.ts +8 -6
  59. package/src/utils/__tests__/get-tsconfig.test.ts +6 -4
  60. package/src/utils/__tests__/read-json.test.ts +12 -16
  61. package/src/webpack/__tests__/create-webpack-config-web-component.test.ts +6 -5
  62. package/src/webpack/__tests__/create-webpack-config.test.ts +48 -9
  63. package/src/webpack/configs/plugins/provide-react-plugin.ts +2 -2
  64. package/src/webpack/configs/rules/tsx-rules.ts +32 -10
  65. package/src/webpack/configs/types.ts +1 -0
  66. package/src/webpack/configs/utils/__tests__/generate-metadata.test.ts +9 -12
  67. package/src/webpack/configs/utils/__tests__/get-startup-version.test.ts +6 -10
  68. package/src/webpack/create-webpack-config.ts +3 -1
  69. package/src/webpack/types.ts +2 -0
  70. package/template/.gitignore +4 -0
@@ -12,6 +12,8 @@ import { Overrides, createWebpackConfig } from '../../webpack';
12
12
  interface Options {
13
13
  buildStat?: boolean;
14
14
  config?: string;
15
+ esbuild?: boolean;
16
+ experimentalBundlers?: boolean;
15
17
  }
16
18
 
17
19
  function getName() {
@@ -33,7 +35,7 @@ export async function bundle(options: Options = {}) {
33
35
  const mode = 'production';
34
36
  const fallback = `./${webpackProdConfigFileName}`;
35
37
  const config = readWebpackConfig({ ...options, fallback });
36
- const { buildStat } = options;
38
+ const { buildStat, esbuild, experimentalBundlers } = options;
37
39
 
38
40
  const run = async (config: Configuration) => {
39
41
  const compiler = webpack(config);
@@ -55,6 +57,8 @@ export async function bundle(options: Options = {}) {
55
57
  process.stdout.write(stats.toString(config.stats) + '\n');
56
58
  };
57
59
 
60
+ const webpackOptions = { name, buildStat, esbuild, experimentalBundlers };
61
+
58
62
  if (isWebComponent()) {
59
63
  const webpackConfig: Overrides = {
60
64
  configuration: { ...config?.configuration, mode },
@@ -62,12 +66,12 @@ export async function bundle(options: Options = {}) {
62
66
  };
63
67
 
64
68
  return Promise.all([
65
- run(createWebpackConfig(webpackConfig, { embed: true, name, buildStat })),
66
- run(createWebpackConfig(webpackConfig, { name, buildStat })),
69
+ run(createWebpackConfig(webpackConfig, { embed: true, ...webpackOptions })),
70
+ run(createWebpackConfig(webpackConfig, webpackOptions)),
67
71
  ]);
68
72
  }
69
73
 
70
- return run(config ?? createWebpackConfig({ configuration: { mode } }, { buildStat, name }));
74
+ return run(config ?? createWebpackConfig({ configuration: { mode } }, webpackOptions));
71
75
  }
72
76
 
73
77
  export async function bundleWatch(options: Options = {}) {
@@ -76,6 +80,7 @@ export async function bundleWatch(options: Options = {}) {
76
80
  const name = getName();
77
81
  const mode = 'development';
78
82
  const config = readWebpackConfig({ ...options, fallback: `./${webpackDevConfigFileName}` });
83
+ const { esbuild, experimentalBundlers } = options;
79
84
 
80
85
  const runServe = async ({ devServer = {}, ...config }: Configuration) => {
81
86
  const compiler = webpack(config);
@@ -118,12 +123,25 @@ export async function bundleWatch(options: Options = {}) {
118
123
  };
119
124
 
120
125
  return Promise.all([
121
- run(createWebpackConfig(webpackConfig, { embed: true, name })),
122
- runServe(createWebpackConfig(webpackConfig, { name })),
126
+ run(
127
+ createWebpackConfig(webpackConfig, {
128
+ embed: true,
129
+ name,
130
+ esbuild,
131
+ experimentalBundlers,
132
+ })
133
+ ),
134
+ runServe(createWebpackConfig(webpackConfig, { name, esbuild, experimentalBundlers })),
123
135
  ]);
124
136
  }
125
137
 
126
- return runServe(config ?? createWebpackConfig({ configuration: { mode } }, { name }));
138
+ return runServe(
139
+ config ??
140
+ createWebpackConfig(
141
+ { configuration: { mode } },
142
+ { name, esbuild, experimentalBundlers }
143
+ )
144
+ );
127
145
  }
128
146
 
129
147
  function readWebpackConfig({ config, fallback }: Options & { fallback: string }) {
@@ -1,4 +1,7 @@
1
- import mockFS from 'mock-fs';
1
+ import { fs, vol } from 'memfs';
2
+ import WebpackDevServer from 'webpack-dev-server';
3
+ import path from 'path';
4
+
2
5
  import {
3
6
  getConfiguration,
4
7
  getConfigurationSafe,
@@ -13,7 +16,8 @@ import {
13
16
  isStyleCheckDisabled,
14
17
  isWebComponent,
15
18
  } from '../get-configuration';
16
- import WebpackDevServer from 'webpack-dev-server';
19
+
20
+ jest.mock('fs', () => fs);
17
21
 
18
22
  describe('[startup] Utils', () => {
19
23
  const packageJson = 'package.json';
@@ -23,7 +27,7 @@ describe('[startup] Utils', () => {
23
27
  }
24
28
 
25
29
  function mockPackageJson(content: Record<string, any> = {}) {
26
- mockFS(packageJsonFS(content));
30
+ vol.fromJSON(packageJsonFS(content));
27
31
  }
28
32
 
29
33
  function itReturns(subject: Function, value: any) {
@@ -32,7 +36,7 @@ describe('[startup] Utils', () => {
32
36
 
33
37
  beforeEach(() => mockPackageJson());
34
38
 
35
- afterEach(() => mockFS.restore());
39
+ afterEach(() => jest.resetAllMocks());
36
40
 
37
41
  describe(`${getConfiguration.name}`, () => {
38
42
  let location: string | undefined;
@@ -56,7 +60,7 @@ describe('[startup] Utils', () => {
56
60
 
57
61
  beforeEach(() => {
58
62
  location = 'packages/foo';
59
- mockFS({ packages: { foo: { [packageJson]: JSON.stringify({ cli }) } } });
63
+ vol.fromJSON({ [`packages/foo/${packageJson}`]: JSON.stringify({ cli }) });
60
64
  });
61
65
 
62
66
  test("returns location's cli", () => {
@@ -65,7 +69,7 @@ describe('[startup] Utils', () => {
65
69
  });
66
70
 
67
71
  describe('with no package.json', () => {
68
- beforeEach(() => mockFS({}));
72
+ beforeEach(() => vol.reset());
69
73
 
70
74
  test('throws error', () => {
71
75
  expect(subject).toThrowError();
@@ -95,7 +99,7 @@ describe('[startup] Utils', () => {
95
99
 
96
100
  beforeEach(() => {
97
101
  location = 'packages/foo';
98
- mockFS({ packages: { foo: { [packageJson]: JSON.stringify({ cli }) } } });
102
+ vol.fromJSON({ [`packages/foo/${packageJson}`]: JSON.stringify({ cli }) });
99
103
  });
100
104
 
101
105
  test("returns location's cli", () => {
@@ -104,7 +108,7 @@ describe('[startup] Utils', () => {
104
108
  });
105
109
 
106
110
  describe('with no package.json', () => {
107
- beforeEach(() => mockFS({}));
111
+ beforeEach(() => vol.reset());
108
112
 
109
113
  itReturns(subject, {});
110
114
  });
@@ -135,7 +139,7 @@ describe('[startup] Utils', () => {
135
139
  });
136
140
 
137
141
  describe('when "cli.webpack.proxy" is a path', () => {
138
- const proxy = 'proxy.json';
142
+ const proxy = 'proxy.js';
139
143
  const proxyConfig = { cli: { webpack: { ...webpack, proxy } } };
140
144
 
141
145
  beforeEach(() => mockPackageJson(proxyConfig));
@@ -148,12 +152,14 @@ describe('[startup] Utils', () => {
148
152
 
149
153
  describe('when file exists', () => {
150
154
  beforeEach(() => {
151
- mockFS({
155
+ vol.fromJSON({
152
156
  ...packageJsonFS(proxyConfig),
153
157
  [proxy]: JSON.stringify(webpack.proxy),
154
- // Need node_modules so Jest can require() json file
155
- // eslint-disable-next-line @typescript-eslint/naming-convention
156
- node_modules: mockFS.load('./node_modules'),
158
+ });
159
+
160
+ // Have to mock proxy.js because our fs is virtual, but require() is not
161
+ jest.doMock(path.resolve('proxy.js'), () => webpack.proxy, {
162
+ virtual: true,
157
163
  });
158
164
  });
159
165
 
@@ -265,7 +271,7 @@ describe('[startup] Utils', () => {
265
271
  describe('with a location', () => {
266
272
  beforeEach(() => {
267
273
  location = 'packages/foo';
268
- mockFS({ packages: { foo: { [packageJson]: JSON.stringify(trigger) } } });
274
+ vol.fromJSON({ [`packages/foo/${packageJson}`]: JSON.stringify(trigger) });
269
275
  });
270
276
 
271
277
  test("returns location's status", () => {
@@ -1,9 +1,9 @@
1
- import fs from 'fs';
2
- import mockFS from 'mock-fs';
1
+ import { fs, vol } from 'memfs';
3
2
  import { getPackages } from '../get-packages';
4
3
 
5
4
  import { getDestinationFolders } from '../get-destination-folders';
6
5
 
6
+ jest.mock('fs', () => fs);
7
7
  jest.mock('../get-packages', () => ({ getPackages: jest.fn() }));
8
8
 
9
9
  describe('[startup] Utils', () => {
@@ -12,28 +12,26 @@ describe('[startup] Utils', () => {
12
12
 
13
13
  beforeEach(() => {
14
14
  locations = ['foo', 'bar'];
15
- mockFS(packageFS(locations));
15
+ vol.fromJSON(packageFS(locations));
16
16
  jest.mocked(getPackages).mockImplementation((): any =>
17
17
  locations.map(location => ({ location: `packages/${location}` }))
18
18
  );
19
19
  });
20
20
 
21
- afterEach(() => mockFS.restore());
21
+ afterEach(() => vol.reset());
22
22
 
23
23
  const subject = () => getDestinationFolders();
24
24
 
25
25
  function packageFS(locations: string[]) {
26
26
  const packages = Object.fromEntries(
27
27
  locations.map(location => [
28
- location,
29
- {
30
- 'tsconfig.json': JSON.stringify({
31
- compilerOptions: { outDir: `${location}outDir` },
32
- }),
33
- },
28
+ `packages/${location}/tsconfig.json`,
29
+ JSON.stringify({
30
+ compilerOptions: { outDir: `${location}outDir` },
31
+ }),
34
32
  ])
35
33
  );
36
- return { packages };
34
+ return packages;
37
35
  }
38
36
 
39
37
  function expectedOutDirs(locations: string[]) {
@@ -45,7 +43,7 @@ describe('[startup] Utils', () => {
45
43
  });
46
44
 
47
45
  describe('when package has no tsconfig', () => {
48
- beforeEach(() => mockFS(packageFS(locations.slice(1))));
46
+ beforeEach(() => fs.rmSync(`packages/${locations[0]}/tsconfig.json`));
49
47
 
50
48
  test('omits location from result', () => {
51
49
  expect(subject()).toEqual(expectedOutDirs(locations.slice(1)));
@@ -54,7 +52,7 @@ describe('[startup] Utils', () => {
54
52
 
55
53
  describe('when tsconfig has no "outDir"', () => {
56
54
  beforeEach(() => {
57
- fs.writeFileSync(`packages/${locations[0]}/tsconfig.json`, JSON.stringify({}));
55
+ vol.fromJSON({ [`packages/${locations[0]}/tsconfig.json`]: JSON.stringify({}) });
58
56
  });
59
57
 
60
58
  test('omits location from result', () => {
@@ -1,14 +1,16 @@
1
- import mock from 'mock-fs';
1
+ import { fs, vol } from 'memfs';
2
2
 
3
3
  import { getFolders } from '..';
4
4
 
5
+ jest.mock('fs', () => fs);
6
+
5
7
  describe('[Startup] utils:get-folders', () => {
6
8
  afterAll(() => {
7
- mock.restore();
9
+ vol.reset();
8
10
  });
9
11
 
10
12
  test('getFolders throws an exception if "compilerOptions.rootDir" isn\'t defined', () => {
11
- mock({
13
+ vol.fromJSON({
12
14
  'package.json': JSON.stringify({}),
13
15
  'tsconfig.json': JSON.stringify({ compilerOptions: { ourDir: 'dist' } }),
14
16
  });
@@ -25,7 +27,7 @@ describe('[Startup] utils:get-folders', () => {
25
27
  });
26
28
 
27
29
  test('getFolders throws an exception if "compilerOptions.outDir" isn\'t defined', () => {
28
- mock({
30
+ vol.fromJSON({
29
31
  'package.json': JSON.stringify({}),
30
32
  'tsconfig.json': JSON.stringify({ compilerOptions: { rootDir: 'src' } }),
31
33
  });
@@ -42,7 +44,7 @@ describe('[Startup] utils:get-folders', () => {
42
44
  });
43
45
 
44
46
  test('"compilerOptions.outDir" used as the destination', () => {
45
- mock({
47
+ vol.fromJSON({
46
48
  'package.json': JSON.stringify({}),
47
49
  'tsconfig.json': JSON.stringify({
48
50
  compilerOptions: { rootDir: 'src', outDir: 'path' },
@@ -53,7 +55,7 @@ describe('[Startup] utils:get-folders', () => {
53
55
  });
54
56
 
55
57
  test('"compilerOptions.rootDir" used as the source', () => {
56
- mock({
58
+ vol.fromJSON({
57
59
  'package.json': JSON.stringify({}),
58
60
  'tsconfig.json': JSON.stringify({
59
61
  compilerOptions: { rootDir: 'path', outDir: 'dist' },
@@ -1,8 +1,10 @@
1
- import mockFS from 'mock-fs';
1
+ import { fs, vol } from 'memfs';
2
2
  import path from 'path';
3
3
 
4
4
  import { getTsConfig } from '../get-tsconfig';
5
5
 
6
+ jest.mock('fs', () => fs);
7
+
6
8
  describe(`[startup] Utils`, () => {
7
9
  describe(`${getTsConfig.name}`, () => {
8
10
  const defaultConfig = 'tsconfig.json';
@@ -13,14 +15,14 @@ describe(`[startup] Utils`, () => {
13
15
 
14
16
  beforeEach(() => (location = undefined));
15
17
 
16
- afterEach(() => mockFS.restore());
18
+ afterEach(() => vol.reset());
17
19
 
18
20
  test(`returns ${defaultConfig}`, () => {
19
21
  expect(subject()).toBe(defaultConfig);
20
22
  });
21
23
 
22
24
  describe(`when ${buildConfig} is present`, () => {
23
- beforeEach(() => mockFS({ [buildConfig]: JSON.stringify({}) }));
25
+ beforeEach(() => vol.fromJSON({ [buildConfig]: JSON.stringify({}) }));
24
26
 
25
27
  test(`returns ${buildConfig}`, () => {
26
28
  expect(subject()).toBe(buildConfig);
@@ -36,7 +38,7 @@ describe(`[startup] Utils`, () => {
36
38
 
37
39
  describe(`when ${buildConfig} is present`, () => {
38
40
  beforeEach(() => {
39
- mockFS({ packages: { foo: { [buildConfig]: JSON.stringify({}) } } });
41
+ vol.fromJSON({ [`packages/foo/${buildConfig}`]: JSON.stringify({}) });
40
42
  });
41
43
 
42
44
  test(`returns {location}/${buildConfig}`, () => {
@@ -1,14 +1,16 @@
1
- import mock from 'mock-fs';
1
+ import { fs, vol } from 'memfs';
2
2
 
3
3
  import { readJson } from '..';
4
4
 
5
+ jest.mock('fs', () => fs);
6
+
5
7
  describe('[Startup] utils:read-json', () => {
6
8
  afterAll(() => {
7
- mock.restore();
9
+ vol.reset();
8
10
  });
9
11
 
10
12
  test('readJson works with the current directory', () => {
11
- mock({
13
+ vol.fromJSON({
12
14
  'file.json': JSON.stringify({
13
15
  a: true,
14
16
  b: 'string',
@@ -28,20 +30,14 @@ describe('[Startup] utils:read-json', () => {
28
30
  });
29
31
 
30
32
  test('readJson works with the nested directory', () => {
31
- mock({
32
- path: {
33
- to: {
34
- folder: {
35
- 'file.json': JSON.stringify({
36
- a: true,
37
- b: 'string',
38
- c: {
39
- d: 7,
40
- },
41
- }),
42
- },
33
+ vol.fromJSON({
34
+ 'path/to/folder/file.json': JSON.stringify({
35
+ a: true,
36
+ b: 'string',
37
+ c: {
38
+ d: 7,
43
39
  },
44
- },
40
+ }),
45
41
  });
46
42
 
47
43
  expect(readJson('./path/to/folder/file.json')).toEqual({
@@ -1,6 +1,6 @@
1
1
  import HtmlWebpackPlugin from 'html-webpack-plugin';
2
2
  import MiniCssExtractPlugin from 'mini-css-extract-plugin';
3
- import mockFS from 'mock-fs';
3
+ import { fs, vol } from 'memfs';
4
4
  import path from 'path';
5
5
  import { DefinePlugin } from 'webpack';
6
6
  import WebpackAssetsManifest from 'webpack-assets-manifest';
@@ -14,6 +14,7 @@ import { featureCohort, getCallerFile, splitByEntry } from '../utils';
14
14
 
15
15
  import { createWebpackConfig } from '../index';
16
16
 
17
+ jest.mock('fs', () => fs);
17
18
  jest.mock('fork-ts-checker-webpack-plugin', () => jest.fn());
18
19
  jest.mock('html-webpack-plugin', () => jest.fn());
19
20
  jest.mock('mini-css-extract-plugin', () =>
@@ -86,10 +87,10 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
86
87
  jest.mocked(getPackages).mockReturnValue([]);
87
88
  jest.mocked(getTsConfig).mockReturnValue(tsConfig);
88
89
 
89
- mockFS({ [packageJson]: JSON.stringify({ name: packageName }) });
90
+ vol.fromJSON({ [packageJson]: JSON.stringify({ name: packageName }) });
90
91
  });
91
92
 
92
- afterEach(() => mockFS.restore());
93
+ afterEach(() => vol.reset());
93
94
 
94
95
  const subject = () => createWebpackConfig(overrides, options);
95
96
 
@@ -222,9 +223,9 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
222
223
 
223
224
  describe('when design-system.css exists', () => {
224
225
  beforeEach(() => {
225
- mockFS({
226
+ vol.fromJSON({
226
227
  [packageJson]: JSON.stringify({ name: packageName }),
227
- [source]: { 'design-system.css': '' },
228
+ [`${source}/design-system.css`]: '',
228
229
  });
229
230
  });
230
231
 
@@ -1,7 +1,7 @@
1
1
  import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
2
2
  import HtmlWebpackPlugin from 'html-webpack-plugin';
3
3
  import MiniCssExtractPlugin from 'mini-css-extract-plugin';
4
- import mockFS from 'mock-fs';
4
+ import { fs, vol } from 'memfs';
5
5
  import MomentLocalesPlugin from 'moment-locales-webpack-plugin';
6
6
  import os from 'os';
7
7
  import path from 'path';
@@ -28,6 +28,7 @@ import { featureCohort, splitByEntry } from '../utils';
28
28
 
29
29
  import { createWebpackConfig } from '../index';
30
30
 
31
+ jest.mock('fs', () => fs);
31
32
  jest.mock('fork-ts-checker-webpack-plugin', () => jest.fn());
32
33
  jest.mock('html-webpack-plugin', () => jest.fn());
33
34
  jest.mock('mini-css-extract-plugin', () =>
@@ -124,10 +125,10 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
124
125
 
125
126
  Object.keys(argv).forEach(key => delete (argv as any)[key]);
126
127
 
127
- mockFS({ [packageJson]: JSON.stringify({ name: packageName }) });
128
+ vol.fromJSON({ [packageJson]: JSON.stringify({ name: packageName }) });
128
129
  });
129
130
 
130
- afterEach(() => mockFS.restore());
131
+ afterEach(() => vol.reset());
131
132
 
132
133
  const subject = () => createWebpackConfig(overrides, options);
133
134
 
@@ -226,25 +227,63 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
226
227
  });
227
228
  });
228
229
 
229
- describe('with --esbuild command line argument', () => {
230
- beforeEach(() => Object.assign(argv, { esbuild: true }));
231
-
230
+ function itConfiguresTsLoader() {
232
231
  test('configures ".tsx" rules to use esbuild-loader', () => {
232
+ expect(subject().module?.rules).toContainEqual({
233
+ test: /\.tsx?$/,
234
+ exclude: /node_modules/,
235
+ use: [{ loader: 'esbuild-loader', options: { loader: 'tsx', target: 'es2018' } }],
236
+ });
237
+ });
238
+ }
239
+ function itConfiguresSwcLoader() {
240
+ test('configures ".tsx" rules to use swc-loader', () => {
233
241
  expect(subject().module?.rules).toContainEqual({
234
242
  test: /\.tsx?$/,
235
243
  exclude: /node_modules/,
236
244
  use: [
237
245
  {
238
- loader: 'esbuild-loader',
239
- options: { loader: 'tsx', target: 'es2018' },
246
+ loader: 'swc-loader',
247
+ options: {
248
+ jsc: {
249
+ parser: { syntax: 'typescript', tsx: true, decorators: true },
250
+ target: 'es2018',
251
+ transform: { legacyDecorator: true, decoratorMetadata: true },
252
+ },
253
+ },
240
254
  },
241
255
  ],
242
256
  });
243
257
  });
244
-
258
+ }
259
+ function itConfiguresReactToLoadAutomatically() {
245
260
  test('configures React to load automatically', () => {
246
261
  expect(subject().plugins).toContainEqual(new ProvidePlugin({ React: 'react' }));
247
262
  });
263
+ }
264
+ describe('with --esbuild command line argument', () => {
265
+ beforeEach(() => Object.assign(argv, { esbuild: true }));
266
+ itConfiguresTsLoader();
267
+ itConfiguresReactToLoadAutomatically();
268
+ });
269
+ describe('with esbuild option', () => {
270
+ beforeEach(() => {
271
+ options = { esbuild: true };
272
+ });
273
+ itConfiguresTsLoader();
274
+ itConfiguresReactToLoadAutomatically();
275
+ });
276
+ describe('with experimental-bundlers option', () => {
277
+ beforeEach(() => {
278
+ options = { experimentalBundlers: true };
279
+ });
280
+ itConfiguresSwcLoader();
281
+ itConfiguresReactToLoadAutomatically();
282
+ });
283
+ describe('with --experimental-bundlers command line argument', () => {
284
+ beforeEach(() => Object.assign(argv, { 'experimental-bundlers': true }));
285
+ itConfiguresSwcLoader();
286
+ itConfiguresReactToLoadAutomatically();
248
287
  });
249
288
 
250
289
  test('configures ".css" rule to exclude .module.css files', () => {
@@ -1,8 +1,8 @@
1
1
  import { ProvidePlugin } from 'webpack';
2
2
  import { Context, Overrides } from '../types';
3
3
 
4
- export function provideReactPlugin({ esbuild }: Context, _: Overrides) {
5
- if (!esbuild) {
4
+ export function provideReactPlugin({ esbuild, experimentalBundlers }: Context, _: Overrides) {
5
+ if (!(esbuild || experimentalBundlers)) {
6
6
  return;
7
7
  }
8
8
 
@@ -1,16 +1,38 @@
1
1
  import { RuleSetRule } from 'webpack';
2
2
  import { Context } from '../types';
3
3
 
4
- export function tsxRules({ esbuild }: Context): RuleSetRule[] {
5
- const loader = esbuild
6
- ? {
7
- loader: 'esbuild-loader',
8
- options: { loader: 'tsx', target: 'es2018' },
9
- }
10
- : {
11
- loader: 'ts-loader',
12
- options: { transpileOnly: true },
13
- };
4
+ export function tsxRules({ esbuild, experimentalBundlers }: Context): RuleSetRule[] {
5
+ let loader: Record<string, any> = {
6
+ loader: 'ts-loader',
7
+ options: { transpileOnly: true },
8
+ };
9
+
10
+ if (esbuild) {
11
+ loader = {
12
+ loader: 'esbuild-loader',
13
+ options: { loader: 'tsx', target: 'es2018' },
14
+ };
15
+ }
16
+
17
+ if (experimentalBundlers) {
18
+ loader = {
19
+ loader: 'swc-loader',
20
+ options: {
21
+ jsc: {
22
+ parser: {
23
+ syntax: 'typescript',
24
+ tsx: true,
25
+ decorators: true,
26
+ },
27
+ target: 'es2018',
28
+ transform: {
29
+ legacyDecorator: true,
30
+ decoratorMetadata: true,
31
+ },
32
+ },
33
+ },
34
+ };
35
+ }
14
36
 
15
37
  return [
16
38
  {
@@ -5,6 +5,7 @@ export { Overrides } from '../types';
5
5
  export interface Context extends Options {
6
6
  destination: string;
7
7
  esbuild: boolean;
8
+ experimentalBundlers: boolean;
8
9
  isCustomStyleRules: boolean;
9
10
  isExposeSharedDependencies: boolean;
10
11
  isProduction: boolean;
@@ -1,11 +1,12 @@
1
- import fs from 'fs';
2
- import mockFS from 'mock-fs';
1
+ import { fs, vol } from 'memfs';
2
+ import * as mockedFs from 'fs';
3
3
  import { createPackage } from '../../../../__mocks__';
4
4
  import { Context } from '../../types';
5
5
  import { getStartupVersion } from '../get-startup-version';
6
6
 
7
7
  import { generateMetadata } from '../generate-metadata';
8
8
 
9
+ jest.mock('fs', () => fs);
9
10
  jest.mock('../get-startup-version');
10
11
 
11
12
  describe(`[startup] ${generateMetadata.name}`, () => {
@@ -15,7 +16,7 @@ describe(`[startup] ${generateMetadata.name}`, () => {
15
16
  let context: Partial<Context>;
16
17
 
17
18
  function generatedMetadata(destination: string) {
18
- return JSON.parse(fs.readFileSync(`${destination}/metadata.json`, 'utf8'));
19
+ return JSON.parse(mockedFs.readFileSync(`${destination}/metadata.json`, 'utf8'));
19
20
  }
20
21
 
21
22
  function packageFS() {
@@ -23,7 +24,7 @@ describe(`[startup] ${generateMetadata.name}`, () => {
23
24
  }
24
25
 
25
26
  beforeEach(() => {
26
- mockFS(packageFS());
27
+ vol.fromJSON(packageFS());
27
28
  jest.mocked(getStartupVersion).mockReturnValue(startupVersion);
28
29
 
29
30
  context = {
@@ -34,7 +35,7 @@ describe(`[startup] ${generateMetadata.name}`, () => {
34
35
  };
35
36
  });
36
37
 
37
- afterEach(() => mockFS.restore());
38
+ afterEach(() => vol.reset());
38
39
 
39
40
  const subject = () => generateMetadata(context as any);
40
41
 
@@ -55,14 +56,10 @@ describe(`[startup] ${generateMetadata.name}`, () => {
55
56
  const light = { css: 'baz', js: 'qux' };
56
57
 
57
58
  beforeEach(() => {
58
- mockFS({
59
+ vol.fromJSON({
59
60
  ...packageFS(),
60
- [destination]: {
61
- bundle: {
62
- full: { 'entrypoints.json': JSON.stringify(full) },
63
- light: { 'entrypoints.json': JSON.stringify(light) },
64
- },
65
- },
61
+ [`${destination}/bundle/full/entrypoints.json`]: JSON.stringify(full),
62
+ [`${destination}/bundle/light/entrypoints.json`]: JSON.stringify(light),
66
63
  });
67
64
  });
68
65