From 9fe5ca6ff9fcd726d3bec6c71a06227470cbb6a8 Mon Sep 17 00:00:00 2001 From: Seaoak Date: Thu, 24 Oct 2019 20:26:12 +0900 Subject: [PATCH] Disable fs.watch() for dirs/files specified by "ignore" option --- lib/box/index.js | 18 +++++++++++ test/scripts/box/box.js | 72 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/lib/box/index.js b/lib/box/index.js index 4629adcd75..adc7c94ce0 100644 --- a/lib/box/index.js +++ b/lib/box/index.js @@ -30,6 +30,10 @@ function Box(ctx, base, options) { this.Cache = ctx.model('Cache'); this.File = this._createFileClass(); this.ignore = ctx.config.ignore; + if (ctx.config.ignore) { + const targets = Array.isArray(ctx.config.ignore) ? ctx.config.ignore : [ctx.config.ignore]; + this.options.ignored = (this.options.ignored || []).concat(targets.map(s => toRegExp(ctx, s)).filter(x => x)); + } } require('util').inherits(Box, EventEmitter); @@ -52,6 +56,20 @@ function getHash(path) { }); } +function toRegExp(ctx, arg) { + if (!arg) return null; + if (typeof arg !== 'string') { + ctx.log.warn('A value of "ignore:" section in "_config.yml" is not invalid (not a string)'); + return null; + } + const result = micromatch.makeRe(arg); + if (!result) { + ctx.log.warn('A value of "ignore:" section in "_config.yml" can not be converted to RegExp:' + arg); + return null; + } + return result; +} + Box.prototype._createFileClass = function() { const ctx = this.context; diff --git a/test/scripts/box/box.js b/test/scripts/box/box.js index efaf61d5b9..3873e92262 100644 --- a/test/scripts/box/box.js +++ b/test/scripts/box/box.js @@ -437,6 +437,78 @@ describe('Box', () => { }); }); + it('watch() - update with simple "ignore" option', () => { + const box = newBox('test', {ignore: '**/ignore_me'}); + const path1 = 'a.txt'; + const path2 = 'b.txt'; + const src1 = pathFn.join(box.base, path1); + const src2 = pathFn.join(box.base, 'ignore_me', path2); + const cacheId1 = 'test/' + path1; + const cacheId2 = 'test/ignore_me/' + path2; + const Cache = box.Cache; + const processor = sinon.spy(); + + box.addProcessor(processor); + + let file; + return Promise.all([ + fs.writeFile(src1, 'a'), + Cache.insert({_id: cacheId1}) + ]).then(() => Promise.all([ + fs.writeFile(src2, 'b'), + Cache.insert({_id: cacheId2}) + ])).then(() => box.watch()).then(() => fs.appendFile(src1, 'aaa')).delay(500).then(() => { + file = processor.lastCall.args[0]; + + file.source.should.eql(src1); + file.path.should.eql(path1); + file.type.should.eql('update'); + file.params.should.eql({}); + }).then(() => fs.appendFile(src2, 'bbb')).delay(500).then(() => { + const file2 = processor.lastCall.args[0]; + file2.should.eql(file); // not changed + }).finally(() => { + box.unwatch(); + return fs.rmdir(box.base); + }); + }); + + it('watch() - update with complex "ignore" option', () => { + const box = newBox('test', {ignore: ['**/ignore_me', '**/ignore_me_too']}); + const path1 = 'a.txt'; + const path2 = 'b.txt'; + const src1 = pathFn.join(box.base, path1); + const src2 = pathFn.join(box.base, 'ignore_me', path2); + const cacheId1 = 'test/' + path1; + const cacheId2 = 'test/ignore_me/' + path2; + const Cache = box.Cache; + const processor = sinon.spy(); + + box.addProcessor(processor); + + let file; + return Promise.all([ + fs.writeFile(src1, 'a'), + Cache.insert({_id: cacheId1}) + ]).then(() => Promise.all([ + fs.writeFile(src2, 'b'), + Cache.insert({_id: cacheId2}) + ])).then(() => box.watch()).then(() => fs.appendFile(src1, 'aaa')).delay(500).then(() => { + file = processor.lastCall.args[0]; + + file.source.should.eql(src1); + file.path.should.eql(path1); + file.type.should.eql('update'); + file.params.should.eql({}); + }).then(() => fs.appendFile(src2, 'bbb')).delay(500).then(() => { + const file2 = processor.lastCall.args[0]; + file2.should.eql(file); // not changed + }).finally(() => { + box.unwatch(); + return fs.rmdir(box.base); + }); + }); + it('watch() - watcher has started', () => { const box = newBox();