opusdev/vector-similarity-api
1
1module.exports.watch = watch;2module.exports.resetWatchers = resetWatchers;3 4var debug = require('debug')('nodemon:watch');5var debugRoot = require('debug')('nodemon');6var chokidar = require('chokidar');7var undefsafe = require('undefsafe');8var config = require('../config');9var path = require('path');10var utils = require('../utils');11var bus = utils.bus;12var match = require('./match');13var watchers = [];14var debouncedBus;15 16bus.on('reset', resetWatchers);17 18function resetWatchers() {19 debugRoot('resetting watchers');20 watchers.forEach(function (watcher) {21 watcher.close();22 });23 watchers = [];24}25 26function watch() {27 if (watchers.length) {28 debug('early exit on watch, still watching (%s)', watchers.length);29 return;30 }31 32 var dirs = [].slice.call(config.dirs);33 34 debugRoot('start watch on: %s', dirs.join(', '));35 const rootIgnored = config.options.ignore;36 debugRoot('ignored', rootIgnored);37 38 var watchedFiles = [];39 40 const promise = new Promise(function (resolve) {41 const dotFilePattern = /[/\\]\./;42 var ignored = match.rulesToMonitor(43 [], // not needed44 Array.from(rootIgnored),45 config46 ).map(pattern => pattern.slice(1));47 48 const addDotFile = dirs.filter(dir => dir.match(dotFilePattern));49 50 // don't ignore dotfiles if explicitly watched.51 if (addDotFile.length === 0) {52 ignored.push(dotFilePattern);53 }54 55 var watchOptions = {56 ignorePermissionErrors: true,57 ignored: ignored,58 persistent: true,59 usePolling: config.options.legacyWatch || false,60 interval: config.options.pollingInterval,61 // note to future developer: I've gone back and forth on adding `cwd`62 // to the props and in some cases it fixes bugs but typically it causes63 // bugs elsewhere (since nodemon is used is so many ways). the final64 // decision is to *not* use it at all and work around it65 // cwd: ...66 };67 68 if (utils.isWindows) {69 watchOptions.disableGlobbing = true;70 }71 72 if (utils.isIBMi) {73 watchOptions.usePolling = true;74 }75 76 if (process.env.TEST) {77 watchOptions.useFsEvents = false;78 }79 80 var watcher = chokidar.watch(81 dirs,82 Object.assign({}, watchOptions, config.options.watchOptions || {})83 );84 85 watcher.ready = false;86 87 var total = 0;88 89 watcher.on('change', filterAndRestart);90 watcher.on('unlink', filterAndRestart);91 watcher.on('add', function (file) {92 if (watcher.ready) {93 return filterAndRestart(file);94 }95 96 watchedFiles.push(file);97 bus.emit('watching', file);98 debug('chokidar watching: %s', file);99 });100 watcher.on('ready', function () {101 watchedFiles = Array.from(new Set(watchedFiles)); // ensure no dupes102 total = watchedFiles.length;103 watcher.ready = true;104 resolve(total);105 debugRoot('watch is complete');106 });107 108 watcher.on('error', function (error) {109 if (error.code === 'EINVAL') {110 utils.log.error(111 'Internal watch failed. Likely cause: too many ' +112 'files being watched (perhaps from the root of a drive?\n' +113 'See https://github.com/paulmillr/chokidar/issues/229 for details'114 );115 } else {116 utils.log.error('Internal watch failed: ' + error.message);117 process.exit(1);118 }119 });120 121 watchers.push(watcher);122 });123 124 return promise.catch(e => {125 // this is a core error and it should break nodemon - so I have to break126 // out of a promise using the setTimeout127 setTimeout(() => {128 throw e;129 });130 }).then(function () {131 utils.log.detail(`watching ${watchedFiles.length} file${132 watchedFiles.length === 1 ? '' : 's'}`);133 return watchedFiles;134 });135}136 137function filterAndRestart(files) {138 if (!Array.isArray(files)) {139 files = [files];140 }141 142 if (files.length) {143 var cwd = process.cwd();144 if (this.options && this.options.cwd) {145 cwd = this.options.cwd;146 }147 148 utils.log.detail(149 'files triggering change check: ' +150 files151 .map(file => {152 const res = path.relative(cwd, file);153 return res;154 })155 .join(', ')156 );157 158 // make sure the path is right and drop an empty159 // filenames (sometimes on windows)160 files = files.filter(Boolean).map(file => {161 return path.relative(process.cwd(), path.relative(cwd, file));162 });163 164 if (utils.isWindows) {165 // ensure the drive letter is in uppercase (c:\foo -> C:\foo)166 files = files.map(f => {167 if (f.indexOf(':') === -1) { return f; }168 return f[0].toUpperCase() + f.slice(1);169 });170 }171 172 173 debug('filterAndRestart on', files);174 175 var matched = match(176 files,177 config.options.monitor,178 undefsafe(config, 'options.execOptions.ext')179 );180 181 debug('matched?', JSON.stringify(matched));182 183 // if there's no matches, then test to see if the changed file is the184 // running script, if so, let's allow a restart185 if (config.options.execOptions && config.options.execOptions.script) {186 const script = path.resolve(config.options.execOptions.script);187 if (matched.result.length === 0 && script) {188 const length = script.length;189 files.find(file => {190 if (file.substr(-length, length) === script) {191 matched = {192 result: [file],193 total: 1,194 };195 return true;196 }197 });198 }199 }200 201 utils.log.detail(202 'changes after filters (before/after): ' +203 [files.length, matched.result.length].join('/')204 );205 206 // reset the last check so we're only looking at recently modified files207 config.lastStarted = Date.now();208 209 if (matched.result.length) {210 if (config.options.delay > 0) {211 utils.log.detail('delaying restart for ' + config.options.delay + 'ms');212 if (debouncedBus === undefined) {213 debouncedBus = debounce(restartBus, config.options.delay);214 }215 debouncedBus(matched);216 } else {217 return restartBus(matched);218 }219 }220 }221}222 223function restartBus(matched) {224 utils.log.status('restarting due to changes...');225 matched.result.map(file => {226 utils.log.detail(path.relative(process.cwd(), file));227 });228 229 if (config.options.verbose) {230 utils.log._log('');231 }232 233 bus.emit('restart', matched.result);234}235 236function debounce(fn, delay) {237 var timer = null;238 return function () {239 const context = this;240 const args = arguments;241 clearTimeout(timer);242 timer = setTimeout(() =>fn.apply(context, args), delay);243 };244}245 