Skip to content

Commit d9bc19d

Browse files
authored
perf: bump chokidar to v4 and remove fsevents dependency (#3960)
1 parent 05c6c65 commit d9bc19d

File tree

12 files changed

+99
-45
lines changed

12 files changed

+99
-45
lines changed

.github/renovate.json5

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@
7373
// require Node 18
7474
'copy-webpack-plugin',
7575
// major version contains breaking changes
76-
'chokidar',
7776
'style-loader',
7877
'http-proxy-middleware',
7978
],

packages/core/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@
6060
"@rslib/core": "0.0.18",
6161
"@types/connect": "3.4.38",
6262
"@types/fs-extra": "^11.0.4",
63+
"@types/is-glob": "^4.0.4",
6364
"@types/node": "^22.9.0",
6465
"@types/on-finished": "2.3.4",
6566
"@types/webpack-bundle-analyzer": "4.7.0",
6667
"@types/ws": "^8.5.13",
6768
"browserslist-load-config": "1.0.0",
68-
"chokidar": "3.6.0",
69+
"chokidar": "^4.0.1",
6970
"commander": "^12.1.0",
7071
"connect": "3.7.0",
7172
"connect-history-api-fallback": "^2.0.0",
@@ -76,6 +77,7 @@
7677
"fs-extra": "^11.2.0",
7778
"html-rspack-plugin": "6.0.2",
7879
"http-proxy-middleware": "^2.0.6",
80+
"is-glob": "^4.0.3",
7981
"jiti": "^1.21.6",
8082
"launch-editor-middleware": "^2.9.1",
8183
"mrmime": "^2.0.0",
@@ -94,15 +96,13 @@
9496
"rspack-manifest-plugin": "5.0.2",
9597
"sirv": "^3.0.0",
9698
"style-loader": "3.3.4",
99+
"tinyglobby": "^0.2.10",
97100
"typescript": "^5.6.3",
98101
"webpack": "^5.96.1",
99102
"webpack-bundle-analyzer": "^4.10.2",
100103
"webpack-merge": "6.0.1",
101104
"ws": "^8.18.0"
102105
},
103-
"optionalDependencies": {
104-
"fsevents": "~2.3.3"
105-
},
106106
"engines": {
107107
"node": ">=16.7.0"
108108
},

packages/core/prebundle.config.mjs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,8 @@ export default {
3939
'webpack-merge',
4040
'html-rspack-plugin',
4141
'mrmime',
42-
{
43-
name: 'chokidar',
44-
externals: {
45-
fsevents: 'fsevents',
46-
},
47-
},
42+
'tinyglobby',
43+
'chokidar',
4844
{
4945
name: 'picocolors',
5046
beforeBundle({ depPath }) {

packages/core/src/cli/init.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ export async function init({
6060

6161
const paths = castArray(watchFilesConfig.paths);
6262
if (watchFilesConfig.options) {
63-
watchFilesForRestart(paths, watchFilesConfig.options);
63+
watchFilesForRestart(paths, root, watchFilesConfig.options);
6464
} else {
6565
files.push(...paths);
6666
}
6767
}
6868
}
6969

70-
watchFilesForRestart(files);
70+
watchFilesForRestart(files, root);
7171
}
7272

7373
config.source ||= {};

packages/core/src/config.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from 'node:fs';
22
import { createRequire } from 'node:module';
33
import { dirname, isAbsolute, join } from 'node:path';
4-
import type { WatchOptions } from '../compiled/chokidar/index.js';
4+
import type { ChokidarOptions } from '../compiled/chokidar/index.js';
55
import RspackChain from '../compiled/rspack-chain/index.js';
66
import {
77
ASSETS_DIST_DIR,
@@ -33,6 +33,7 @@ import {
3333
import { logger } from './logger';
3434
import { mergeRsbuildConfig } from './mergeConfig';
3535
import { restartDevServer } from './server/restart';
36+
import { createChokidar } from './server/watchFiles.js';
3637
import type {
3738
InspectConfigOptions,
3839
InspectConfigResult,
@@ -348,14 +349,14 @@ const resolveConfigPath = (root: string, customConfig?: string) => {
348349

349350
export async function watchFilesForRestart(
350351
files: string[],
351-
watchOptions?: WatchOptions,
352+
root: string,
353+
watchOptions?: ChokidarOptions,
352354
): Promise<void> {
353355
if (!files.length) {
354356
return;
355357
}
356358

357-
const chokidar = await import('../compiled/chokidar/index.js');
358-
const watcher = chokidar.watch(files, {
359+
const watcher = await createChokidar(files, root, {
359360
// do not trigger add for initial files
360361
ignoreInitial: true,
361362
// If watching fails due to read permissions, the errors will be suppressed silently.

packages/core/src/server/devServer.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,15 @@ export async function createDevServer<
249249

250250
const compileMiddlewareAPI = runCompile ? await startCompile() : undefined;
251251

252+
const root = options.context.rootPath;
253+
252254
const fileWatcher = await setupWatchFiles({
253255
dev: devConfig,
254256
server: config.server,
255257
compileMiddlewareAPI,
258+
root,
256259
});
257260

258-
const pwd = options.context.rootPath;
259-
260261
const readFileSync = (fileName: string) => {
261262
if ('readFileSync' in outputFileSystem) {
262263
// bundle require needs a synchronous method, although readFileSync is not within the outputFileSystem type definition, but nodejs fs API implemented.
@@ -298,7 +299,7 @@ export async function createDevServer<
298299
);
299300

300301
const devMiddlewares = await getMiddlewares({
301-
pwd,
302+
pwd: root,
302303
compileMiddlewareAPI,
303304
dev: devConfig,
304305
server: config.server,

packages/core/src/server/watchFiles.ts

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import isGlob from 'is-glob';
12
import type { FSWatcher } from '../../compiled/chokidar/index.js';
23
import { normalizePublicDirs } from '../config';
34
import { castArray } from '../helpers';
45
import type {
5-
ChokidarWatchOptions,
6+
ChokidarOptions,
67
DevConfig,
78
ServerConfig,
89
WatchFiles,
@@ -13,6 +14,7 @@ type WatchFilesOptions = {
1314
dev: DevConfig;
1415
server: ServerConfig;
1516
compileMiddlewareAPI?: CompileMiddlewareAPI;
17+
root: string;
1618
};
1719

1820
export async function setupWatchFiles(options: WatchFilesOptions): Promise<
@@ -21,17 +23,22 @@ export async function setupWatchFiles(options: WatchFilesOptions): Promise<
2123
}
2224
| undefined
2325
> {
24-
const { dev, server, compileMiddlewareAPI } = options;
26+
const { dev, server, root, compileMiddlewareAPI } = options;
2527

2628
const { hmr, liveReload } = dev;
2729
if ((!hmr && !liveReload) || !compileMiddlewareAPI) {
2830
return;
2931
}
3032

31-
const closeDevFilesWatcher = await watchDevFiles(dev, compileMiddlewareAPI);
33+
const closeDevFilesWatcher = await watchDevFiles(
34+
dev,
35+
compileMiddlewareAPI,
36+
root,
37+
);
3238
const serverFilesWatcher = await watchServerFiles(
3339
server,
3440
compileMiddlewareAPI,
41+
root,
3542
);
3643

3744
return {
@@ -47,6 +54,7 @@ export async function setupWatchFiles(options: WatchFilesOptions): Promise<
4754
async function watchDevFiles(
4855
devConfig: DevConfig,
4956
compileMiddlewareAPI: CompileMiddlewareAPI,
57+
root: string,
5058
) {
5159
const { watchFiles } = devConfig;
5260
if (!watchFiles) {
@@ -57,7 +65,11 @@ async function watchDevFiles(
5765

5866
for (const { paths, options, type } of castArray(watchFiles)) {
5967
const watchOptions = prepareWatchOptions(paths, options, type);
60-
const watcher = await startWatchFiles(watchOptions, compileMiddlewareAPI);
68+
const watcher = await startWatchFiles(
69+
watchOptions,
70+
compileMiddlewareAPI,
71+
root,
72+
);
6173
if (watcher) {
6274
watchers.push(watcher);
6375
}
@@ -73,6 +85,7 @@ async function watchDevFiles(
7385
function watchServerFiles(
7486
serverConfig: ServerConfig,
7587
compileMiddlewareAPI: CompileMiddlewareAPI,
88+
root: string,
7689
) {
7790
const publicDirs = normalizePublicDirs(serverConfig.publicDir);
7891
if (!publicDirs.length) {
@@ -88,12 +101,12 @@ function watchServerFiles(
88101
}
89102

90103
const watchOptions = prepareWatchOptions(watchPaths);
91-
return startWatchFiles(watchOptions, compileMiddlewareAPI);
104+
return startWatchFiles(watchOptions, compileMiddlewareAPI, root);
92105
}
93106

94107
function prepareWatchOptions(
95108
paths: string | string[],
96-
options: ChokidarWatchOptions = {},
109+
options: ChokidarOptions = {},
97110
type?: WatchFiles['type'],
98111
) {
99112
return {
@@ -103,20 +116,53 @@ function prepareWatchOptions(
103116
};
104117
}
105118

119+
export async function createChokidar(
120+
pathOrGlobs: string[],
121+
root: string,
122+
options: ChokidarOptions,
123+
): Promise<FSWatcher> {
124+
const chokidar = await import('../../compiled/chokidar/index.js');
125+
126+
const watchFiles: Set<string> = new Set();
127+
128+
const globPatterns = pathOrGlobs.filter((pathOrGlob) => {
129+
if (isGlob(pathOrGlob)) {
130+
return true;
131+
}
132+
watchFiles.add(pathOrGlob);
133+
return false;
134+
});
135+
136+
if (globPatterns.length) {
137+
const tinyglobby = await import('../../compiled/tinyglobby/index.js');
138+
// interop default to make both CJS and ESM work
139+
const { glob } = tinyglobby.default || tinyglobby;
140+
const files = await glob(globPatterns, {
141+
cwd: root,
142+
absolute: true,
143+
});
144+
for (const file of files) {
145+
watchFiles.add(file);
146+
}
147+
}
148+
149+
return chokidar.watch(Array.from(watchFiles), options);
150+
}
151+
106152
async function startWatchFiles(
107153
{
108154
paths,
109155
options,
110156
type = 'reload-page',
111157
}: ReturnType<typeof prepareWatchOptions>,
112158
compileMiddlewareAPI: CompileMiddlewareAPI,
159+
root: string,
113160
) {
114161
if (type !== 'reload-page') {
115162
return;
116163
}
117164

118-
const chokidar = await import('../../compiled/chokidar/index.js');
119-
const watcher = chokidar.watch(paths, options);
165+
const watcher = await createChokidar(paths, root, options);
120166

121167
watcher.on('change', () => {
122168
compileMiddlewareAPI.sockWrite('static-changed');

packages/core/src/types/config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
SwcLoaderOptions,
1313
rspack,
1414
} from '@rspack/core';
15-
import type { WatchOptions } from '../../compiled/chokidar/index.js';
15+
import type { ChokidarOptions } from '../../compiled/chokidar/index.js';
1616
import type {
1717
Options as HttpProxyOptions,
1818
Filter as ProxyFilter,
@@ -1253,11 +1253,11 @@ export type ClientConfig = {
12531253
export type NormalizedClientConfig = Pick<ClientConfig, 'protocol'> &
12541254
Omit<Required<ClientConfig>, 'protocol'>;
12551255

1256-
export type ChokidarWatchOptions = WatchOptions;
1256+
export type { ChokidarOptions };
12571257

12581258
export type WatchFiles = {
12591259
paths: string | string[];
1260-
options?: WatchOptions;
1260+
options?: ChokidarOptions;
12611261
type?: 'reload-page' | 'reload-server';
12621262
};
12631263

pnpm-lock.yaml

Lines changed: 16 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)