Skip to content

Commit 5026601

Browse files
authoredNov 25, 2021
feat: allow to pass all chokidar options (#4025)
1 parent 7e78bfa commit 5026601

6 files changed

+1344
-172
lines changed
 

‎lib/Server.js

+135-72
Original file line numberDiff line numberDiff line change
@@ -444,14 +444,125 @@ class Server {
444444

445445
const compilerOptions = this.getCompilerOptions();
446446
// TODO remove `{}` after drop webpack v4 support
447-
const watchOptions = compilerOptions.watchOptions || {};
448-
const defaultOptionsForStatic = {
449-
directory: path.join(process.cwd(), "public"),
450-
staticOptions: {},
451-
publicPath: ["/"],
452-
serveIndex: { icons: true },
453-
// Respect options from compiler watchOptions
454-
watch: watchOptions,
447+
const compilerWatchOptions = compilerOptions.watchOptions || {};
448+
const getWatchOptions = (watchOptions = {}) => {
449+
const getPolling = () => {
450+
if (typeof watchOptions.usePolling !== "undefined") {
451+
return watchOptions.usePolling;
452+
}
453+
454+
if (typeof watchOptions.poll !== "undefined") {
455+
return Boolean(watchOptions.poll);
456+
}
457+
458+
if (typeof compilerWatchOptions.poll !== "undefined") {
459+
return Boolean(compilerWatchOptions.poll);
460+
}
461+
462+
return false;
463+
};
464+
const getInterval = () => {
465+
if (typeof watchOptions.interval !== "undefined") {
466+
return watchOptions.interval;
467+
}
468+
469+
if (typeof watchOptions.poll === "number") {
470+
return watchOptions.poll;
471+
}
472+
473+
if (typeof compilerWatchOptions.poll === "number") {
474+
return compilerWatchOptions.poll;
475+
}
476+
};
477+
478+
const usePolling = getPolling();
479+
const interval = getInterval();
480+
const { poll, ...rest } = watchOptions;
481+
482+
return {
483+
ignoreInitial: true,
484+
persistent: true,
485+
followSymlinks: false,
486+
atomic: false,
487+
alwaysStat: true,
488+
ignorePermissionErrors: true,
489+
// Respect options from compiler watchOptions
490+
usePolling,
491+
interval,
492+
ignored: watchOptions.ignored,
493+
// TODO: we respect these options for all watch options and allow developers to pass them to chokidar, but chokidar doesn't have these options maybe we need revisit that in future
494+
...rest,
495+
};
496+
};
497+
const getStaticItem = (optionsForStatic) => {
498+
const getDefaultStaticOptions = () => {
499+
return {
500+
directory: path.join(process.cwd(), "public"),
501+
staticOptions: {},
502+
publicPath: ["/"],
503+
serveIndex: { icons: true },
504+
watch: getWatchOptions(),
505+
};
506+
};
507+
508+
let item;
509+
510+
if (typeof optionsForStatic === "undefined") {
511+
item = getDefaultStaticOptions();
512+
} else if (typeof optionsForStatic === "string") {
513+
item = {
514+
...getDefaultStaticOptions(),
515+
directory: optionsForStatic,
516+
};
517+
} else {
518+
const def = getDefaultStaticOptions();
519+
520+
item = {
521+
directory:
522+
typeof optionsForStatic.directory !== "undefined"
523+
? optionsForStatic.directory
524+
: def.directory,
525+
// TODO: do merge in the next major release
526+
staticOptions:
527+
typeof optionsForStatic.staticOptions !== "undefined"
528+
? optionsForStatic.staticOptions
529+
: def.staticOptions,
530+
publicPath:
531+
typeof optionsForStatic.publicPath !== "undefined"
532+
? optionsForStatic.publicPath
533+
: def.publicPath,
534+
// TODO: do merge in the next major release
535+
serveIndex:
536+
// eslint-disable-next-line no-nested-ternary
537+
typeof optionsForStatic.serveIndex !== "undefined"
538+
? typeof optionsForStatic.serveIndex === "boolean" &&
539+
optionsForStatic.serveIndex
540+
? def.serveIndex
541+
: optionsForStatic.serveIndex
542+
: def.serveIndex,
543+
watch:
544+
// eslint-disable-next-line no-nested-ternary
545+
typeof optionsForStatic.watch !== "undefined"
546+
? // eslint-disable-next-line no-nested-ternary
547+
typeof optionsForStatic.watch === "boolean"
548+
? optionsForStatic.watch
549+
? def.watch
550+
: false
551+
: getWatchOptions(optionsForStatic.watch)
552+
: def.watch,
553+
};
554+
}
555+
556+
if (Server.isAbsoluteURL(item.directory)) {
557+
throw new Error("Using a URL as static.directory is not supported");
558+
}
559+
560+
// ensure that publicPath is an array
561+
if (typeof item.publicPath === "string") {
562+
item.publicPath = [item.publicPath];
563+
}
564+
565+
return item;
455566
};
456567

457568
if (typeof options.allowedHosts === "undefined") {
@@ -921,50 +1032,27 @@ class Server {
9211032
}
9221033

9231034
if (typeof options.static === "undefined") {
924-
options.static = [defaultOptionsForStatic];
1035+
options.static = [getStaticItem()];
9251036
} else if (typeof options.static === "boolean") {
926-
options.static = options.static ? [defaultOptionsForStatic] : false;
1037+
options.static = options.static ? [getStaticItem()] : false;
9271038
} else if (typeof options.static === "string") {
928-
options.static = [
929-
{ ...defaultOptionsForStatic, directory: options.static },
930-
];
1039+
options.static = [getStaticItem(options.static)];
9311040
} else if (Array.isArray(options.static)) {
9321041
options.static = options.static.map((item) => {
9331042
if (typeof item === "string") {
934-
return { ...defaultOptionsForStatic, directory: item };
1043+
return getStaticItem(item);
9351044
}
9361045

937-
return { ...defaultOptionsForStatic, ...item };
1046+
return getStaticItem(item);
9381047
});
9391048
} else {
940-
options.static = [{ ...defaultOptionsForStatic, ...options.static }];
941-
}
942-
943-
if (options.static) {
944-
options.static.forEach((staticOption) => {
945-
if (Server.isAbsoluteURL(staticOption.directory)) {
946-
throw new Error("Using a URL as static.directory is not supported");
947-
}
948-
949-
// ensure that publicPath is an array
950-
if (typeof staticOption.publicPath === "string") {
951-
staticOption.publicPath = [staticOption.publicPath];
952-
}
953-
954-
// ensure that watch is an object if true
955-
if (staticOption.watch === true) {
956-
staticOption.watch = defaultOptionsForStatic.watch;
957-
}
958-
959-
// ensure that serveIndex is an object if true
960-
if (staticOption.serveIndex === true) {
961-
staticOption.serveIndex = defaultOptionsForStatic.serveIndex;
962-
}
963-
});
1049+
options.static = [getStaticItem(options.static)];
9641050
}
9651051

9661052
if (typeof options.watchFiles === "string") {
967-
options.watchFiles = [{ paths: options.watchFiles, options: {} }];
1053+
options.watchFiles = [
1054+
{ paths: options.watchFiles, options: getWatchOptions() },
1055+
];
9681056
} else if (
9691057
typeof options.watchFiles === "object" &&
9701058
options.watchFiles !== null &&
@@ -973,16 +1061,19 @@ class Server {
9731061
options.watchFiles = [
9741062
{
9751063
paths: options.watchFiles.paths,
976-
options: options.watchFiles.options || {},
1064+
options: getWatchOptions(options.watchFiles.options || {}),
9771065
},
9781066
];
9791067
} else if (Array.isArray(options.watchFiles)) {
9801068
options.watchFiles = options.watchFiles.map((item) => {
9811069
if (typeof item === "string") {
982-
return { paths: item, options: {} };
1070+
return { paths: item, options: getWatchOptions() };
9831071
}
9841072

985-
return { paths: item.paths, options: item.options || {} };
1073+
return {
1074+
paths: item.paths,
1075+
options: getWatchOptions(item.options || {}),
1076+
};
9861077
});
9871078
} else {
9881079
options.watchFiles = [];
@@ -2124,36 +2215,8 @@ class Server {
21242215
}
21252216

21262217
watchFiles(watchPath, watchOptions) {
2127-
// duplicate the same massaging of options that watchpack performs
2128-
// https://github.com/webpack/watchpack/blob/master/lib/DirectoryWatcher.js#L49
2129-
// this isn't an elegant solution, but we'll improve it in the future
2130-
const usePolling =
2131-
typeof watchOptions.usePolling !== "undefined"
2132-
? watchOptions.usePolling
2133-
: Boolean(watchOptions.poll);
2134-
const interval =
2135-
// eslint-disable-next-line no-nested-ternary
2136-
typeof watchOptions.interval !== "undefined"
2137-
? watchOptions.interval
2138-
: typeof watchOptions.poll === "number"
2139-
? watchOptions.poll
2140-
: // eslint-disable-next-line no-undefined
2141-
undefined;
2142-
2143-
const finalWatchOptions = {
2144-
ignoreInitial: true,
2145-
persistent: true,
2146-
followSymlinks: false,
2147-
atomic: false,
2148-
alwaysStat: true,
2149-
ignorePermissionErrors: true,
2150-
ignored: watchOptions.ignored,
2151-
usePolling,
2152-
interval,
2153-
};
2154-
21552218
const chokidar = require("chokidar");
2156-
const watcher = chokidar.watch(watchPath, finalWatchOptions);
2219+
const watcher = chokidar.watch(watchPath, watchOptions);
21572220

21582221
// disabling refreshing on changing the content
21592222
if (this.options.liveReload) {

‎test/server/__snapshots__/Server.test.js.snap.webpack4

+540-50
Large diffs are not rendered by default.

‎test/server/__snapshots__/Server.test.js.snap.webpack5

+540-50
Large diffs are not rendered by default.

‎test/server/__snapshots__/watchFiles-option.test.js.snap.webpack4

+56
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`'watchFiles' option should work with options {"interval":400,"poll":200} should pass correct options to chokidar config 1`] = `
4+
Object {
5+
"alwaysStat": true,
6+
"atomic": false,
7+
"followSymlinks": false,
8+
"ignoreInitial": true,
9+
"ignorePermissionErrors": true,
10+
"ignored": undefined,
11+
"interval": 400,
12+
"persistent": true,
13+
"usePolling": true,
14+
}
15+
`;
16+
317
exports[`'watchFiles' option should work with options {"poll":200} should pass correct options to chokidar config 1`] = `
418
Object {
519
"alwaysStat": true,
@@ -28,6 +42,34 @@ Object {
2842
}
2943
`;
3044

45+
exports[`'watchFiles' option should work with options {"usePolling":false,"interval":200,"poll":400} should pass correct options to chokidar config 1`] = `
46+
Object {
47+
"alwaysStat": true,
48+
"atomic": false,
49+
"followSymlinks": false,
50+
"ignoreInitial": true,
51+
"ignorePermissionErrors": true,
52+
"ignored": undefined,
53+
"interval": 200,
54+
"persistent": true,
55+
"usePolling": false,
56+
}
57+
`;
58+
59+
exports[`'watchFiles' option should work with options {"usePolling":false,"poll":200} should pass correct options to chokidar config 1`] = `
60+
Object {
61+
"alwaysStat": true,
62+
"atomic": false,
63+
"followSymlinks": false,
64+
"ignoreInitial": true,
65+
"ignorePermissionErrors": true,
66+
"ignored": undefined,
67+
"interval": 200,
68+
"persistent": true,
69+
"usePolling": false,
70+
}
71+
`;
72+
3173
exports[`'watchFiles' option should work with options {"usePolling":false,"poll":true} should pass correct options to chokidar config 1`] = `
3274
Object {
3375
"alwaysStat": true,
@@ -70,6 +112,20 @@ Object {
70112
}
71113
`;
72114

115+
exports[`'watchFiles' option should work with options {"usePolling":true,"poll":200} should pass correct options to chokidar config 1`] = `
116+
Object {
117+
"alwaysStat": true,
118+
"atomic": false,
119+
"followSymlinks": false,
120+
"ignoreInitial": true,
121+
"ignorePermissionErrors": true,
122+
"ignored": undefined,
123+
"interval": 200,
124+
"persistent": true,
125+
"usePolling": true,
126+
}
127+
`;
128+
73129
exports[`'watchFiles' option should work with options {"usePolling":true} should pass correct options to chokidar config 1`] = `
74130
Object {
75131
"alwaysStat": true,

‎test/server/__snapshots__/watchFiles-option.test.js.snap.webpack5

+56
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`'watchFiles' option should work with options {"interval":400,"poll":200} should pass correct options to chokidar config 1`] = `
4+
Object {
5+
"alwaysStat": true,
6+
"atomic": false,
7+
"followSymlinks": false,
8+
"ignoreInitial": true,
9+
"ignorePermissionErrors": true,
10+
"ignored": undefined,
11+
"interval": 400,
12+
"persistent": true,
13+
"usePolling": true,
14+
}
15+
`;
16+
317
exports[`'watchFiles' option should work with options {"poll":200} should pass correct options to chokidar config 1`] = `
418
Object {
519
"alwaysStat": true,
@@ -28,6 +42,34 @@ Object {
2842
}
2943
`;
3044

45+
exports[`'watchFiles' option should work with options {"usePolling":false,"interval":200,"poll":400} should pass correct options to chokidar config 1`] = `
46+
Object {
47+
"alwaysStat": true,
48+
"atomic": false,
49+
"followSymlinks": false,
50+
"ignoreInitial": true,
51+
"ignorePermissionErrors": true,
52+
"ignored": undefined,
53+
"interval": 200,
54+
"persistent": true,
55+
"usePolling": false,
56+
}
57+
`;
58+
59+
exports[`'watchFiles' option should work with options {"usePolling":false,"poll":200} should pass correct options to chokidar config 1`] = `
60+
Object {
61+
"alwaysStat": true,
62+
"atomic": false,
63+
"followSymlinks": false,
64+
"ignoreInitial": true,
65+
"ignorePermissionErrors": true,
66+
"ignored": undefined,
67+
"interval": 200,
68+
"persistent": true,
69+
"usePolling": false,
70+
}
71+
`;
72+
3173
exports[`'watchFiles' option should work with options {"usePolling":false,"poll":true} should pass correct options to chokidar config 1`] = `
3274
Object {
3375
"alwaysStat": true,
@@ -70,6 +112,20 @@ Object {
70112
}
71113
`;
72114

115+
exports[`'watchFiles' option should work with options {"usePolling":true,"poll":200} should pass correct options to chokidar config 1`] = `
116+
Object {
117+
"alwaysStat": true,
118+
"atomic": false,
119+
"followSymlinks": false,
120+
"ignoreInitial": true,
121+
"ignorePermissionErrors": true,
122+
"ignored": undefined,
123+
"interval": 200,
124+
"persistent": true,
125+
"usePolling": true,
126+
}
127+
`;
128+
73129
exports[`'watchFiles' option should work with options {"usePolling":true} should pass correct options to chokidar config 1`] = `
74130
Object {
75131
"alwaysStat": true,

‎test/server/watchFiles-option.test.js

+17
Original file line numberDiff line numberDiff line change
@@ -327,18 +327,35 @@ describe("'watchFiles' option", () => {
327327
{
328328
usePolling: true,
329329
},
330+
{
331+
usePolling: true,
332+
poll: 200,
333+
},
330334
{
331335
usePolling: false,
332336
},
337+
{
338+
usePolling: false,
339+
poll: 200,
340+
},
333341
{
334342
usePolling: false,
335343
poll: true,
336344
},
345+
{
346+
interval: 400,
347+
poll: 200,
348+
},
337349
{
338350
usePolling: true,
339351
interval: 200,
340352
poll: 400,
341353
},
354+
{
355+
usePolling: false,
356+
interval: 200,
357+
poll: 400,
358+
},
342359
];
343360

344361
optionCases.forEach((optionCase) => {

0 commit comments

Comments
 (0)
Please sign in to comment.