WatchIgnorePlugin.js 4.1 KB

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