@rnx-kit/cli 0.15.4 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +32 -20
  3. package/coverage/clover.xml +75 -26
  4. package/coverage/coverage-final.json +2 -2
  5. package/coverage/lcov-report/index.html +21 -21
  6. package/coverage/lcov-report/src/bundle/defaultPlugins.ts.html +31 -13
  7. package/coverage/lcov-report/src/bundle/index.html +5 -5
  8. package/coverage/lcov-report/src/bundle/kit-config.ts.html +1 -1
  9. package/coverage/lcov-report/src/bundle/metro.ts.html +1 -1
  10. package/coverage/lcov-report/src/bundle/overrides.ts.html +1 -1
  11. package/coverage/lcov-report/src/copy-assets.ts.html +1 -1
  12. package/coverage/lcov-report/src/index.html +20 -20
  13. package/coverage/lcov-report/src/metro-config.ts.html +426 -42
  14. package/coverage/lcov.info +127 -42
  15. package/lib/bundle/defaultPlugins.d.ts +4 -2
  16. package/lib/bundle/defaultPlugins.d.ts.map +1 -1
  17. package/lib/bundle/defaultPlugins.js +9 -6
  18. package/lib/bundle/defaultPlugins.js.map +1 -1
  19. package/lib/bundle/types.d.ts +1 -1
  20. package/lib/bundle/types.d.ts.map +1 -1
  21. package/lib/metro-config.d.ts +5 -3
  22. package/lib/metro-config.d.ts.map +1 -1
  23. package/lib/metro-config.js +122 -21
  24. package/lib/metro-config.js.map +1 -1
  25. package/lib/serve/kit-config.d.ts +7 -3
  26. package/lib/serve/kit-config.d.ts.map +1 -1
  27. package/lib/serve/kit-config.js +1 -1
  28. package/lib/serve/kit-config.js.map +1 -1
  29. package/package.json +6 -10
  30. package/src/bundle/defaultPlugins.ts +14 -8
  31. package/src/bundle/types.ts +1 -3
  32. package/src/metro-config.ts +154 -26
  33. package/src/serve/kit-config.ts +8 -5
  34. package/src/start.ts +1 -1
  35. package/test/bundle/kit-config.test.ts +25 -15
  36. package/test/bundle/metro.test.ts +10 -11
  37. package/test/bundle/overrides.test.ts +5 -4
  38. package/test/metro-config.test.ts +71 -32
@@ -1,4 +1,5 @@
1
- import type { BundlerPlugins } from "@rnx-kit/config";
1
+ import type { BundleParameters } from "@rnx-kit/config";
2
+ import { warn } from "@rnx-kit/console";
2
3
  import { CyclicDependencies } from "@rnx-kit/metro-plugin-cyclic-dependencies-detector";
3
4
  import { DuplicateDependencies } from "@rnx-kit/metro-plugin-duplicates-checker";
4
5
  import { TypeScriptPlugin } from "@rnx-kit/metro-plugin-typescript";
@@ -9,44 +10,158 @@ import {
9
10
  MetroSerializer as MetroSerializerEsbuild,
10
11
  } from "@rnx-kit/metro-serializer-esbuild";
11
12
  import type { InputConfigT, SerializerConfigT } from "metro-config";
13
+ import { getDefaultBundlerPlugins } from "./bundle/defaultPlugins";
14
+
15
+ type MetroExtraParams = Pick<
16
+ BundleParameters,
17
+ | "detectCyclicDependencies"
18
+ | "detectDuplicateDependencies"
19
+ | "typescriptValidation"
20
+ | "plugins"
21
+ | "treeShake"
22
+ >;
23
+
24
+ function resolvePlugin(name: string): string {
25
+ try {
26
+ return require.resolve(name);
27
+ } catch (_) {
28
+ return require.resolve(name, { paths: [process.cwd()] });
29
+ }
30
+ }
31
+
32
+ function importPlugin(name: string) {
33
+ const plugin = require(resolvePlugin(name));
34
+ return plugin.default || plugin;
35
+ }
36
+
37
+ function readLegacyOptions({
38
+ detectCyclicDependencies,
39
+ detectDuplicateDependencies,
40
+ typescriptValidation,
41
+ }: MetroExtraParams): string | undefined {
42
+ const oldOptions = [];
43
+ const deprecatedOptions: Record<string, Record<string, unknown> | true> = {};
44
+
45
+ if (detectCyclicDependencies) {
46
+ oldOptions.push("detectCyclicDependencies");
47
+ deprecatedOptions["@rnx-kit/metro-plugin-cyclic-dependencies-detector"] =
48
+ detectCyclicDependencies;
49
+ }
50
+ if (detectDuplicateDependencies) {
51
+ oldOptions.push("detectDuplicateDependencies");
52
+ deprecatedOptions["@rnx-kit/metro-plugin-duplicates-checker"] =
53
+ detectDuplicateDependencies;
54
+ }
55
+ if (typescriptValidation) {
56
+ oldOptions.push("typescriptValidation");
57
+ deprecatedOptions["@rnx-kit/metro-plugin-typescript"] =
58
+ typescriptValidation;
59
+ }
60
+
61
+ if (oldOptions.length === 0) {
62
+ return undefined;
63
+ }
64
+
65
+ return (
66
+ `'${oldOptions.join("', '")}' have been replaced by a new 'plugins' ` +
67
+ "option. Below is an equivalent config:\n" +
68
+ "\n" +
69
+ ' "plugins": [\n' +
70
+ Object.entries(deprecatedOptions)
71
+ .map(([name, options]) => {
72
+ if (typeof options === "object") {
73
+ const json = JSON.stringify(options, null, 2)
74
+ .split("\n")
75
+ .join("\n ");
76
+ return ` ["${name}", ${json}]`;
77
+ } else {
78
+ return ` "${name}"`;
79
+ }
80
+ })
81
+ .join(",\n") +
82
+ "\n" +
83
+ " ]\n" +
84
+ "\n" +
85
+ "For more details, see " +
86
+ "https://github.com/microsoft/rnx-kit/tree/main/packages/cli#readme"
87
+ );
88
+ }
12
89
 
13
90
  /**
14
91
  * Customize the Metro configuration.
15
92
  *
16
93
  * @param metroConfigReadonly Metro configuration
17
- * @param bundlerPlugins Plugins used to further customize Metro configuration
94
+ * @param extraParams Parameters used to further customize Metro configuration
18
95
  * @param print Optional function to use when printing status messages to the Metro console
19
96
  */
20
97
  export function customizeMetroConfig(
21
98
  metroConfigReadonly: InputConfigT,
22
- {
23
- detectCyclicDependencies,
24
- detectDuplicateDependencies,
25
- treeShake,
26
- typescriptValidation,
27
- }: BundlerPlugins,
99
+ extraParams: MetroExtraParams,
28
100
  print?: (message: string) => void
29
101
  ): void {
30
- // We will be making changes to the Metro configuration. Coerce from a
31
- // type with readonly props to a type where the props are writeable.
102
+ // We will be making changes to the Metro configuration. Coerce from a type
103
+ // with readonly props to a type where the props are writeable.
32
104
  const metroConfig = metroConfigReadonly as InputConfigT;
33
105
 
34
- const plugins: MetroPlugin[] = [];
35
- if (typeof detectDuplicateDependencies === "object") {
36
- plugins.push(DuplicateDependencies(detectDuplicateDependencies));
37
- } else if (detectDuplicateDependencies !== false) {
38
- plugins.push(DuplicateDependencies());
39
- }
40
- if (typeof detectCyclicDependencies === "object") {
41
- plugins.push(CyclicDependencies(detectCyclicDependencies));
42
- } else if (detectCyclicDependencies !== false) {
43
- plugins.push(CyclicDependencies());
106
+ const metroPlugins: MetroPlugin[] = [];
107
+ const serializerHooks: Record<
108
+ string,
109
+ SerializerConfigT["experimentalSerializerHook"]
110
+ > = {};
111
+
112
+ const legacyWarning = readLegacyOptions(extraParams);
113
+ if (legacyWarning) {
114
+ warn(legacyWarning);
115
+
116
+ const {
117
+ detectCyclicDependencies,
118
+ detectDuplicateDependencies,
119
+ typescriptValidation,
120
+ } = extraParams;
121
+
122
+ if (typeof detectDuplicateDependencies === "object") {
123
+ metroPlugins.push(DuplicateDependencies(detectDuplicateDependencies));
124
+ } else if (detectDuplicateDependencies !== false) {
125
+ metroPlugins.push(DuplicateDependencies());
126
+ }
127
+
128
+ if (typeof detectCyclicDependencies === "object") {
129
+ metroPlugins.push(CyclicDependencies(detectCyclicDependencies));
130
+ } else if (detectCyclicDependencies !== false) {
131
+ metroPlugins.push(CyclicDependencies());
132
+ }
133
+
134
+ if (typescriptValidation !== false) {
135
+ const plugin = TypeScriptPlugin(typescriptValidation, print);
136
+ serializerHooks["@rnx-kit/metro-plugin-typescript"] = plugin;
137
+ }
138
+ } else {
139
+ const plugins = extraParams.plugins || getDefaultBundlerPlugins().plugins;
140
+ for (const entry of plugins) {
141
+ const [module, options] = Array.isArray(entry)
142
+ ? entry
143
+ : [entry, undefined];
144
+ const plugin = importPlugin(module);
145
+ switch (plugin.type) {
146
+ case "serializer":
147
+ metroPlugins.push(plugin(options));
148
+ break;
149
+
150
+ case "serializerHook":
151
+ serializerHooks[module] = plugin(options, print);
152
+ break;
153
+
154
+ default:
155
+ throw new Error(`${module}: unknown plugin type: ${plugin.type}`);
156
+ }
157
+ }
44
158
  }
45
159
 
46
- if (treeShake) {
47
- metroConfig.serializer.customSerializer = MetroSerializerEsbuild(plugins);
160
+ if (extraParams.treeShake) {
161
+ metroConfig.serializer.customSerializer =
162
+ MetroSerializerEsbuild(metroPlugins);
48
163
  Object.assign(metroConfig.transformer, esbuildTransformerConfig);
49
- } else if (plugins.length > 0) {
164
+ } else if (metroPlugins.length > 0) {
50
165
  // MetroSerializer acts as a CustomSerializer, and it works with both
51
166
  // older and newer versions of Metro. Older versions expect a return
52
167
  // value, while newer versions expect a promise.
@@ -58,12 +173,25 @@ export function customizeMetroConfig(
58
173
  // Since it can handle either scenario, just coerce it to whatever
59
174
  // the current version of Metro expects.
60
175
  metroConfig.serializer.customSerializer = MetroSerializer(
61
- plugins
176
+ metroPlugins
62
177
  ) as SerializerConfigT["customSerializer"];
63
178
  } else {
64
179
  delete metroConfig.serializer.customSerializer;
65
180
  }
66
181
 
67
- const hook = TypeScriptPlugin(typescriptValidation, print);
68
- metroConfig.serializer.experimentalSerializerHook = hook;
182
+ const hooks = Object.values(serializerHooks);
183
+ switch (hooks.length) {
184
+ case 0:
185
+ break;
186
+ case 1:
187
+ metroConfig.serializer.experimentalSerializerHook = hooks[0];
188
+ break;
189
+ default:
190
+ metroConfig.serializer.experimentalSerializerHook = (graph, delta) => {
191
+ for (const hook of hooks) {
192
+ hook(graph, delta);
193
+ }
194
+ };
195
+ break;
196
+ }
69
197
  }
@@ -1,15 +1,18 @@
1
- import type { ServerConfig, BundlerPlugins } from "@rnx-kit/config";
2
- import { getKitConfig, getBundleConfig } from "@rnx-kit/config";
1
+ import type { ServerConfig } from "@rnx-kit/config";
2
+ import { getBundleConfig, getKitConfig } from "@rnx-kit/config";
3
3
  import { pickValues } from "@rnx-kit/tools-language/properties";
4
4
  import { getDefaultBundlerPlugins } from "../bundle/defaultPlugins";
5
5
 
6
- export type ServerConfigOverrides = {
6
+ type ServerConfigOverrides = {
7
7
  projectRoot?: string;
8
8
  assetPlugins?: string[];
9
9
  sourceExts?: string[];
10
10
  };
11
11
 
12
- export type CliServerConfig = ServerConfig & Required<BundlerPlugins>;
12
+ type CliServerConfig = ServerConfig & {
13
+ plugins: Required<ServerConfig>["plugins"];
14
+ treeShake: false;
15
+ };
13
16
 
14
17
  /**
15
18
  * Get the server configuration from the rnx-kit configuration. Apply any overrides.
@@ -29,7 +32,7 @@ export function getKitServerConfig(
29
32
  "detectCyclicDependencies",
30
33
  "detectDuplicateDependencies",
31
34
  "typescriptValidation",
32
- //"treeShake", // don't pull in treeShake yet, since it doesn't work with the server
35
+ "plugins",
33
36
  ]);
34
37
  }
35
38
  }
package/src/start.ts CHANGED
@@ -19,7 +19,7 @@ import { customizeMetroConfig } from "./metro-config";
19
19
  import { getKitServerConfig } from "./serve/kit-config";
20
20
 
21
21
  type DevServerMiddleware = ReturnType<
22
- typeof CliServerApi["createDevServerMiddleware"]
22
+ (typeof CliServerApi)["createDevServerMiddleware"]
23
23
  >;
24
24
 
25
25
  type DevServerMiddleware6 = Pick<DevServerMiddleware, "middleware"> & {
@@ -1,4 +1,3 @@
1
- import "jest-extended";
2
1
  import {
3
2
  getTargetPlatforms,
4
3
  getCliPlatformBundleConfigs,
@@ -9,8 +8,9 @@ const rnxKitConfig = require("@rnx-kit/config");
9
8
  describe("CLI > Bundle > Kit Config > getTargetPlatforms", () => {
10
9
  test("returns the override platform", () => {
11
10
  const platforms = getTargetPlatforms("ios", ["android", "ios", "windows"]);
12
- expect(platforms).toBeArrayOfSize(1);
13
- expect(platforms).toIncludeSameMembers(["ios"]);
11
+ expect(Array.isArray(platforms)).toBe(true);
12
+ expect(platforms.length).toBe(1);
13
+ expect(platforms).toEqual(["ios"]);
14
14
  });
15
15
 
16
16
  test("returns the target platforms", () => {
@@ -19,8 +19,9 @@ describe("CLI > Bundle > Kit Config > getTargetPlatforms", () => {
19
19
  "ios",
20
20
  "windows",
21
21
  ]);
22
- expect(platforms).toBeArrayOfSize(3);
23
- expect(platforms).toIncludeSameMembers(["android", "ios", "windows"]);
22
+ expect(Array.isArray(platforms)).toBe(true);
23
+ expect(platforms.length).toBe(3);
24
+ expect(platforms).toEqual(["android", "ios", "windows"]);
24
25
  });
25
26
 
26
27
  test("throws when no override or target platform is given", () => {
@@ -32,10 +33,12 @@ describe("CLI > Bundle > Kit Config > getCliPlatformBundleConfigs", () => {
32
33
  const defaultConfig = {
33
34
  entryFile: "index.js",
34
35
  sourcemapUseAbsolutePath: false,
35
- detectCyclicDependencies: true,
36
- detectDuplicateDependencies: true,
37
- typescriptValidation: true,
38
36
  treeShake: false,
37
+ plugins: [
38
+ "@rnx-kit/metro-plugin-cyclic-dependencies-detector",
39
+ "@rnx-kit/metro-plugin-duplicates-checker",
40
+ "@rnx-kit/metro-plugin-typescript",
41
+ ],
39
42
  };
40
43
 
41
44
  const defaultConfigIOS = {
@@ -68,25 +71,29 @@ describe("CLI > Bundle > Kit Config > getCliPlatformBundleConfigs", () => {
68
71
 
69
72
  test("returns defaults for iOS when package has no config", () => {
70
73
  const configs = getCliPlatformBundleConfigs(undefined, "ios");
71
- expect(configs).toBeArrayOfSize(1);
74
+ expect(Array.isArray(configs)).toBe(true);
75
+ expect(configs.length).toBe(1);
72
76
  expect(configs[0]).toEqual(defaultConfigIOS);
73
77
  });
74
78
 
75
79
  test("returns defaults for MacOS when package has no config", () => {
76
80
  const configs = getCliPlatformBundleConfigs(undefined, "macos");
77
- expect(configs).toBeArrayOfSize(1);
81
+ expect(Array.isArray(configs)).toBe(true);
82
+ expect(configs.length).toBe(1);
78
83
  expect(configs[0]).toEqual(defaultConfigMacOS);
79
84
  });
80
85
 
81
86
  test("returns defaults for Android when package has no config", () => {
82
87
  const configs = getCliPlatformBundleConfigs(undefined, "android");
83
- expect(configs).toBeArrayOfSize(1);
88
+ expect(Array.isArray(configs)).toBe(true);
89
+ expect(configs.length).toBe(1);
84
90
  expect(configs[0]).toEqual(defaultConfigAndroid);
85
91
  });
86
92
 
87
93
  test("returns defaults for Windows when package has no config", () => {
88
94
  const configs = getCliPlatformBundleConfigs(undefined, "windows");
89
- expect(configs).toBeArrayOfSize(1);
95
+ expect(Array.isArray(configs)).toBe(true);
96
+ expect(configs.length).toBe(1);
90
97
  expect(configs[0]).toEqual(defaultConfigWindows);
91
98
  });
92
99
 
@@ -102,7 +109,8 @@ describe("CLI > Bundle > Kit Config > getCliPlatformBundleConfigs", () => {
102
109
  test("returns config with defaults for all target platforms", () => {
103
110
  rnxKitConfig.__setMockConfig(testConfig);
104
111
  const configs = getCliPlatformBundleConfigs();
105
- expect(configs).toBeArrayOfSize(4);
112
+ expect(Array.isArray(configs)).toBe(true);
113
+ expect(configs.length).toBe(4);
106
114
  expect(configs[0]).toEqual({ ...defaultConfigIOS, ...testConfig.bundle });
107
115
  expect(configs[1]).toEqual({ ...defaultConfigMacOS, ...testConfig.bundle });
108
116
  expect(configs[2]).toEqual({
@@ -131,7 +139,8 @@ describe("CLI > Bundle > Kit Config > getCliPlatformBundleConfigs", () => {
131
139
  test("returns the first config when no id is given", () => {
132
140
  rnxKitConfig.__setMockConfig(testMultiConfig);
133
141
  const configs = getCliPlatformBundleConfigs(undefined, "ios");
134
- expect(configs).toBeArrayOfSize(1);
142
+ expect(Array.isArray(configs)).toBe(true);
143
+ expect(configs.length).toBe(1);
135
144
  expect(configs[0]).toEqual({
136
145
  ...defaultConfigIOS,
137
146
  ...testMultiConfig.bundle[0],
@@ -141,7 +150,8 @@ describe("CLI > Bundle > Kit Config > getCliPlatformBundleConfigs", () => {
141
150
  test("returns the selected config when an id is given", () => {
142
151
  rnxKitConfig.__setMockConfig(testMultiConfig);
143
152
  const configs = getCliPlatformBundleConfigs("second", "android");
144
- expect(configs).toBeArrayOfSize(1);
153
+ expect(Array.isArray(configs)).toBe(true);
154
+ expect(configs.length).toBe(1);
145
155
  expect(configs[0]).toEqual({
146
156
  ...defaultConfigAndroid,
147
157
  ...testMultiConfig.bundle[1],
@@ -1,4 +1,3 @@
1
- import "jest-extended";
2
1
  import { metroBundle } from "../../src/bundle/metro";
3
2
  import type { CliPlatformBundleConfig } from "../../src/bundle/types";
4
3
 
@@ -19,10 +18,8 @@ describe("CLI > Bundle > Metro > metroBundle", () => {
19
18
  const bundleConfigNoPlugins: CliPlatformBundleConfig = {
20
19
  entryFile: "out/entry.js",
21
20
  bundleOutput: "src/main.jsbundle",
22
- detectCyclicDependencies: false,
23
- detectDuplicateDependencies: false,
24
- typescriptValidation: false,
25
21
  treeShake: false,
22
+ plugins: [],
26
23
  platform: "ios",
27
24
  sourcemapOutput: "map/main.map",
28
25
  sourcemapSourcesRoot: "root-path-for-source-maps",
@@ -32,10 +29,12 @@ describe("CLI > Bundle > Metro > metroBundle", () => {
32
29
 
33
30
  const bundleConfig: CliPlatformBundleConfig = {
34
31
  ...bundleConfigNoPlugins,
35
- detectCyclicDependencies: true,
36
- detectDuplicateDependencies: true,
37
- typescriptValidation: true,
38
32
  treeShake: true,
33
+ plugins: [
34
+ "@rnx-kit/metro-plugin-cyclic-dependencies-detector",
35
+ "@rnx-kit/metro-plugin-duplicates-checker",
36
+ "@rnx-kit/metro-plugin-typescript",
37
+ ],
39
38
  };
40
39
 
41
40
  const dev = true;
@@ -43,16 +42,16 @@ describe("CLI > Bundle > Metro > metroBundle", () => {
43
42
 
44
43
  it("does not use a custom serializer when all plugins are disabled", async () => {
45
44
  const metroConfig = await getDefaultConfig();
46
- expect(metroConfig.serializer.customSerializer).toBeNil();
45
+ expect(metroConfig.serializer.customSerializer).toBeFalsy();
47
46
  await metroBundle(metroConfig, bundleConfigNoPlugins, dev, minify);
48
- expect(metroConfig.serializer.customSerializer).toBeNil();
47
+ expect(metroConfig.serializer.customSerializer).toBeFalsy();
49
48
  });
50
49
 
51
50
  it("uses a custom serializer when at least one plugin is enabled", async () => {
52
51
  const metroConfig = await getDefaultConfig();
53
- expect(metroConfig.serializer.customSerializer).toBeNil();
52
+ expect(metroConfig.serializer.customSerializer).toBeFalsy();
54
53
  await metroBundle(metroConfig, bundleConfig, dev, minify);
55
- expect(metroConfig.serializer.customSerializer).not.toBeNil();
54
+ expect(metroConfig.serializer.customSerializer).toBeTruthy();
56
55
  });
57
56
 
58
57
  it("creates directories for the bundle, the source map, and assets", async () => {
@@ -1,4 +1,3 @@
1
- import "jest-extended";
2
1
  import {
3
2
  applyBundleConfigOverrides,
4
3
  overridableCommonBundleOptions,
@@ -10,10 +9,12 @@ describe("CLI > Bundle > Overrides > applyBundleConfigOverrides", () => {
10
9
  entryFile: "src/index.js",
11
10
  bundleOutput: "main.jsbundle",
12
11
  sourcemapUseAbsolutePath: false,
13
- detectCyclicDependencies: true,
14
- detectDuplicateDependencies: true,
15
- typescriptValidation: true,
16
12
  treeShake: true,
13
+ plugins: [
14
+ "@rnx-kit/metro-plugin-cyclic-dependencies-detector",
15
+ "@rnx-kit/metro-plugin-duplicates-checker",
16
+ "@rnx-kit/metro-plugin-typescript",
17
+ ],
17
18
  indexedRamBundle: false,
18
19
  platform: "ios",
19
20
  };
@@ -1,18 +1,36 @@
1
- import { CyclicDependencies } from "@rnx-kit/metro-plugin-cyclic-dependencies-detector";
2
- import { DuplicateDependencies } from "@rnx-kit/metro-plugin-duplicates-checker";
1
+ import CyclicDependencies from "@rnx-kit/metro-plugin-cyclic-dependencies-detector";
2
+ import DuplicateDependencies from "@rnx-kit/metro-plugin-duplicates-checker";
3
3
  import type { InputConfigT } from "metro-config";
4
4
  import { customizeMetroConfig } from "../src/metro-config";
5
5
 
6
- jest.mock("@rnx-kit/metro-plugin-cyclic-dependencies-detector", () => {
7
- return {
8
- CyclicDependencies: jest.fn(),
6
+ function mockPlugin(moduleName: string) {
7
+ const state: { timesCalled: number; options?: Record<string, unknown> } = {
8
+ timesCalled: 0,
9
+ };
10
+
11
+ function PluginMock(options: Record<string, unknown>) {
12
+ state.timesCalled = state.timesCalled + 1;
13
+ state.options = options;
14
+ return () => ({});
15
+ }
16
+
17
+ const Plugin = jest.requireActual(moduleName);
18
+ PluginMock.type = Plugin.default.type;
19
+ PluginMock.__context = state;
20
+ PluginMock.mockClear = () => {
21
+ state.timesCalled = 0;
22
+ state.options = undefined;
9
23
  };
24
+
25
+ return PluginMock;
26
+ }
27
+
28
+ jest.mock("@rnx-kit/metro-plugin-cyclic-dependencies-detector", () => {
29
+ return mockPlugin("@rnx-kit/metro-plugin-cyclic-dependencies-detector");
10
30
  });
11
31
 
12
32
  jest.mock("@rnx-kit/metro-plugin-duplicates-checker", () => {
13
- return {
14
- DuplicateDependencies: jest.fn(),
15
- };
33
+ return mockPlugin("@rnx-kit/metro-plugin-duplicates-checker");
16
34
  });
17
35
 
18
36
  // Always use a new mock since `customizeMetroConfig()` modifies the object.
@@ -26,10 +44,14 @@ function makeMockConfig(): InputConfigT {
26
44
  } as unknown as InputConfigT;
27
45
  }
28
46
 
47
+ function toMock(module: unknown): ReturnType<typeof mockPlugin> {
48
+ return module as ReturnType<typeof mockPlugin>;
49
+ }
50
+
29
51
  describe("cli/metro-config/customizeMetroConfig", () => {
30
52
  afterEach(() => {
31
- (CyclicDependencies as any).mockClear();
32
- (DuplicateDependencies as any).mockClear();
53
+ toMock(CyclicDependencies).mockClear();
54
+ toMock(DuplicateDependencies).mockClear();
33
55
  });
34
56
 
35
57
  test("returns a config with plugins by default", () => {
@@ -47,15 +69,14 @@ describe("cli/metro-config/customizeMetroConfig", () => {
47
69
  expect(typeof inputConfig.serializer.experimentalSerializerHook).toBe(
48
70
  "function"
49
71
  );
50
- expect(CyclicDependencies).toHaveBeenCalledWith();
51
- expect(DuplicateDependencies).toHaveBeenCalledWith();
72
+ expect(toMock(CyclicDependencies).__context).toEqual({ timesCalled: 1 });
73
+ expect(toMock(DuplicateDependencies).__context).toEqual({ timesCalled: 1 });
52
74
  });
53
75
 
54
76
  test("returns a config without a custom serializer when there are no plugins", () => {
55
77
  const inputConfig = makeMockConfig();
56
78
  customizeMetroConfig(inputConfig, {
57
- detectCyclicDependencies: false,
58
- detectDuplicateDependencies: false,
79
+ plugins: ["@rnx-kit/metro-plugin-typescript"],
59
80
  });
60
81
 
61
82
  expect(inputConfig).toEqual({
@@ -68,14 +89,17 @@ describe("cli/metro-config/customizeMetroConfig", () => {
68
89
  expect(typeof inputConfig.serializer.experimentalSerializerHook).toBe(
69
90
  "function"
70
91
  );
71
- expect(CyclicDependencies).not.toHaveBeenCalled();
72
- expect(DuplicateDependencies).not.toHaveBeenCalled();
92
+ expect(toMock(CyclicDependencies).__context).toEqual({ timesCalled: 0 });
93
+ expect(toMock(DuplicateDependencies).__context).toEqual({ timesCalled: 0 });
73
94
  });
74
95
 
75
96
  test("returns a config with only duplicates plugin", () => {
76
97
  const inputConfig = makeMockConfig();
77
98
  customizeMetroConfig(inputConfig, {
78
- detectCyclicDependencies: false,
99
+ plugins: [
100
+ "@rnx-kit/metro-plugin-duplicates-checker",
101
+ "@rnx-kit/metro-plugin-typescript",
102
+ ],
79
103
  });
80
104
 
81
105
  expect(inputConfig).toEqual({
@@ -89,14 +113,17 @@ describe("cli/metro-config/customizeMetroConfig", () => {
89
113
  expect(typeof inputConfig.serializer.experimentalSerializerHook).toBe(
90
114
  "function"
91
115
  );
92
- expect(CyclicDependencies).not.toHaveBeenCalled();
93
- expect(DuplicateDependencies).toHaveBeenCalledWith();
116
+ expect(toMock(CyclicDependencies).__context).toEqual({ timesCalled: 0 });
117
+ expect(toMock(DuplicateDependencies).__context).toEqual({ timesCalled: 1 });
94
118
  });
95
119
 
96
120
  test("returns a config with only cyclic dependencies plugin", () => {
97
121
  const inputConfig = makeMockConfig();
98
122
  customizeMetroConfig(inputConfig, {
99
- detectDuplicateDependencies: false,
123
+ plugins: [
124
+ "@rnx-kit/metro-plugin-cyclic-dependencies-detector",
125
+ "@rnx-kit/metro-plugin-typescript",
126
+ ],
100
127
  });
101
128
 
102
129
  expect(inputConfig).toEqual({
@@ -110,8 +137,8 @@ describe("cli/metro-config/customizeMetroConfig", () => {
110
137
  expect(typeof inputConfig.serializer.experimentalSerializerHook).toBe(
111
138
  "function"
112
139
  );
113
- expect(CyclicDependencies).toHaveBeenCalledWith();
114
- expect(DuplicateDependencies).not.toHaveBeenCalled();
140
+ expect(toMock(CyclicDependencies).__context).toEqual({ timesCalled: 1 });
141
+ expect(toMock(DuplicateDependencies).__context).toEqual({ timesCalled: 0 });
115
142
  });
116
143
 
117
144
  test("forwards plugin options", () => {
@@ -119,8 +146,17 @@ describe("cli/metro-config/customizeMetroConfig", () => {
119
146
  const cyclicDependenciesOptions = { cyclicDependencies: true } as any;
120
147
  const duplicateDependencesOptions = { duplicateDependencies: true } as any;
121
148
  customizeMetroConfig(inputConfig, {
122
- detectCyclicDependencies: cyclicDependenciesOptions,
123
- detectDuplicateDependencies: duplicateDependencesOptions,
149
+ plugins: [
150
+ [
151
+ "@rnx-kit/metro-plugin-cyclic-dependencies-detector",
152
+ cyclicDependenciesOptions,
153
+ ],
154
+ [
155
+ "@rnx-kit/metro-plugin-duplicates-checker",
156
+ duplicateDependencesOptions,
157
+ ],
158
+ "@rnx-kit/metro-plugin-typescript",
159
+ ],
124
160
  });
125
161
 
126
162
  expect(inputConfig).toEqual({
@@ -134,18 +170,21 @@ describe("cli/metro-config/customizeMetroConfig", () => {
134
170
  expect(typeof inputConfig.serializer.experimentalSerializerHook).toBe(
135
171
  "function"
136
172
  );
137
- expect(CyclicDependencies).toHaveBeenCalledWith(cyclicDependenciesOptions);
138
- expect(DuplicateDependencies).toHaveBeenCalledWith(
139
- duplicateDependencesOptions
140
- );
173
+ expect(toMock(CyclicDependencies).__context).toEqual({
174
+ timesCalled: 1,
175
+ options: cyclicDependenciesOptions,
176
+ });
177
+ expect(toMock(DuplicateDependencies).__context).toEqual({
178
+ timesCalled: 1,
179
+ options: duplicateDependencesOptions,
180
+ });
141
181
  });
142
182
 
143
183
  test("returns a config with a custom serializer when tree shaking is enabled", () => {
144
184
  const inputConfig = makeMockConfig();
145
185
  customizeMetroConfig(inputConfig, {
146
- detectCyclicDependencies: false,
147
- detectDuplicateDependencies: false,
148
186
  treeShake: true,
187
+ plugins: ["@rnx-kit/metro-plugin-typescript"],
149
188
  });
150
189
 
151
190
  expect(inputConfig).toEqual({
@@ -161,7 +200,7 @@ describe("cli/metro-config/customizeMetroConfig", () => {
161
200
  expect(typeof inputConfig.serializer.experimentalSerializerHook).toBe(
162
201
  "function"
163
202
  );
164
- expect(CyclicDependencies).not.toHaveBeenCalled();
165
- expect(DuplicateDependencies).not.toHaveBeenCalled();
203
+ expect(toMock(CyclicDependencies).__context).toEqual({ timesCalled: 0 });
204
+ expect(toMock(DuplicateDependencies).__context).toEqual({ timesCalled: 0 });
166
205
  });
167
206
  });