forked from open-cli-tools/chokidar-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
executable file
·232 lines (206 loc) · 7.1 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/bin/env node
'use strict';
const throttle = require('lodash.throttle');
const debounce = require('lodash.debounce');
const chokidar = require('chokidar');
const yargs = require('yargs');
const { version: chokidarVersion } = require('chokidar/package.json');
const { version } = require('./package.json');
const utils = require('./utils');
const EVENT_DESCRIPTIONS = {
add: 'File added',
addDir: 'Directory added',
unlink: 'File removed',
unlinkDir: 'Directory removed',
change: 'File changed'
};
const defaultOpts = {
debounce: 400,
throttle: 0,
followSymlinks: false,
ignore: null,
polling: false,
pollInterval: 100,
pollIntervalBinary: 300,
verbose: false,
silent: false,
initial: false,
command: null
};
const VERSION = `chokidar-cli: ${version}\nchokidar: ${chokidarVersion}`;
const {argv} = yargs
.usage(
'Usage: chokidar <pattern> [<pattern>...] [options]\n\n' +
'<pattern>:\n' +
'Glob pattern to specify files to be watched.\n' +
'Multiple patterns can be watched by separating patterns with spaces.\n' +
'To prevent shell globbing, write pattern inside quotes.\n' +
'Guide to globs: https://github.com/isaacs/node-glob#glob-primer\n'
)
.example('chokidar "**/*.js" -c "npm run build-js"', 'build when any .js file changes')
.example('chokidar "**/*.js" "**/*.less"', 'output changes of .js and .less files')
.demand(1)
.option('c', {
alias: 'command',
describe: 'Command to run after each change. ' +
'Needs to be surrounded with quotes when command contains ' +
'spaces. Instances of `{path}` or `{event}` within the ' +
'command will be replaced by the corresponding values from ' +
'the chokidar event.'
})
.option('d', {
alias: 'debounce',
default: defaultOpts.debounce,
describe: 'Debounce timeout in ms for executing command',
type: 'number'
})
.option('t', {
alias: 'throttle',
default: defaultOpts.throttle,
describe: 'Throttle timeout in ms for executing command',
type: 'number'
})
.option('s', {
alias: 'follow-symlinks',
default: defaultOpts.followSymlinks,
describe: 'When not set, only the symlinks themselves will be watched ' +
'for changes instead of following the link references and ' +
'bubbling events through the links path',
type: 'boolean'
})
.option('i', {
alias: 'ignore',
describe: 'Pattern for files which should be ignored. ' +
'Needs to be surrounded with quotes to prevent shell globbing. ' +
'The whole relative or absolute path is tested, not just filename. ' +
'Supports glob patters or regexes using format: /yourmatch/i'
})
.option('initial', {
describe: 'When set, command is initially run once',
default: defaultOpts.initial,
type: 'boolean'
})
.option('p', {
alias: 'polling',
describe: 'Whether to use fs.watchFile(backed by polling) instead of ' +
'fs.watch. This might lead to high CPU utilization. ' +
'It is typically necessary to set this to true to ' +
'successfully watch files over a network, and it may be ' +
'necessary to successfully watch files in other ' +
'non-standard situations',
default: defaultOpts.polling,
type: 'boolean'
})
.option('poll-interval', {
describe: 'Interval of file system polling. Effective when --polling ' +
'is set',
default: defaultOpts.pollInterval,
type: 'number'
})
.option('poll-interval-binary', {
describe: 'Interval of file system polling for binary files. ' +
'Effective when --polling is set',
default: defaultOpts.pollIntervalBinary,
type: 'number'
})
.option('verbose', {
describe: 'When set, output is more verbose and human readable.',
default: defaultOpts.verbose,
type: 'boolean'
})
.option('silent', {
describe: 'When set, internal messages of chokidar-cli won\'t be written.',
default: defaultOpts.silent,
type: 'boolean'
})
.help('h')
.alias('h', 'help')
.alias('v', 'version')
.version(VERSION);
function main() {
const userOpts = getUserOpts(argv);
const opts = Object.assign(defaultOpts, userOpts);
startWatching(opts);
}
function getUserOpts(argv) {
argv.patterns = argv._;
return argv;
}
// Estimates spent working hours based on commit dates
function startWatching(opts) {
const chokidarOpts = createChokidarOpts(opts);
const watcher = chokidar.watch(opts.patterns, chokidarOpts);
let throttledRun = run;
if (opts.throttle > 0) {
throttledRun = throttle(run, opts.throttle);
}
let debouncedRun = throttledRun;
if (opts.debounce > 0) {
debouncedRun = debounce(throttledRun, opts.debounce);
}
watcher.on('all', (event, path) => {
const description = `${EVENT_DESCRIPTIONS[event]}:`;
if (opts.verbose) {
console.error(description, path);
} else if (!opts.silent) {
console.log(`${event}:${path}`);
}
// XXX: commands might be still run concurrently
if (opts.command) {
debouncedRun(
opts.command
.replace(/\{path\}/ig, path)
.replace(/\{event\}/ig, event)
);
}
});
watcher.on('error', error => {
console.error('Error:', error);
console.error(error.stack);
});
watcher.once('ready', () => {
const list = opts.patterns.join('", "');
if (!opts.silent) {
console.error('Watching', `"${list}" ..`);
}
});
}
function createChokidarOpts(opts) {
// Transform e.g. regex ignores to real regex objects
opts.ignore = _resolveIgnoreOpt(opts.ignore);
const chokidarOpts = {
followSymlinks: opts.followSymlinks,
usePolling: opts.polling,
interval: opts.pollInterval,
binaryInterval: opts.pollIntervalBinary,
ignoreInitial: !opts.initial
};
if (opts.ignore) {
chokidarOpts.ignored = opts.ignore;
}
return chokidarOpts;
}
// Takes string or array of strings
function _resolveIgnoreOpt(ignoreOpt) {
if (!ignoreOpt) {
return ignoreOpt;
}
const ignores = Array.isArray(ignoreOpt) ? ignoreOpt : [ignoreOpt];
return ignores.map(ignore => {
const isRegex = ignore[0] === '/' && ignore[ignore.length - 1] === '/';
if (isRegex) {
// Convert user input to regex object
const match = ignore.match(/^\/(.*)\/(.*?)$/);
return new RegExp(match[1], match[2]);
}
return ignore;
});
}
function run(cmd) {
return utils.run(cmd)
.catch(error => {
console.error('Error when executing', cmd);
console.error(error.stack);
});
}
main();