Permalink
Browse files

Rewrite in js. No more postinstall compilation.

  • Loading branch information...
1 parent ff75085 commit 790094a75ea8b02fc96383fc478019480ce74ce3 @paulmillr committed May 4, 2012
Showing with 205 additions and 166 deletions.
  1. +2 −0 .gitignore
  2. +5 −2 CHANGELOG.md
  3. +198 −0 lib/index.js
  4. +0 −164 src/index.coffee
View
@@ -38,3 +38,5 @@ publish
*.sublime-project
*.sublime-workspace
+
+node_modules/
View
@@ -1,6 +1,9 @@
-## Chokidar 0.1.1 (April 26, 2012)
+# Chokidar 0.2.0 (May 4, 2012)
+* Rewritten in js.
+
+# Chokidar 0.1.1 (April 26, 2012)
* Changed api to `chokidar.watch()`.
* Fixed compilation on windows.
-## Chokidar 0.1.0 (April 20, 2012)
+# Chokidar 0.1.0 (April 20, 2012)
* Initial release.
View
@@ -0,0 +1,198 @@
+'use strict';
+
+var EventEmitter = require('events').EventEmitter;
+var fs = require('fs');
+var sysPath = require('path');
+
+
+// Watches files & directories for changes.
+//
+// Emitted events: `add`, `change`, `unlink`, `error`.
+//
+// Examples
+//
+// var watcher = new FSWatcher()
+// .add(directories)
+// .on('add', function(path) {console.log('File', path, 'was added');})
+// .on('change', function(path) {console.log('File', path, 'was changed');})
+// .on('unlink', function(path) {console.log('File', path, 'was removed');})
+//
+function FSWatcher(options) {
+ this.options = options = options != null ? options : {};
+ this._handle = this._handle.bind(this);
+ this.watched = {};
+ this.watchers = [];
+ if (this.options.persistent == null) {
+ this.options.persistent = false;
+ }
+ this._ignored = (function() {
+ switch (toString.call(options.ignored)) {
+ case '[object RegExp]':
+ return function(string) {
+ return options.ignored.test(string);
+ };
+ case '[object Function]':
+ return options.ignored;
+ default:
+ return function() {
+ return false;
+ };
+ }
+ })();
+}
+
+// Inherit from EventEmitter.
+FSWatcher.prototype = Object.create(EventEmitter.prototype);
+
+FSWatcher.prototype._getWatchedDir = function(directory) {
+ if (this.watched[directory] == null) this.watched[directory] = [];
+ return this.watched[directory];
+};
+
+// Private: Watch file for changes with fs.watchFile or fs.watch.
+//
+// item - string, path to file or directory.
+// callback - function that will be executed on fs change.
+//
+// Returns nothing.
+FSWatcher.prototype._watch = function(item, callback) {
+ var _this = this;
+ var options, watcher;
+ if (callback == null) callback = function() {};
+ var parent = this._getWatchedDir(sysPath.dirname(item));
+ var basename = sysPath.basename(item);
+ // Prevent memory leaks.
+ if (parent.indexOf(basename) >= 0) return;
+ parent.push(basename);
+ if (process.platform === 'win32') {
+ watcher = fs.watch(item, {
+ persistent: this.options.persistent
+ }, callback);
+ this.watchers.push(watcher);
+ } else {
+ options = {
+ persistent: this.options.persistent,
+ interval: 100
+ };
+ fs.watchFile(item, options, function(curr, prev) {
+ if (curr.mtime.getTime() !== prev.mtime.getTime()) {
+ callback(item);
+ }
+ });
+ }
+};
+
+// Private: Emit `change` event once and watch file to emit it in the future
+// once the file is changed.
+//
+// file - string, fs path.
+//
+// Returns nothing.
+FSWatcher.prototype._handleFile = function(file) {
+ var _this = this;
+ this._watch(file, function(file) {
+ _this.emit('change', file);
+ });
+ this.emit('add', file);
+};
+
+// Private: Read directory to add / remove files from `@watched` list
+// and re-read it on change.
+//
+// directory - string, fs path.
+//
+// Returns nothing.
+FSWatcher.prototype._handleDir = function(directory) {
+ var _this = this;
+ var read = function(directory) {
+ fs.readdir(directory, function(error, current) {
+ var previous;
+ if (error != null) return _this.emit('error', error);
+ if (!current) return;
+ previous = _this._getWatchedDir(directory);
+
+ // Files that absent in current directory snapshot
+ // but present in previous emit `remove` event
+ // and are removed from @watched[directory].
+ previous
+ .filter(function(file) {
+ return current.indexOf(file) < 0;
+ })
+ .forEach(function(file, index) {
+ var path = sysPath.join(directory, file);
+ previous.splice(index, 1);
+ _this.emit('unlink', path);
+ });
+
+ // Files that present in current directory snapshot
+ // but absent in previous are added to watch list and
+ // emit `add` event.
+ current
+ .filter(function(file) {
+ return previous.indexOf(file) < 0;
+ })
+ .forEach(function(file) {
+ _this._handle(sysPath.join(directory, file));
+ });
+ });
+ };
+ read(directory);
+ this._watch(directory, read);
+};
+
+// Private: Handle added file or directory.
+// Delegates call to _handleFile / _handleDir after checks.
+//
+// item - string, path to file or directory.
+//
+// Returns nothing.
+FSWatcher.prototype._handle = function(item) {
+ var _this = this;
+ // Don't handle invalid files, dotfiles etc.
+ if (this._ignored(item)) return;
+ // Get the canonicalized absolute pathname.
+ fs.realpath(item, function(error, path) {
+ if (error != null) return _this.emit('error', error);
+ // Get file info, check is it file, directory or something else.
+ fs.stat(item, function(error, stats) {
+ if (error != null) return _this.emit('error', error);
+ if (stats.isFile()) _this._handleFile(item);
+ if (stats.isDirectory()) _this._handleDir(item);
+ });
+ });
+};
+
+// Public: Adds directories / files for tracking.
+//
+// * files - array of strings (file paths).
+//
+// Examples
+//
+// add ['app', 'vendor']
+//
+// Returns an instance of FSWatcher for chaning.
+FSWatcher.prototype.add = function(files) {
+ if (!Array.isArray(files)) files = [files];
+ files.forEach(this._handle);
+ return this;
+};
+
+// Public: Remove all listeners from watched files.
+// Returns an instance of FSWatcher for chaning.
+FSWatcher.prototype.close = function() {
+ var _this = this;
+ this.watchers.forEach(function(watcher) {
+ watcher.close();
+ });
+ Object.keys(this.watched).forEach(function(directory) {
+ _this.watched[directory].forEach(function(file) {
+ fs.unwatchFile(sysPath.join(directory, file));
+ });
+ });
+ this.watched = {};
+ return this;
+};
+
+exports.watch = function(files, options) {
+ return new FSWatcher(files, options).add(files);
+};
View
@@ -1,164 +0,0 @@
-{EventEmitter} = require 'events'
-fs = require 'fs'
-sysPath = require 'path'
-
-# Watches files & directories for changes.
-#
-# Emitted events: `add`, `change`, `unlink`, `error`.
-#
-# Examples
-#
-# watcher = (new FSWatcher)
-# .add(directories)
-# .on('change', (path) -> console.log 'File', path, 'was added / changed')
-# .on('unlink', (path) -> console.log 'File', path, 'was removed')
-#
-class FSWatcher extends EventEmitter
- # Files that wouldn't be watched.
-
- constructor: (files, @options = {}) ->
- @watched = {}
- @watchers = []
- @options.persistent ?= no
- @_ignored = switch toString.call(options.ignored)
- when '[object RegExp]'
- (string) -> options.ignored.test(string)
- when '[object Function]'
- options.ignored
- else
- (-> no)
- @add(files)
-
- _getWatchedDir: (directory) ->
- @watched[directory] ?= []
-
- # Private: Watch file for changes with fs.watchFile or fs.watch.
- #
- # item - string, path to file or directory.
- # callback - function that will be executed on fs change.
- #
- # Returns nothing.
- _watch: (item, callback) ->
- parent = @_getWatchedDir sysPath.dirname item
- basename = sysPath.basename item
- # Prevent memory leaks.
- return if basename in parent
- parent.push basename
- # @watchers.push fs.watch item, persistent: yes, =>
- # callback? item
- if process.platform is 'win32'
- @watchers.push fs.watch item, persistent: @options.persistent, =>
- callback? item
- else
- options = persistent: @options.persistent, interval: 100
- fs.watchFile item, options, (curr, prev) =>
- callback item if curr.mtime.getTime() isnt prev.mtime.getTime()
-
- # Private: Emit `change` event once and watch file to emit it in the future
- # once the file is changed.
- #
- # file - string, fs path.
- #
- # Returns nothing.
- _handleFile: (file) ->
- @_watch file, (file) =>
- @emit 'change', file
- @emit 'add', file
-
- # Private: Read directory to add / remove files from `@watched` list
- # and re-read it on change.
- #
- # directory - string, fs path.
- #
- # Returns nothing.
- _handleDir: (directory) ->
- read = (directory) =>
- fs.readdir directory, (error, current) =>
- return @emit 'error', error if error?
- return unless current
- previous = @_getWatchedDir directory
-
- # Files that absent in current directory snapshot
- # but present in previous emit `remove` event
- # and are removed from @watched[directory].
- previous
- .filter (file) =>
- file not in current
- .forEach (file, index) =>
- path = sysPath.join directory, file
- previous.splice(index, 1)
- @emit 'unlink', path
-
- # Files that present in current directory snapshot
- # but absent in previous are added to watch list and
- # emit `add` event.
- current
- .filter (file) ->
- file not in previous
- .forEach (file) =>
- @_handle sysPath.join directory, file
- read directory
- @_watch directory, read
-
- # Private: Handle added file or directory.
- # Delegates call to _handleFile / _handleDir after checks.
- #
- # item - string, path to file or directory.
- #
- # Returns nothing.
- _handle: (item) =>
- # Don't handle invalid files, dotfiles etc.
- return if @_ignored item
- # Get the canonicalized absolute pathname.
- fs.realpath item, (error, path) =>
- return @emit 'error', error if error?
- # Get file info, check is it file, directory or something else.
- fs.stat item, (error, stats) =>
- return @emit 'error', error if error?
- @_handleFile item if stats.isFile()
- @_handleDir item if stats.isDirectory()
-
- # Public: Adds directories / files for tracking.
- #
- # * files - array of strings (file paths).
- #
- # Examples
- #
- # add ['app', 'vendor']
- #
- # Returns an instance of FSWatcher for chaning.
- add: (files) ->
- files = [files] unless Array.isArray(files)
- files.forEach @_handle
- this
-
- # Public: Emit event. Delegates to superclass & also emits 'all' event.
- #
- # * event - event name.
- # * args... - event arguments.
- #
- # Returns an instance of FSWatcher for chaning.
- emit: (event, args...) ->
- super 'all', event, args...
- super
- this
-
- # Public: Call EventEmitter's event listening function.
- # Returns an instance of FSWatcher for chaning.
- on: ->
- super
- this
-
- # Public: Remove all listeners from watched files.
- # Returns an instance of FSWatcher for chaning.
- close: ->
- @watchers.forEach (watcher) =>
- watcher.close()
- Object.keys(@watched).forEach (directory) =>
- @watched[directory].forEach (file) =>
- fs.unwatchFile sysPath.join directory, file
- @watched = {}
- this
-
-exports.watch = (files, options) ->
- new FSWatcher(files, options)

0 comments on commit 790094a

Please sign in to comment.