WatchIgnorePlugin.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { groupBy } = require("./util/ArrayHelpers");
  7. /** @import { TimeInfoEntries } from "watchpack" */
  8. /**
  9. * @import {
  10. * WatchIgnorePluginOptions
  11. * } from "../declarations/plugins/WatchIgnorePlugin"
  12. */
  13. /** @import Compiler from "./Compiler" */
  14. /** @import { WatchFileSystem, WatchMethod, Watcher } from "./util/fs" */
  15. const IGNORE_TIME_ENTRY = "ignore";
  16. class IgnoringWatchFileSystem {
  17. /**
  18. * Creates an instance of IgnoringWatchFileSystem.
  19. * @param {WatchFileSystem} wfs original file system
  20. * @param {WatchIgnorePluginOptions["paths"]} paths ignored paths
  21. */
  22. constructor(wfs, paths) {
  23. /** @type {WatchFileSystem} */
  24. this.wfs = wfs;
  25. this.paths = paths;
  26. }
  27. /** @type {WatchMethod} */
  28. watch(files, dirs, missing, startTime, options, callback, callbackUndelayed) {
  29. files = [...files];
  30. dirs = [...dirs];
  31. /**
  32. * Returns true, if path is ignored.
  33. * @param {string} path path to check
  34. * @returns {boolean} true, if path is ignored
  35. */
  36. const ignored = (path) =>
  37. this.paths.some((p) =>
  38. p instanceof RegExp ? p.test(path) : path.indexOf(p) === 0
  39. );
  40. const [ignoredFiles, notIgnoredFiles] = groupBy(
  41. /** @type {string[]} */
  42. (files),
  43. ignored
  44. );
  45. const [ignoredDirs, notIgnoredDirs] = groupBy(
  46. /** @type {string[]} */
  47. (dirs),
  48. ignored
  49. );
  50. const watcher = this.wfs.watch(
  51. notIgnoredFiles,
  52. notIgnoredDirs,
  53. missing,
  54. startTime,
  55. options,
  56. (err, fileTimestamps, dirTimestamps, changedFiles, removedFiles) => {
  57. if (err) return callback(err);
  58. for (const path of ignoredFiles) {
  59. /** @type {TimeInfoEntries} */
  60. (fileTimestamps).set(path, IGNORE_TIME_ENTRY);
  61. }
  62. for (const path of ignoredDirs) {
  63. /** @type {TimeInfoEntries} */
  64. (dirTimestamps).set(path, IGNORE_TIME_ENTRY);
  65. }
  66. callback(
  67. null,
  68. fileTimestamps,
  69. dirTimestamps,
  70. changedFiles,
  71. removedFiles
  72. );
  73. },
  74. callbackUndelayed
  75. );
  76. return {
  77. close: () => watcher.close(),
  78. pause: () => watcher.pause(),
  79. getContextTimeInfoEntries: () => {
  80. const dirTimestamps = watcher.getContextTimeInfoEntries();
  81. for (const path of ignoredDirs) {
  82. dirTimestamps.set(path, IGNORE_TIME_ENTRY);
  83. }
  84. return dirTimestamps;
  85. },
  86. getFileTimeInfoEntries: () => {
  87. const fileTimestamps = watcher.getFileTimeInfoEntries();
  88. for (const path of ignoredFiles) {
  89. fileTimestamps.set(path, IGNORE_TIME_ENTRY);
  90. }
  91. return fileTimestamps;
  92. },
  93. getInfo:
  94. watcher.getInfo &&
  95. (() => {
  96. const info =
  97. /** @type {NonNullable<Watcher["getInfo"]>} */
  98. (watcher.getInfo)();
  99. const { fileTimeInfoEntries, contextTimeInfoEntries } = info;
  100. for (const path of ignoredFiles) {
  101. fileTimeInfoEntries.set(path, IGNORE_TIME_ENTRY);
  102. }
  103. for (const path of ignoredDirs) {
  104. contextTimeInfoEntries.set(path, IGNORE_TIME_ENTRY);
  105. }
  106. return info;
  107. })
  108. };
  109. }
  110. }
  111. const PLUGIN_NAME = "WatchIgnorePlugin";
  112. class WatchIgnorePlugin {
  113. /**
  114. * Creates an instance of WatchIgnorePlugin.
  115. * @param {WatchIgnorePluginOptions} options options
  116. */
  117. constructor(options) {
  118. /** @type {WatchIgnorePluginOptions} */
  119. this.options = options;
  120. }
  121. /**
  122. * Applies the plugin by registering its hooks on the compiler.
  123. * @param {Compiler} compiler the compiler instance
  124. * @returns {void}
  125. */
  126. apply(compiler) {
  127. compiler.hooks.validate.tap(PLUGIN_NAME, () => {
  128. compiler.validate(
  129. () => require("../schemas/plugins/WatchIgnorePlugin.json"),
  130. this.options,
  131. {
  132. name: "Watch Ignore Plugin",
  133. baseDataPath: "options"
  134. },
  135. (options) =>
  136. require("../schemas/plugins/WatchIgnorePlugin.check")(options)
  137. );
  138. });
  139. compiler.hooks.afterEnvironment.tap(PLUGIN_NAME, () => {
  140. compiler.watchFileSystem = new IgnoringWatchFileSystem(
  141. /** @type {WatchFileSystem} */
  142. (compiler.watchFileSystem),
  143. this.options.paths
  144. );
  145. });
  146. }
  147. }
  148. module.exports = WatchIgnorePlugin;