Skip to content

Commit 2bc7645

Browse files
⚡ optimize plugin detection with concurrent operations (#26)
Replaced the sequential `for...of` loop in `checkPlugins` with `Promise.all` to execute `plugin.detect()` calls concurrently. This significantly reduces the time required for the detection phase, especially as the number of plugins grows. The results are processed in the original order to ensure deterministic behavior for logging and merging `bundleParams`. Measured improvement: - Baseline (3 plugins, 100ms each): ~311ms - Optimized (3 plugins, 100ms each): ~102ms - Speedup: ~3x for the simulated case. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: sunnylqm <615282+sunnylqm@users.noreply.github.com>
1 parent 62c129a commit 2bc7645

2 files changed

Lines changed: 93 additions & 11 deletions

File tree

src/utils/check-plugin.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,26 @@ export async function checkPlugins(): Promise<BundleParams> {
1313
sourcemap: false,
1414
};
1515

16-
for (const plugin of plugins) {
17-
try {
18-
const isEnabled = await plugin.detect();
19-
if (isEnabled && plugin.bundleParams) {
20-
Object.assign(params, plugin.bundleParams);
21-
console.log(t('pluginDetected', { name: plugin.name }));
16+
const results = await Promise.all(
17+
plugins.map(async (plugin) => {
18+
try {
19+
const isEnabled = await plugin.detect();
20+
return { isEnabled, error: null };
21+
} catch (error) {
22+
return { isEnabled: false, error };
2223
}
23-
} catch (err) {
24-
console.warn(
25-
t('pluginDetectionError', { name: plugin.name, error: err }),
26-
);
24+
}),
25+
);
26+
27+
results.forEach(({ isEnabled, error }, index) => {
28+
const plugin = plugins[index];
29+
if (error) {
30+
console.warn(t('pluginDetectionError', { name: plugin.name, error }));
31+
} else if (isEnabled && plugin.bundleParams) {
32+
Object.assign(params, plugin.bundleParams);
33+
console.log(t('pluginDetected', { name: plugin.name }));
2734
}
28-
}
35+
});
2936

3037
return params;
3138
}

tests/check-plugin.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { describe, expect, mock, spyOn, test, beforeEach, afterEach } from 'bun:test';
2+
3+
// Mock dependencies before imports
4+
mock.module('fs-extra', () => ({
5+
default: {
6+
access: async () => {},
7+
},
8+
}));
9+
10+
mock.module('i18next', () => ({
11+
default: {
12+
init: () => {},
13+
t: (key: string) => key,
14+
},
15+
}));
16+
17+
import { checkPlugins } from '../src/utils/check-plugin';
18+
import * as pluginConfig from '../src/utils/plugin-config';
19+
20+
describe('checkPlugins', () => {
21+
test('should detect plugins concurrently (simulated)', async () => {
22+
const mockPlugins = [
23+
{
24+
name: 'plugin1',
25+
bundleParams: { p1: true },
26+
detect: async () => {
27+
await new Promise(resolve => setTimeout(resolve, 100));
28+
return true;
29+
}
30+
},
31+
{
32+
name: 'plugin2',
33+
bundleParams: { p2: true },
34+
detect: async () => {
35+
await new Promise(resolve => setTimeout(resolve, 100));
36+
return true;
37+
}
38+
},
39+
{
40+
name: 'plugin3',
41+
bundleParams: { p3: true },
42+
detect: async () => {
43+
await new Promise(resolve => setTimeout(resolve, 100));
44+
return true;
45+
}
46+
}
47+
];
48+
49+
// Replacing the plugins array in plugin-config
50+
const originalPlugins = [...pluginConfig.plugins];
51+
(pluginConfig.plugins as any).splice(0, pluginConfig.plugins.length, ...mockPlugins);
52+
53+
const start = Date.now();
54+
const result = await checkPlugins();
55+
const end = Date.now();
56+
const duration = end - start;
57+
58+
console.log(`Duration with optimized implementation: ${duration}ms`);
59+
60+
expect(result).toEqual({
61+
sentry: false, // default
62+
sourcemap: false, // default
63+
p1: true,
64+
p2: true,
65+
p3: true
66+
} as any);
67+
68+
// Now it's concurrent, so we expect around 100ms.
69+
// We'll allow some buffer, but it should definitely be less than 250ms.
70+
expect(duration).toBeLessThan(250);
71+
72+
// Restore original plugins
73+
(pluginConfig.plugins as any).splice(0, pluginConfig.plugins.length, ...originalPlugins);
74+
});
75+
});

0 commit comments

Comments
 (0)