1 | "use strict";
|
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
4 | };
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | const tree_kill_1 = __importDefault(require("tree-kill"));
|
7 | const path_1 = require("path");
|
8 | const cross_spawn_1 = require("cross-spawn");
|
9 | const chokidar_1 = __importDefault(require("chokidar"));
|
10 | const deque_1 = require("@blakeembrey/deque");
|
11 | const template_1 = require("@blakeembrey/template");
|
12 | const ECHO_JS_PATH = path_1.resolve(__dirname, "echo.js");
|
13 | const ECHO_CMD = `${quote(process.execPath)} ${quote(ECHO_JS_PATH)}`;
|
14 |
|
15 |
|
16 |
|
17 | class Job {
|
18 | constructor(log, command, outpipe) {
|
19 | this.log = log;
|
20 | this.command = command;
|
21 | this.outpipe = outpipe;
|
22 | }
|
23 | start(cwd, stdin, stdout, stderr, env, onexit) {
|
24 | var _a, _b;
|
25 | if (this.outpipe) {
|
26 | const stdio = [null, stdout, stderr];
|
27 | this.log(`executing outpipe "${this.outpipe}"`);
|
28 | this.childOutpipe = cross_spawn_1.spawn(this.outpipe, { cwd, env, stdio, shell: true });
|
29 | this.childOutpipe.on("exit", (code, signal) => {
|
30 | this.log(`outpipe ${exitMessage(code, signal)}`);
|
31 | this.childOutpipe = undefined;
|
32 | if (!this.childCommand)
|
33 | return onexit();
|
34 | });
|
35 | }
|
36 | if (this.command.length) {
|
37 | const stdio = [
|
38 | stdin,
|
39 |
|
40 | this.childOutpipe ? this.childOutpipe.stdin : stdout,
|
41 | stderr,
|
42 | ];
|
43 | this.log(`executing command "${this.command.join(" ")}"`);
|
44 | this.childCommand = cross_spawn_1.spawn(this.command[0], this.command.slice(1), {
|
45 | cwd,
|
46 | env,
|
47 | stdio,
|
48 | });
|
49 | this.childCommand.on("exit", (code, signal) => {
|
50 | var _a, _b;
|
51 | this.log(`command ${exitMessage(code, signal)}`);
|
52 | this.childCommand = undefined;
|
53 | if (!this.childOutpipe)
|
54 | return onexit();
|
55 | return (_b = (_a = this.childOutpipe) === null || _a === void 0 ? void 0 : _a.stdin) === null || _b === void 0 ? void 0 : _b.end();
|
56 | });
|
57 | }
|
58 | else {
|
59 |
|
60 | (_b = (_a = this.childOutpipe) === null || _a === void 0 ? void 0 : _a.stdin) === null || _b === void 0 ? void 0 : _b.end();
|
61 | }
|
62 | }
|
63 | kill(killSignal) {
|
64 | if (this.childOutpipe) {
|
65 | this.log(`killing outpipe ${this.childOutpipe.pid}`);
|
66 | tree_kill_1.default(this.childOutpipe.pid, killSignal);
|
67 | }
|
68 | if (this.childCommand) {
|
69 | this.log(`killing command ${this.childCommand.pid}`);
|
70 | tree_kill_1.default(this.childCommand.pid, killSignal);
|
71 | }
|
72 | }
|
73 | }
|
74 |
|
75 |
|
76 |
|
77 | function onchange(options) {
|
78 | const { matches } = options;
|
79 | const onReady = options.onReady || (() => undefined);
|
80 | const initial = !!options.initial;
|
81 | const kill = !!options.kill;
|
82 | const cwd = options.cwd ? path_1.resolve(options.cwd) : process.cwd();
|
83 | const stdin = options.stdin || process.stdin;
|
84 | const stdout = options.stdout || process.stdout;
|
85 | const stderr = options.stderr || process.stderr;
|
86 | const env = options.env || process.env;
|
87 | const delay = Math.max(options.delay || 0, 0);
|
88 | const jobs = Math.max(options.jobs || 0, 1);
|
89 | const killSignal = options.killSignal || "SIGTERM";
|
90 | const command = options.command
|
91 | ? options.command.map((arg) => template_1.template(arg))
|
92 | : [];
|
93 | const outpipe = options.outpipe
|
94 | ? outpipeTemplate(options.outpipe)
|
95 | : undefined;
|
96 | const filter = options.filter || [];
|
97 | const running = new Set();
|
98 | const queue = new deque_1.Deque();
|
99 |
|
100 | const log = options.verbose
|
101 | ? function log(message) {
|
102 | stdout.write(`onchange: ${message}\n`);
|
103 | }
|
104 | : function () { };
|
105 |
|
106 | if (command.length === 0 && !outpipe) {
|
107 | throw new TypeError('Expected "command" and/or "outpipe" to be specified');
|
108 | }
|
109 | const ignored = options.exclude || [];
|
110 | const ignoreInitial = options.add !== true;
|
111 | const usePolling = options.poll !== undefined;
|
112 | const interval = options.poll !== undefined ? options.poll : undefined;
|
113 | const awaitWriteFinish = options.awaitWriteFinish
|
114 | ? { stabilityThreshold: options.awaitWriteFinish }
|
115 | : undefined;
|
116 |
|
117 | if (options.defaultExclude !== false) {
|
118 | ignored.push("**/node_modules/**", "**/.git/**");
|
119 | }
|
120 |
|
121 | const watcher = chokidar_1.default.watch(matches, {
|
122 | cwd,
|
123 | ignored,
|
124 | ignoreInitial,
|
125 | usePolling,
|
126 | interval,
|
127 | awaitWriteFinish,
|
128 | });
|
129 | |
130 |
|
131 |
|
132 | function dequeue() {
|
133 |
|
134 | if (queue.size === 0)
|
135 | return;
|
136 |
|
137 | if (running.size >= jobs)
|
138 | return;
|
139 |
|
140 | const job = queue.popLeft();
|
141 |
|
142 | running.add(job);
|
143 |
|
144 | job.start(cwd, stdin, stdout, stderr, env, () => {
|
145 | running.delete(job);
|
146 | if (delay > 0)
|
147 | return setTimeout(dequeue, delay);
|
148 | return dequeue();
|
149 | });
|
150 | }
|
151 | |
152 |
|
153 |
|
154 | function enqueue(event, file) {
|
155 | const fileExt = path_1.extname(file);
|
156 | const state = {
|
157 | event,
|
158 | changed: file,
|
159 | file,
|
160 | fileExt,
|
161 | fileBase: path_1.basename(file),
|
162 | fileBaseNoExt: path_1.basename(file, fileExt),
|
163 | fileDir: path_1.dirname(file),
|
164 | };
|
165 |
|
166 | if (kill) {
|
167 | queue.clear();
|
168 | running.forEach((child) => child.kill(killSignal));
|
169 | }
|
170 |
|
171 | log(`${file}: ${event}`);
|
172 |
|
173 | queue.push(new Job(log, command.map((arg) => arg(state)), outpipe === null || outpipe === void 0 ? void 0 : outpipe(state)));
|
174 |
|
175 | return dequeue();
|
176 | }
|
177 |
|
178 | if (initial)
|
179 | enqueue("", "");
|
180 |
|
181 | watcher.on("all", (event, file) => {
|
182 | if (filter.length && filter.indexOf(event) === -1)
|
183 | return;
|
184 | return enqueue(event, file);
|
185 | });
|
186 |
|
187 | watcher.on("ready", () => {
|
188 | log(`watcher ready`);
|
189 |
|
190 | return onReady();
|
191 | });
|
192 | watcher.on("error", (error) => log(`watcher error: ${error}`));
|
193 |
|
194 | return () => watcher.close();
|
195 | }
|
196 | exports.onchange = onchange;
|
197 |
|
198 | function outpipeTemplate(str) {
|
199 | var value = str.trim();
|
200 | if (value.charAt(0) === "|" || value.charAt(0) === ">") {
|
201 | return template_1.template(`${ECHO_CMD} ${value}`);
|
202 | }
|
203 | return template_1.template(value);
|
204 | }
|
205 |
|
206 | function exitMessage(code, signal) {
|
207 | return code === null ? `exited with ${signal}` : `completed with ${code}`;
|
208 | }
|
209 |
|
210 |
|
211 |
|
212 | function quote(str) {
|
213 | return `"${str.replace(/["\\$`!]/g, "\\$&")}"`;
|
214 | }
|
215 |
|
\ | No newline at end of file |