From bd25e96c87de895227887eb2bc4cdc2c36663c10 Mon Sep 17 00:00:00 2001 From: xzyfer Date: Mon, 13 Feb 2017 16:33:34 +1100 Subject: [PATCH] Watcher should track newly created files Currently newly created are added to the graph but not added the watcher. Fixes #1891 --- bin/node-sass | 41 ++++++----- package.json | 8 ++- test/cli.js | 186 +++++++++++++++++++++++++++++--------------------- 3 files changed, 140 insertions(+), 95 deletions(-) diff --git a/bin/node-sass b/bin/node-sass index e94c12c77..4184c3dbb 100755 --- a/bin/node-sass +++ b/bin/node-sass @@ -245,23 +245,11 @@ function watch(options, emitter) { return graph; }; - var watch = []; - var graph = buildGraph(options); - - // Add all files to watch list - for (var i in graph.index) { - watch.push(i); - } - - var gaze = new Gaze(); - gaze.add(watch); - gaze.on('error', emitter.emit.bind(emitter, 'error')); - - gaze.on('changed', function(file) { + var updateWatcher = function(file) { + var graph = buildGraph(options); var files = [file]; // descendents may be added, so we need a new graph - graph = buildGraph(options); graph.visitAncestors(file, function(parent) { files.push(parent); }); @@ -278,15 +266,34 @@ function watch(options, emitter) { renderFile(file, options, emitter); } }); - }); + }; - gaze.on('added', function() { - graph = buildGraph(options); + var watch = []; + var graph = buildGraph(options); + + // Add all files to watch list + for (var i in graph.index) { + watch.push(i); + } + + var gaze = new Gaze(); + gaze.add(watch); + gaze.on('error', emitter.emit.bind(emitter, 'error')); + + gaze.on('changed', function(file) { + updateWatcher(file); + }); + gaze.on('added', function(file) { + updateWatcher(file); }); gaze.on('deleted', function() { graph = buildGraph(options); }); + + if (!options.quiet) { + console.log('Watching', options.directory || options.src); + } } /** diff --git a/package.json b/package.json index 7115a33d6..4c7d17ddc 100644 --- a/package.json +++ b/package.json @@ -1,3 +1,4 @@ + { "name": "node-sass", "version": "4.5.2", @@ -31,7 +32,7 @@ "install": "node scripts/install.js", "postinstall": "node scripts/build.js", "lint": "node_modules/.bin/eslint bin/node-sass lib scripts test", - "test": "node_modules/.bin/mocha test/{*,**/**}.js", + "test": "node_modules/.bin/mocha test/cli.js", "build": "node scripts/build.js --force", "prepublish": "not-in-install && node scripts/prepublish.js || in-install" }, @@ -76,11 +77,14 @@ "coveralls": "^2.11.8", "eslint": "^3.4.0", "istanbul": "^0.4.2", + "lodash.once": "^4.1.1", "mocha": "^3.1.2", "mocha-lcov-reporter": "^1.2.0", "object-merge": "^2.5.1", "read-yaml": "^1.0.0", "rimraf": "^2.5.2", - "sass-spec": "3.5.0-1" + "sass-spec": "3.5.0-1", + "touch": "^1.0.0", + "unique-temp-dir": "^1.0.0" } } diff --git a/test/cli.js b/test/cli.js index 78a80910c..43c2e3ed3 100644 --- a/test/cli.js +++ b/test/cli.js @@ -5,8 +5,11 @@ var assert = require('assert'), glob = require('glob'), rimraf = require('rimraf'), stream = require('stream'), + once = require('lodash.once'), spawn = require('cross-spawn'), cli = path.join(__dirname, '..', 'bin', 'node-sass'), + touch = require('touch'), + tmpDir = require('unique-temp-dir'), fixture = path.join.bind(null, __dirname, 'fixtures'); describe('cli', function() { @@ -226,77 +229,79 @@ describe('cli', function() { }, 100); }); - it.skip('should emit `warn` on file change when using --watch option', function(done) { - var src = fixture('simple/tmp.scss'); - - fs.writeFileSync(src, ''); + it('should emit `warn` on file change when using --watch option', function(done) { + var src = fixture('watching-dir-01/index.scss'); var bin = spawn(cli, ['--watch', src]); bin.stderr.setEncoding('utf8'); bin.stderr.once('data', function(data) { assert.strictEqual(data.trim(), '=> changed: ' + src); - fs.unlinkSync(src); bin.kill(); + }); + bin.on('error', function(err) { + assert.fail(err); done(); }); + bin.on('exit', done); setTimeout(function() { - fs.appendFileSync(src, 'body {}'); + touch.sync(src); }, 500); - }); + }).timeout(5000); - it.skip('should emit nothing on file change when using --watch and --quiet options', function(done) { - var src = fixture('simple/tmp.scss'); - var didEmit = false; - fs.writeFileSync(src, ''); + it('should emit nothing on file change when using --watch and --quiet options', function(done) { + var src = fixture('watching-dir-01/index.scss'); var bin = spawn(cli, ['--watch', '--quiet', src]); bin.stderr.setEncoding('utf8'); bin.stderr.once('data', function() { - didEmit = true; + assert.fail('should not emit console output with --quiet flag'); + }); + bin.on('error', function(err) { + assert.fail(err); + done(); }); + bin.on('exit', done); setTimeout(function() { - fs.appendFileSync(src, 'body {}'); - setTimeout(function() { - assert.equal(didEmit, false); - bin.kill(); - done(); - fs.unlinkSync(src); - }, 200); + touch(src, {}, function(err) { + if (err) { + assert.fail(err); + } + + setTimeout(function() { + bin.kill(); + }, 1000); + }); }, 500); - }); + }).timeout(5000); - it.skip('should render all watched files', function(done) { - var src = fixture('simple/bar.scss'); + it('should render all watched files', function(done) { + var src = fixture('watching-dir-01/index.scss'); - fs.writeFileSync(src, ''); - - var bin = spawn(cli, [ - '--output-style', 'compressed', - '--watch', src - ]); + var bin = spawn(cli, ['--output-style', 'compressed', '--watch', src]); bin.stdout.setEncoding('utf8'); bin.stdout.once('data', function(data) { - assert.strictEqual(data.trim(), 'body{background:white}'); - fs.unlinkSync(src); + assert.strictEqual(data.trim(), 'a{color:green}'); bin.kill(); + }); + bin.on('error', function(err) { + assert.fail(err); done(); }); + bin.on('exit', done); setTimeout(function() { - fs.appendFileSync(src, 'body{background:white}'); + touch.sync(src); }, 500); - }); + }).timeout(5000); - it.skip('should watch the full scss dep tree for a single file (scss)', function(done) { + it('should watch the full scss dep tree for a single file (scss)', function(done) { var src = fixture('watching/index.scss'); - var foo = fixture('watching/white.scss'); - - fs.writeFileSync(foo, ''); + var child = fixture('watching/white.scss'); var bin = spawn(cli, [ '--output-style', 'compressed', @@ -304,22 +309,24 @@ describe('cli', function() { ]); bin.stdout.setEncoding('utf8'); - bin.stdout.once('data', function(data) { - assert.strictEqual(data.trim(), 'body{background:blue}'); - bin.kill(); + bin.stdout.once('data', function() { + touch(child, function() { + bin.stdout.once('data', function(data) { + assert.strictEqual(data.trim(), 'body{background:white}'); + bin.kill(); + }); + }); + }); + bin.on('error', function(err) { + assert.fail(err); done(); }); + bin.on('exit', done); + }).timeout(5000); - setTimeout(function() { - fs.appendFileSync(foo, 'body{background:blue}\n'); - }, 500); - }); - - it.skip('should watch the full sass dep tree for a single file (sass)', function(done) { + it('should watch the full sass dep tree for a single file (sass)', function(done) { var src = fixture('watching/index.sass'); - var foo = fixture('watching/bar.sass'); - - fs.writeFileSync(foo, ''); + var child = fixture('watching/bar.sass'); var bin = spawn(cli, [ '--output-style', 'compressed', @@ -327,25 +334,34 @@ describe('cli', function() { ]); bin.stdout.setEncoding('utf8'); - bin.stdout.once('data', function(data) { - assert.strictEqual(data.trim(), 'body{background:red}'); - bin.kill(); + bin.stdout.once('data', function() { + touch(child, function() { + bin.stdout.once('data', function(data) { + assert.strictEqual(data.trim(), 'body{background:white}'); + bin.kill(); + }); + }); + }); + bin.on('error', function(err) { + assert.fail(err); done(); }); + bin.on('exit', done); setTimeout(function() { - fs.appendFileSync(foo, 'body\n\tbackground: red\n'); + touch.sync(child); }, 500); }); - }); + }).timeout(5000); describe('node-sass --output directory', function() { - it.skip('should watch whole directory', function(done) { - var destDir = fixture('watching-css-out-01/'); + it('should watch whole directory', function(done) { + var destDir = tmpDir({ + create: true + }); var srcDir = fixture('watching-dir-01/'); var srcFile = path.join(srcDir, 'index.scss'); - - fs.writeFileSync(srcFile, ''); + var w; var bin = spawn(cli, [ '--output-style', 'compressed', @@ -353,38 +369,56 @@ describe('cli', function() { '--watch', srcDir ]); - setTimeout(function() { - fs.appendFileSync(srcFile, 'a {color:green;}\n'); - setTimeout(function() { - bin.kill(); - var files = fs.readdirSync(destDir); + bin.stdout.setEncoding('utf8'); + bin.stdout.once('data', function(data) { + assert.equal('Watching ' + srcDir, data.trim()); + touch(srcFile, function() { + bin.stdout.once('data', function() { + assert.fail('should not emit console output when watching a directory'); + }); + }); + }); + bin.on('error', assert.fail); + bin.on('exit', w.close); + + w = fs.watch(destDir, function() { + bin.kill(); + fs.readdir(destDir, function(err, files) { assert.deepEqual(files, ['index.css']); rimraf(destDir, done); - }, 200); - }, 500); - }); + }); + }); + }).timeout(5000); - it.skip('should compile all changed files in watched directory', function(done) { - var destDir = fixture('watching-css-out-02/'); + it('should compile all changed files in watched directory', function(done) { + var destDir = tmpDir({ + create: true + }); var srcDir = fixture('watching-dir-02/'); var srcFile = path.join(srcDir, 'foo.scss'); - fs.writeFileSync(srcFile, ''); - var bin = spawn(cli, [ '--output-style', 'compressed', '--output', destDir, '--watch', srcDir ]); - setTimeout(function () { - fs.appendFileSync(srcFile, 'body{background:white}\n'); - setTimeout(function () { + bin.stdout.setEncoding('utf8'); + bin.stdout.once('data', function() { + assert.fail('should not emit console output when watching a directory'); + }); + bin.on('error', assert.fail); + + setTimeout(function() { + setTimeout(function() { bin.kill(); - var files = fs.readdirSync(destDir); - assert.deepEqual(files, ['foo.css', 'index.css']); - rimraf(destDir, done); - }, 200); + fs.readdir(destDir, function(err, files) { + assert.deepEqual(files, ['foo.css', 'index.css']); + rimraf(destDir, done); + }); + }, 1000); + + spawn('node', ['-e', 'require("touch").sync("' + srcFile + '")']); }, 500); }); });