WatchIgnorePlugin.js 3.9 KB

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