NodeWatchFileSystem.js 5.2 KB

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