NodeWatchFileSystem.js 5.5 KB

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