@kanso-labs/unplugin-style-dictionary 0.10.13 → 0.10.15

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.
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { colourAllowed, paint } from "./colour.js";
2
2
  import { asError, errorMessage } from "./errors.js";
3
3
  import { resolveConfigOption } from "./config.js";
4
- import { expandPatterns, watchPatternsOf } from "./patterns.js";
4
+ import { expandPatterns, realpathOf, watchOptionPatterns, watchPatternsOf } from "./patterns.js";
5
5
  import { runBuilds } from "./compile.js";
6
+ import { checkOptions } from "./options.js";
6
7
  import { createScheduler } from "./scheduler.js";
7
- import { isWatchedSource } from "./watch-filter.js";
8
+ import { compileWatchPatterns, isWatchedSource, matchesWatchedFile, registeredSpellingOf } from "./watch-filter.js";
8
9
  import fs from "node:fs";
9
10
  import path from "node:path";
10
11
  import { createUnplugin } from "unplugin";
@@ -28,11 +29,14 @@ function discardTemporaryFile(temporary) {
28
29
  function isMessageChannel(value) {
29
30
  return typeof value === "function";
30
31
  }
31
- function nodeModulesNegations(paths) {
32
- const negations = /* @__PURE__ */ new Set();
32
+ function nodeModulesNegations(patterns, paths) {
33
+ const reaching = [...patterns.globs, ...patterns.literals].filter((pattern) => pattern.includes("/node_modules/"));
34
+ const negations = new Set(reaching.map((pattern) => `!${pattern}`));
33
35
  for (const file of paths) {
34
36
  const normalised = file.replace(/\\/g, "/");
35
- if (normalised.includes("/node_modules/")) negations.add(`!${normalised}`);
37
+ if (!normalised.includes("/node_modules/") || isDirectory(file)) continue;
38
+ if (matchesWatchedFile(normalised, reaching)) continue;
39
+ negations.add(`!${normalised}`);
36
40
  }
37
41
  return Array.from(negations);
38
42
  }
@@ -46,25 +50,93 @@ function nodeModulesWatchDirectories(paths) {
46
50
  return Array.from(directories);
47
51
  }
48
52
  let temporaryFileCounter = 0;
49
- async function rendersWhatIsAlreadyThere(temporary, destination) {
53
+ const sweptTemporaryPrefixes = /* @__PURE__ */ new Set();
54
+ const ABANDONED_TEMPORARY_AGE_MS = 36e5;
55
+ async function alreadyHolds(destination, rendered) {
50
56
  try {
51
- const [existing, rendered] = await Promise.all([fs.promises.readFile(destination), fs.promises.readFile(temporary)]);
52
- return existing.equals(rendered);
57
+ return (await fs.promises.readFile(destination)).equals(rendered);
53
58
  } catch {
54
59
  return false;
55
60
  }
56
61
  }
57
- function rendersWhatIsAlreadyThereSync(temporary, destination) {
62
+ function alreadyHoldsSync(destination, rendered) {
58
63
  try {
59
- return fs.readFileSync(destination).equals(fs.readFileSync(temporary));
64
+ return fs.readFileSync(destination).equals(rendered);
60
65
  } catch {
61
66
  return false;
62
67
  }
63
68
  }
69
+ function atomicTargetOf(destination) {
70
+ try {
71
+ return fs.realpathSync(destination);
72
+ } catch {
73
+ try {
74
+ return path.resolve(path.dirname(destination), fs.readlinkSync(destination));
75
+ } catch {
76
+ return destination;
77
+ }
78
+ }
79
+ }
80
+ function bytesToWrite(data, options) {
81
+ if (ArrayBuffer.isView(data)) return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
82
+ if (typeof data !== "string") return void 0;
83
+ const encoding = typeof options === "string" ? options : options?.encoding;
84
+ return Buffer.from(data, encoding ?? "utf8");
85
+ }
86
+ function isOlderThan(file, ageMs) {
87
+ try {
88
+ return Date.now() - fs.statSync(file).mtimeMs > ageMs;
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+ function isRunning(pid) {
94
+ try {
95
+ process.kill(pid, 0);
96
+ return true;
97
+ } catch (err) {
98
+ return !(typeof err === "object" && err !== null && "code" in err && err.code === "ESRCH");
99
+ }
100
+ }
101
+ function keepExistingMode(temporary, target) {
102
+ let existing;
103
+ try {
104
+ existing = fs.statSync(target);
105
+ } catch {
106
+ return;
107
+ }
108
+ fs.chmodSync(temporary, existing.mode & 4095);
109
+ if (process.getuid?.() === 0) fs.chownSync(temporary, existing.uid, existing.gid);
110
+ }
111
+ function sweepAbandonedTemporaries(destination) {
112
+ const directory = path.dirname(destination);
113
+ const stem = path.basename(destination, path.extname(destination));
114
+ const prefix = path.join(directory, `.${stem}.`);
115
+ if (sweptTemporaryPrefixes.has(prefix)) return;
116
+ sweptTemporaryPrefixes.add(prefix);
117
+ let names;
118
+ try {
119
+ names = fs.readdirSync(directory);
120
+ } catch {
121
+ return;
122
+ }
123
+ for (const name of names) {
124
+ const writer = temporaryWriterOf(name, stem);
125
+ if (writer === void 0) continue;
126
+ const file = path.join(directory, name);
127
+ if (isRunning(writer) && !isOlderThan(file, ABANDONED_TEMPORARY_AGE_MS)) continue;
128
+ discardTemporaryFile(file);
129
+ }
130
+ }
64
131
  function temporaryPathFor(destination) {
65
132
  const extension = path.extname(destination);
66
133
  return path.join(path.dirname(destination), `.${path.basename(destination, extension)}.${process.pid}.${temporaryFileCounter++}.tmp`);
67
134
  }
135
+ function temporaryWriterOf(name, stem) {
136
+ if (!name.startsWith(`.${stem}.`) || !name.endsWith(".tmp")) return void 0;
137
+ const numbers = /^(\d+)\.\d+$/.exec(name.slice(stem.length + 2, -4));
138
+ return numbers ? Number(numbers[1]) : void 0;
139
+ }
68
140
  const RENAME_RETRY_CODES = /* @__PURE__ */ new Set(["EBUSY", "EPERM"]);
69
141
  const RENAME_RETRY_DELAYS_MS = [
70
142
  1,
@@ -117,14 +189,19 @@ function renameWithRetrySync(temporary, destination) {
117
189
  }
118
190
  const writeFileAtomic = async (file, data, options) => {
119
191
  if (typeof file !== "string") return fs.promises.writeFile(file, data, options);
120
- const temporary = temporaryPathFor(file);
192
+ const target = atomicTargetOf(file);
193
+ sweepAbandonedTemporaries(target);
194
+ const bytes = bytesToWrite(data, options);
195
+ if (bytes && await alreadyHolds(target, bytes)) return;
196
+ const temporary = temporaryPathFor(target);
121
197
  try {
122
198
  await fs.promises.writeFile(temporary, data, options);
123
- if (await rendersWhatIsAlreadyThere(temporary, file)) {
199
+ if (!bytes && await alreadyHolds(target, await fs.promises.readFile(temporary))) {
124
200
  discardTemporaryFile(temporary);
125
201
  return;
126
202
  }
127
- await renameWithRetry(temporary, file);
203
+ keepExistingMode(temporary, target);
204
+ await renameWithRetry(temporary, target);
128
205
  } catch (err) {
129
206
  discardTemporaryFile(temporary);
130
207
  throw err;
@@ -135,35 +212,125 @@ const writeFileSyncAtomic = (file, data, options) => {
135
212
  fs.writeFileSync(file, data, options);
136
213
  return;
137
214
  }
138
- const temporary = temporaryPathFor(file);
215
+ const target = atomicTargetOf(file);
216
+ sweepAbandonedTemporaries(target);
217
+ const bytes = bytesToWrite(data, options);
218
+ if (bytes && alreadyHoldsSync(target, bytes)) return;
219
+ const temporary = temporaryPathFor(target);
139
220
  try {
140
221
  fs.writeFileSync(temporary, data, options);
141
- if (rendersWhatIsAlreadyThereSync(temporary, file)) {
142
- discardTemporaryFile(temporary);
143
- return;
144
- }
145
- renameWithRetrySync(temporary, file);
222
+ keepExistingMode(temporary, target);
223
+ renameWithRetrySync(temporary, target);
224
+ } catch (err) {
225
+ discardTemporaryFile(temporary);
226
+ throw err;
227
+ }
228
+ };
229
+ const copyFileAtomic = async (source, destination, mode) => {
230
+ if (typeof destination !== "string" || excludesExisting(mode)) return fs.promises.copyFile(source, destination, mode);
231
+ const target = atomicTargetOf(destination);
232
+ sweepAbandonedTemporaries(target);
233
+ if (await alreadyHolds(target, await fs.promises.readFile(source))) return;
234
+ const temporary = temporaryPathFor(target);
235
+ try {
236
+ await fs.promises.copyFile(source, temporary, mode);
237
+ await renameWithRetry(temporary, target);
238
+ } catch (err) {
239
+ discardTemporaryFile(temporary);
240
+ throw err;
241
+ }
242
+ };
243
+ const copyFileSyncAtomic = (source, destination, mode) => {
244
+ if (typeof destination !== "string" || excludesExisting(mode)) {
245
+ fs.copyFileSync(source, destination, mode);
246
+ return;
247
+ }
248
+ const target = atomicTargetOf(destination);
249
+ sweepAbandonedTemporaries(target);
250
+ if (alreadyHoldsSync(target, fs.readFileSync(source))) return;
251
+ const temporary = temporaryPathFor(target);
252
+ try {
253
+ fs.copyFileSync(source, temporary, mode);
254
+ renameWithRetrySync(temporary, target);
146
255
  } catch (err) {
147
256
  discardTemporaryFile(temporary);
148
257
  throw err;
149
258
  }
150
259
  };
260
+ function excludesExisting(mode) {
261
+ return mode !== void 0 && (mode & fs.constants.COPYFILE_EXCL) !== 0;
262
+ }
263
+ const cpAtomic = async (source, destination, options) => {
264
+ if (typeof source !== "string" || typeof destination !== "string" || !walksCopy(options) || isWithin(destination, source)) return fs.promises.cp(source, destination, options);
265
+ await fs.promises.mkdir(path.dirname(path.resolve(destination)), { recursive: true });
266
+ await copyTreeAtomic(source, destination, options);
267
+ };
268
+ async function copyTreeAtomic(source, destination, options) {
269
+ const stat = await fs.promises.lstat(source);
270
+ const existing = await fs.promises.lstat(destination).catch(() => void 0);
271
+ if (stat.isFile() && !existing?.isDirectory()) {
272
+ await copyFileAtomic(source, destination, options?.mode);
273
+ return;
274
+ }
275
+ if (!stat.isDirectory() || !options?.recursive || existing && !existing.isDirectory()) {
276
+ await fs.promises.cp(source, destination, options);
277
+ return;
278
+ }
279
+ const created = await fs.promises.mkdir(destination, { recursive: true });
280
+ for (const entry of await fs.promises.readdir(source)) await copyTreeAtomic(path.join(source, entry), path.join(destination, entry), options);
281
+ if (created !== void 0) await fs.promises.chmod(destination, stat.mode);
282
+ }
283
+ function isWithin(candidate, directory) {
284
+ const relative = path.relative(path.resolve(directory), path.resolve(candidate));
285
+ return !(relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative));
286
+ }
287
+ function walksCopy(options) {
288
+ return Object.entries(options ?? {}).every(([key, value]) => value === void 0 || key === "mode" || key === "recursive" || key === "force" && value === true);
289
+ }
151
290
  const atomicVolume = Object.create(fs, {
152
- promises: { value: Object.create(fs.promises, { writeFile: { value: writeFileAtomic } }) },
291
+ copyFileSync: { value: copyFileSyncAtomic },
292
+ promises: { value: Object.create(fs.promises, {
293
+ copyFile: { value: copyFileAtomic },
294
+ cp: { value: cpAtomic },
295
+ writeFile: { value: writeFileAtomic }
296
+ }) },
153
297
  writeFileSync: { value: writeFileSyncAtomic }
154
298
  });
155
299
  const destinationRecords = /* @__PURE__ */ new Map();
156
300
  const persistedRecordFiles = /* @__PURE__ */ new Map();
157
301
  const compilesInFlight = /* @__PURE__ */ new Map();
158
- function buildKey(root, resolved) {
302
+ function buildKey(root, resolved, settings) {
159
303
  try {
160
- return JSON.stringify([root, resolved.map((item) => item.file ?? item.config)], (_key, value) => typeof value === "function" ? `[fn]${String(value)}` : value);
304
+ return JSON.stringify([
305
+ root,
306
+ resolved.map((item) => item.file ?? item.config),
307
+ settings
308
+ ], (_key, value) => typeof value === "function" ? `[fn]${String(value)}` : value);
161
309
  } catch {
162
310
  return null;
163
311
  }
164
312
  }
313
+ function contextDependenciesOf(context) {
314
+ const native = context.getNativeBuildContext?.();
315
+ const compilation = typeof native === "object" && native !== null && "compilation" in native ? native.compilation : void 0;
316
+ const dependencies = typeof compilation === "object" && compilation !== null && "contextDependencies" in compilation ? compilation.contextDependencies : void 0;
317
+ if (typeof dependencies !== "object" || dependencies === null || !("add" in dependencies)) return;
318
+ const add = dependencies.add;
319
+ if (typeof add !== "function") return void 0;
320
+ return { add: (entry) => {
321
+ Reflect.apply(add, dependencies, [entry]);
322
+ } };
323
+ }
324
+ function isDirectory(file) {
325
+ try {
326
+ return fs.statSync(file).isDirectory();
327
+ } catch {
328
+ return false;
329
+ }
330
+ }
165
331
  const unpluginFactory = (options = {}, meta) => {
166
332
  const isWebpack = meta.framework === "webpack" || meta.framework === "rspack";
333
+ checkOptions(options);
167
334
  const { cache = true, errorOverlay = true, failOnError = "build", logLevel, onBuildEnd, onBuildError, onBuildStart, platforms: platformsOption, report = true, root: rootOption, silent = false } = options;
168
335
  const level = logLevel ?? (silent ? "silent" : void 0);
169
336
  const quiet = level === "silent" || level === "warn";
@@ -184,7 +351,27 @@ const unpluginFactory = (options = {}, meta) => {
184
351
  };
185
352
  let root = rootOption ? path.resolve(process.cwd(), rootOption) : process.cwd();
186
353
  const generatedDestinations = /* @__PURE__ */ new Set();
187
- let cachedPatterns;
354
+ let cachedMatcher;
355
+ const linkedPaths = /* @__PURE__ */ new Map();
356
+ const addWatchFiles = (context, paths) => {
357
+ const contextDependencies = isWebpack ? contextDependenciesOf(context) : void 0;
358
+ const register = (file) => {
359
+ if (contextDependencies && isDirectory(file)) {
360
+ contextDependencies.add(path.resolve(file));
361
+ watchedDirectories.add(file);
362
+ return;
363
+ }
364
+ context.addWatchFile(file);
365
+ };
366
+ for (const file of paths) {
367
+ register(file);
368
+ const real = realpathOf(file);
369
+ if (real === void 0 || real === file) continue;
370
+ linkedPaths.set(real, file);
371
+ register(real);
372
+ }
373
+ };
374
+ const watchedDirectories = /* @__PURE__ */ new Set();
188
375
  let watchRebuild = false;
189
376
  let hasCompiled = false;
190
377
  let hostClosed = false;
@@ -222,21 +409,49 @@ const unpluginFactory = (options = {}, meta) => {
222
409
  }
223
410
  console.log(paint(type === "success" ? "32" : "36", `${prefix} ${message}`, stdoutColour));
224
411
  };
225
- const resolveConfigs = async () => resolveConfigOption({
226
- config: options.config,
227
- configContext,
228
- discovery,
229
- log,
230
- root
231
- });
232
- const getWatchTargets = async (resolvedConfigs) => {
412
+ const logTable = (render) => {
413
+ if (host?.info) {
414
+ for (const line of render(false)) host.info(line);
415
+ return;
416
+ }
417
+ for (const line of render(stdoutColour)) console.log(line);
418
+ };
419
+ const saidAboutConfig = /* @__PURE__ */ new Set();
420
+ const logConfig = (message, type) => {
421
+ if (saidAboutConfig.has(message)) return;
422
+ saidAboutConfig.add(message);
423
+ log(message, type);
424
+ };
425
+ const resolveConfigs = async () => {
426
+ const resolved = await resolveConfigOption({
427
+ config: options.config,
428
+ configContext,
429
+ discovery,
430
+ log: logConfig,
431
+ root
432
+ });
433
+ if (!options.watch) return resolved;
434
+ const watched = (await expandPatterns(watchOptionPatterns(root, options.watch), log)).filter((file) => !isDirectory(file));
435
+ for (const item of resolved) if (item.file) item.watched = watched;
436
+ return resolved;
437
+ };
438
+ const deriveWatchPatterns = async (resolvedConfigs) => {
233
439
  const patterns = await watchPatternsOf({
234
- log,
440
+ log: logConfig,
235
441
  root,
236
442
  watch: options.watch
237
443
  }, resolvedConfigs);
238
- cachedPatterns = patterns;
444
+ const matcher = compileWatchPatterns(patterns);
445
+ cachedMatcher = matcher;
446
+ return {
447
+ matcher,
448
+ patterns
449
+ };
450
+ };
451
+ const getWatchTargets = async (resolvedConfigs) => {
452
+ const { matcher, patterns } = await deriveWatchPatterns(resolvedConfigs);
239
453
  return {
454
+ matcher,
240
455
  paths: await expandPatterns(patterns, log),
241
456
  patterns
242
457
  };
@@ -247,7 +462,9 @@ const unpluginFactory = (options = {}, meta) => {
247
462
  failOnError,
248
463
  generatedDestinations,
249
464
  log,
465
+ logTable,
250
466
  notifyBuildOutcome: (error) => {
467
+ if (!error) saidAboutConfig.clear();
251
468
  notifyBuildOutcome?.(error);
252
469
  },
253
470
  onBuildEnd,
@@ -258,13 +475,20 @@ const unpluginFactory = (options = {}, meta) => {
258
475
  quiet,
259
476
  report,
260
477
  root: () => root,
261
- stdoutColour,
262
478
  verbosity,
263
479
  volume: atomicVolume,
264
480
  watch: options.watch
265
481
  };
266
482
  const compileOnceAcrossInstances = async (resolvedConfigs) => {
267
- const key = buildKey(root, resolvedConfigs);
483
+ const key = buildKey(root, resolvedConfigs, {
484
+ cache,
485
+ failOnError,
486
+ onBuildEnd,
487
+ onBuildError,
488
+ onBuildStart,
489
+ platforms: platformsOption,
490
+ watch: options.watch
491
+ });
268
492
  if (key === null) {
269
493
  await runBuilds(instance, resolvedConfigs);
270
494
  return;
@@ -321,25 +545,55 @@ const unpluginFactory = (options = {}, meta) => {
321
545
  host = { error: (message) => {
322
546
  pending.push(message);
323
547
  } };
548
+ let heldFailure;
324
549
  compiler.hooks.compilation.tap("unplugin-style-dictionary", (compilation) => {
325
550
  for (const message of pending.splice(0)) {
326
551
  const reported = new Error(message);
327
552
  reported.name = "UnpluginStyleDictionaryWarning";
328
553
  compilation.warnings.push(reported);
329
554
  }
555
+ if (heldFailure) {
556
+ compilation.errors.push(heldFailure);
557
+ heldFailure = void 0;
558
+ }
330
559
  });
331
560
  const drainToConsole = () => {
332
561
  for (const message of pending.splice(0)) console.error(paint("31", message, stderrColour));
333
562
  };
334
563
  compiler.hooks.failed.tap("unplugin-style-dictionary", drainToConsole);
335
564
  compiler.hooks.done.tap("unplugin-style-dictionary", drainToConsole);
336
- compiler.hooks.beforeCompile.tapPromise("unplugin-style-dictionary", async () => {
565
+ const compileTokens = async () => {
337
566
  isWatching = compiler.watchMode;
338
567
  const resolved = await resolveConfigs();
339
568
  if (resolved.length === 0) return;
340
569
  await compileOnceAcrossInstances(resolved);
341
570
  hasCompiled = true;
342
- });
571
+ };
572
+ const changedSource = () => {
573
+ const changed = [...compiler.modifiedFiles ?? [], ...compiler.removedFiles ?? []];
574
+ for (const file of changed) {
575
+ const id = registeredSpellingOf(file, linkedPaths);
576
+ if (!cachedMatcher || watchedDirectories.has(id.replace(/\\/g, "/")) || isWatchedSource(id, cachedMatcher, generatedDestinations)) return id;
577
+ }
578
+ };
579
+ const rebuildTokens = async () => {
580
+ if (!hasCompiled || compiler.modifiedFiles === void 0) {
581
+ await compileTokens();
582
+ return;
583
+ }
584
+ isWatching = compiler.watchMode;
585
+ const changed = changedSource();
586
+ if (changed === void 0) return;
587
+ const resolved = await resolveConfigs();
588
+ if (resolved.length === 0) return;
589
+ try {
590
+ await runBuilds(instance, resolved, path.basename(changed));
591
+ } catch (err) {
592
+ heldFailure = asError(err);
593
+ }
594
+ };
595
+ compiler.hooks.run.tapPromise("unplugin-style-dictionary", compileTokens);
596
+ compiler.hooks.watchRun.tapPromise("unplugin-style-dictionary", rebuildTokens);
343
597
  };
344
598
  return {
345
599
  async buildStart() {
@@ -349,7 +603,7 @@ const unpluginFactory = (options = {}, meta) => {
349
603
  if (resolved.length === 0) return;
350
604
  if (!hostClosed) {
351
605
  const { paths } = await getWatchTargets(resolved);
352
- for (const file of paths) this.addWatchFile(file);
606
+ addWatchFiles(this, paths);
353
607
  }
354
608
  if (isWebpack) return;
355
609
  const reentry = watchRebuild && hasCompiled && hostCommand !== "serve";
@@ -385,8 +639,8 @@ const unpluginFactory = (options = {}, meta) => {
385
639
  try {
386
640
  const resolved = await resolveConfigs();
387
641
  if (resolved.length > 0) {
388
- const { paths } = await getWatchTargets(resolved);
389
- appendChokidarIgnored(config.build.watch, nodeModulesNegations(paths));
642
+ const { paths, patterns } = await getWatchTargets(resolved);
643
+ appendChokidarIgnored(config.build.watch, nodeModulesNegations(patterns, paths));
390
644
  }
391
645
  } catch (err) {
392
646
  log(`Could not read the configuration while preparing the watch list: ${errorMessage(err)}`, "error");
@@ -399,8 +653,8 @@ const unpluginFactory = (options = {}, meta) => {
399
653
  startupResolved = await resolveConfigs();
400
654
  if (startupResolved.length === 0) return;
401
655
  if (config.server.watch === null) return;
402
- const { paths } = await getWatchTargets(startupResolved);
403
- const negations = nodeModulesNegations(paths);
656
+ const { paths, patterns } = await getWatchTargets(startupResolved);
657
+ const negations = nodeModulesNegations(patterns, paths);
404
658
  if (negations.length === 0) return;
405
659
  const existing = config.server.watch?.ignored;
406
660
  config.server.watch = {
@@ -435,7 +689,7 @@ const unpluginFactory = (options = {}, meta) => {
435
689
  const watcher = fs.watch(directory, (_event, filename) => {
436
690
  if (filename === null) return;
437
691
  const changed = path.posix.join(directory, filename);
438
- if (!isWatchedSource(changed, targets.patterns, generatedDestinations)) return;
692
+ if (!isWatchedSource(changed, targets.matcher, generatedDestinations)) return;
439
693
  schedule(path.basename(changed)).catch(() => {});
440
694
  });
441
695
  watcher.unref();
@@ -449,8 +703,10 @@ const unpluginFactory = (options = {}, meta) => {
449
703
  ownWatchers.clear();
450
704
  };
451
705
  refreshServerWatchList = async (rebuilt) => {
706
+ const previous = new Set(targets.paths);
452
707
  targets = await getWatchTargets(rebuilt);
453
- server.watcher.add(targets.paths);
708
+ const adding = targets.paths.filter((file) => !previous.has(file) || !fs.existsSync(file));
709
+ if (adding.length > 0) server.watcher.add(adding);
454
710
  watchNodeModules(targets.paths);
455
711
  };
456
712
  if (errorOverlay) {
@@ -477,23 +733,25 @@ const unpluginFactory = (options = {}, meta) => {
477
733
  };
478
734
  }
479
735
  server.watcher.on("all", (_event, file) => {
480
- if (!isWatchedSource(file, targets.patterns, generatedDestinations)) return;
736
+ if (!isWatchedSource(file, targets.matcher, generatedDestinations)) return;
481
737
  schedule(path.basename(file)).catch(() => {});
482
738
  });
483
739
  }
484
740
  },
485
- async watchChange(id) {
741
+ async watchChange(changed) {
486
742
  adoptHost(this);
487
743
  adoptWatchMode(this);
488
744
  if (hostClosed) return;
489
745
  watchRebuild = true;
490
- if (cachedPatterns && !isWatchedSource(id, cachedPatterns, generatedDestinations)) return;
746
+ const id = registeredSpellingOf(changed, linkedPaths);
747
+ if (cachedMatcher && !isWatchedSource(id, cachedMatcher, generatedDestinations)) return;
491
748
  const resolved = await resolveConfigs();
492
749
  if (resolved.length === 0) return;
493
- const { patterns } = await getWatchTargets(resolved);
494
- if (!isWatchedSource(id, patterns, generatedDestinations)) return;
750
+ const { matcher, patterns } = await deriveWatchPatterns(resolved);
751
+ if (!isWatchedSource(id, matcher, generatedDestinations)) return;
495
752
  if (!isWebpack) await schedule(path.basename(id));
496
- for (const file of await expandPatterns(patterns, log)) this.addWatchFile(file);
753
+ if (refreshServerWatchList) return;
754
+ addWatchFiles(this, await expandPatterns(patterns, log));
497
755
  },
498
756
  webpack: adoptCompiler
499
757
  };