UNPKG

4.91 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const Stats = require("./Stats");
8
9class Watching {
10 constructor(compiler, watchOptions, handler) {
11 this.startTime = null;
12 this.invalid = false;
13 this.handler = handler;
14 this.callbacks = [];
15 this.closed = false;
16 this.suspended = false;
17 if (typeof watchOptions === "number") {
18 this.watchOptions = {
19 aggregateTimeout: watchOptions
20 };
21 } else if (watchOptions && typeof watchOptions === "object") {
22 this.watchOptions = Object.assign({}, watchOptions);
23 } else {
24 this.watchOptions = {};
25 }
26 this.watchOptions.aggregateTimeout =
27 this.watchOptions.aggregateTimeout || 200;
28 this.compiler = compiler;
29 this.running = true;
30 this.compiler.readRecords(err => {
31 if (err) return this._done(err);
32
33 this._go();
34 });
35 }
36
37 _go() {
38 this.startTime = Date.now();
39 this.running = true;
40 this.invalid = false;
41 this.compiler.hooks.watchRun.callAsync(this.compiler, err => {
42 if (err) return this._done(err);
43 const onCompiled = (err, compilation) => {
44 if (err) return this._done(err);
45 if (this.invalid) return this._done();
46
47 if (this.compiler.hooks.shouldEmit.call(compilation) === false) {
48 return this._done(null, compilation);
49 }
50
51 this.compiler.emitAssets(compilation, err => {
52 if (err) return this._done(err);
53 if (this.invalid) return this._done();
54 this.compiler.emitRecords(err => {
55 if (err) return this._done(err);
56
57 if (compilation.hooks.needAdditionalPass.call()) {
58 compilation.needAdditionalPass = true;
59
60 const stats = new Stats(compilation);
61 stats.startTime = this.startTime;
62 stats.endTime = Date.now();
63 this.compiler.hooks.done.callAsync(stats, err => {
64 if (err) return this._done(err);
65
66 this.compiler.hooks.additionalPass.callAsync(err => {
67 if (err) return this._done(err);
68 this.compiler.compile(onCompiled);
69 });
70 });
71 return;
72 }
73 return this._done(null, compilation);
74 });
75 });
76 };
77 this.compiler.compile(onCompiled);
78 });
79 }
80
81 _getStats(compilation) {
82 const stats = new Stats(compilation);
83 stats.startTime = this.startTime;
84 stats.endTime = Date.now();
85 return stats;
86 }
87
88 _done(err, compilation) {
89 this.running = false;
90 if (this.invalid) return this._go();
91
92 const stats = compilation ? this._getStats(compilation) : null;
93 if (err) {
94 this.compiler.hooks.failed.call(err);
95 this.handler(err, stats);
96 return;
97 }
98 this.compiler.hooks.done.callAsync(stats, () => {
99 this.handler(null, stats);
100 if (!this.closed) {
101 this.watch(
102 Array.from(compilation.fileDependencies),
103 Array.from(compilation.contextDependencies),
104 Array.from(compilation.missingDependencies)
105 );
106 }
107 for (const cb of this.callbacks) cb();
108 this.callbacks.length = 0;
109 });
110 }
111
112 watch(files, dirs, missing) {
113 this.pausedWatcher = null;
114 this.watcher = this.compiler.watchFileSystem.watch(
115 files,
116 dirs,
117 missing,
118 this.startTime,
119 this.watchOptions,
120 (
121 err,
122 filesModified,
123 contextModified,
124 missingModified,
125 fileTimestamps,
126 contextTimestamps,
127 removedFiles
128 ) => {
129 this.pausedWatcher = this.watcher;
130 this.watcher = null;
131 if (err) {
132 return this.handler(err);
133 }
134 this.compiler.fileTimestamps = fileTimestamps;
135 this.compiler.contextTimestamps = contextTimestamps;
136 this.compiler.removedFiles = removedFiles;
137 if (!this.suspended) {
138 this._invalidate();
139 }
140 },
141 (fileName, changeTime) => {
142 this.compiler.hooks.invalid.call(fileName, changeTime);
143 }
144 );
145 }
146
147 invalidate(callback) {
148 if (callback) {
149 this.callbacks.push(callback);
150 }
151 if (this.watcher) {
152 this.compiler.fileTimestamps = this.watcher.getFileTimestamps();
153 this.compiler.contextTimestamps = this.watcher.getContextTimestamps();
154 }
155 return this._invalidate();
156 }
157
158 _invalidate() {
159 if (this.watcher) {
160 this.pausedWatcher = this.watcher;
161 this.watcher.pause();
162 this.watcher = null;
163 }
164
165 if (this.running) {
166 this.invalid = true;
167 return false;
168 } else {
169 this._go();
170 }
171 }
172
173 suspend() {
174 this.suspended = true;
175 this.invalid = false;
176 }
177
178 resume() {
179 if (this.suspended) {
180 this.suspended = false;
181 this._invalidate();
182 }
183 }
184
185 close(callback) {
186 const finalCallback = () => {
187 this.compiler.hooks.watchClose.call();
188 this.compiler.running = false;
189 this.compiler.watchMode = false;
190 if (callback !== undefined) callback();
191 };
192
193 this.closed = true;
194 if (this.watcher) {
195 this.watcher.close();
196 this.watcher = null;
197 }
198 if (this.pausedWatcher) {
199 this.pausedWatcher.close();
200 this.pausedWatcher = null;
201 }
202 if (this.running) {
203 this.invalid = true;
204 this._done = finalCallback;
205 } else {
206 finalCallback();
207 }
208 }
209}
210
211module.exports = Watching;