NodeWatchFileSystem.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const util = require("util");
  7. const Watchpack = require("watchpack");
  8. /** @typedef {import("../../declarations/WebpackOptions").WatchOptions} WatchOptions */
  9. /** @typedef {import("../FileSystemInfo").FileSystemInfoEntry} FileSystemInfoEntry */
  10. /** @typedef {import("../util/fs").WatchFileSystem} WatchFileSystem */
  11. /** @typedef {import("../util/fs").WatchMethod} WatchMethod */
  12. /** @typedef {import("../util/fs").Watcher} Watcher */
  13. class NodeWatchFileSystem {
  14. constructor(inputFileSystem) {
  15. this.inputFileSystem = inputFileSystem;
  16. this.watcherOptions = {
  17. aggregateTimeout: 0
  18. };
  19. this.watcher = new Watchpack(this.watcherOptions);
  20. }
  21. /**
  22. * @param {Iterable<string>} files watched files
  23. * @param {Iterable<string>} directories watched directories
  24. * @param {Iterable<string>} missing watched exitance entries
  25. * @param {number} startTime timestamp of start time
  26. * @param {WatchOptions} options options object
  27. * @param {function(Error=, Map<string, FileSystemInfoEntry>, Map<string, FileSystemInfoEntry>, Set<string>, Set<string>): void} callback aggregated callback
  28. * @param {function(string, number): void} callbackUndelayed callback when the first change was detected
  29. * @returns {Watcher} a watcher
  30. */
  31. watch(
  32. files,
  33. directories,
  34. missing,
  35. startTime,
  36. options,
  37. callback,
  38. callbackUndelayed
  39. ) {
  40. if (!files || typeof files[Symbol.iterator] !== "function") {
  41. throw new Error("Invalid arguments: 'files'");
  42. }
  43. if (!directories || typeof directories[Symbol.iterator] !== "function") {
  44. throw new Error("Invalid arguments: 'directories'");
  45. }
  46. if (!missing || typeof missing[Symbol.iterator] !== "function") {
  47. throw new Error("Invalid arguments: 'missing'");
  48. }
  49. if (typeof callback !== "function") {
  50. throw new Error("Invalid arguments: 'callback'");
  51. }
  52. if (typeof startTime !== "number" && startTime) {
  53. throw new Error("Invalid arguments: 'startTime'");
  54. }
  55. if (typeof options !== "object") {
  56. throw new Error("Invalid arguments: 'options'");
  57. }
  58. if (typeof callbackUndelayed !== "function" && callbackUndelayed) {
  59. throw new Error("Invalid arguments: 'callbackUndelayed'");
  60. }
  61. const oldWatcher = this.watcher;
  62. this.watcher = new Watchpack(options);
  63. if (callbackUndelayed) {
  64. this.watcher.once("change", callbackUndelayed);
  65. }
  66. const fetchTimeInfo = () => {
  67. const fileTimeInfoEntries = new Map();
  68. const contextTimeInfoEntries = new Map();
  69. if (this.watcher) {
  70. this.watcher.collectTimeInfoEntries(
  71. fileTimeInfoEntries,
  72. contextTimeInfoEntries
  73. );
  74. }
  75. return { fileTimeInfoEntries, contextTimeInfoEntries };
  76. };
  77. this.watcher.once("aggregated", (changes, removals) => {
  78. // pause emitting events (avoids clearing aggregated changes and removals on timeout)
  79. this.watcher.pause();
  80. if (this.inputFileSystem && this.inputFileSystem.purge) {
  81. const fs = this.inputFileSystem;
  82. for (const item of changes) {
  83. fs.purge(item);
  84. }
  85. for (const item of removals) {
  86. fs.purge(item);
  87. }
  88. }
  89. const { fileTimeInfoEntries, contextTimeInfoEntries } = fetchTimeInfo();
  90. callback(
  91. null,
  92. fileTimeInfoEntries,
  93. contextTimeInfoEntries,
  94. changes,
  95. removals
  96. );
  97. });
  98. this.watcher.watch({ files, directories, missing, startTime });
  99. if (oldWatcher) {
  100. oldWatcher.close();
  101. }
  102. return {
  103. close: () => {
  104. if (this.watcher) {
  105. this.watcher.close();
  106. this.watcher = null;
  107. }
  108. },
  109. pause: () => {
  110. if (this.watcher) {
  111. this.watcher.pause();
  112. }
  113. },
  114. getAggregatedRemovals: util.deprecate(
  115. () => {
  116. const items = this.watcher && this.watcher.aggregatedRemovals;
  117. if (items && this.inputFileSystem && this.inputFileSystem.purge) {
  118. const fs = this.inputFileSystem;
  119. for (const item of items) {
  120. fs.purge(item);
  121. }
  122. }
  123. return items;
  124. },
  125. "Watcher.getAggregatedRemovals is deprecated in favor of Watcher.getInfo since that's more performant.",
  126. "DEP_WEBPACK_WATCHER_GET_AGGREGATED_REMOVALS"
  127. ),
  128. getAggregatedChanges: util.deprecate(
  129. () => {
  130. const items = this.watcher && this.watcher.aggregatedChanges;
  131. if (items && this.inputFileSystem && this.inputFileSystem.purge) {
  132. const fs = this.inputFileSystem;
  133. for (const item of items) {
  134. fs.purge(item);
  135. }
  136. }
  137. return items;
  138. },
  139. "Watcher.getAggregatedChanges is deprecated in favor of Watcher.getInfo since that's more performant.",
  140. "DEP_WEBPACK_WATCHER_GET_AGGREGATED_CHANGES"
  141. ),
  142. getFileTimeInfoEntries: util.deprecate(
  143. () => {
  144. return fetchTimeInfo().fileTimeInfoEntries;
  145. },
  146. "Watcher.getFileTimeInfoEntries is deprecated in favor of Watcher.getInfo since that's more performant.",
  147. "DEP_WEBPACK_WATCHER_FILE_TIME_INFO_ENTRIES"
  148. ),
  149. getContextTimeInfoEntries: util.deprecate(
  150. () => {
  151. return fetchTimeInfo().contextTimeInfoEntries;
  152. },
  153. "Watcher.getContextTimeInfoEntries is deprecated in favor of Watcher.getInfo since that's more performant.",
  154. "DEP_WEBPACK_WATCHER_CONTEXT_TIME_INFO_ENTRIES"
  155. ),
  156. getInfo: () => {
  157. const removals = this.watcher && this.watcher.aggregatedRemovals;
  158. const changes = this.watcher && this.watcher.aggregatedChanges;
  159. if (this.inputFileSystem && this.inputFileSystem.purge) {
  160. const fs = this.inputFileSystem;
  161. if (removals) {
  162. for (const item of removals) {
  163. fs.purge(item);
  164. }
  165. }
  166. if (changes) {
  167. for (const item of changes) {
  168. fs.purge(item);
  169. }
  170. }
  171. }
  172. const { fileTimeInfoEntries, contextTimeInfoEntries } = fetchTimeInfo();
  173. return {
  174. changes,
  175. removals,
  176. fileTimeInfoEntries,
  177. contextTimeInfoEntries
  178. };
  179. }
  180. };
  181. }
  182. }
  183. module.exports = NodeWatchFileSystem;