batman-rails-flo 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +29 -0
  3. data/Rakefile +2 -0
  4. data/lib/batman-rails-flo.rb +5 -0
  5. data/lib/batman_rails_flo/railtie.rb +9 -0
  6. data/lib/batman_rails_flo/tasks.rake +14 -0
  7. data/lib/batman_rails_flo/version.rb +3 -0
  8. data/vendor/assets/javascripts/batman_rails_flo/flo_server.js +67 -0
  9. data/vendor/assets/javascripts/batman_rails_flo/live_reload.js.coffee +129 -0
  10. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/CNAME +1 -0
  11. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/CONTRIBUTING.md +9 -0
  12. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/LICENSE +22 -0
  13. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/README +50 -0
  14. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/bin/cake +7 -0
  15. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/bin/coffee +7 -0
  16. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/browser.js +134 -0
  17. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/cake.js +112 -0
  18. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/coffee-script.js +335 -0
  19. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/command.js +569 -0
  20. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/grammar.js +631 -0
  21. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/helpers.js +252 -0
  22. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/index.js +11 -0
  23. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/lexer.js +926 -0
  24. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/nodes.js +3158 -0
  25. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/optparse.js +139 -0
  26. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/parser.js +724 -0
  27. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/register.js +66 -0
  28. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/repl.js +163 -0
  29. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/rewriter.js +475 -0
  30. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/scope.js +146 -0
  31. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/lib/coffee-script/sourcemap.js +161 -0
  32. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/LICENSE +21 -0
  33. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/examples/pow.js +6 -0
  34. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/index.js +82 -0
  35. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/package.json +34 -0
  36. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/readme.markdown +63 -0
  37. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/chmod.js +38 -0
  38. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/clobber.js +37 -0
  39. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/mkdirp.js +28 -0
  40. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/perm.js +32 -0
  41. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/perm_sync.js +39 -0
  42. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/race.js +41 -0
  43. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/rel.js +32 -0
  44. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/return.js +25 -0
  45. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/return_sync.js +24 -0
  46. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/root.js +18 -0
  47. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/sync.js +32 -0
  48. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/umask.js +28 -0
  49. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/node_modules/mkdirp/test/umask_sync.js +32 -0
  50. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/package.json +50 -0
  51. data/vendor/assets/javascripts/batman_rails_flo/node_modules/coffee-script/register.js +1 -0
  52. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/LICENSE +30 -0
  53. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/PATENTS +23 -0
  54. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/README.md +124 -0
  55. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/bin/flo +26 -0
  56. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/configure.html +57 -0
  57. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/configure.js +205 -0
  58. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/configure/style.css +334 -0
  59. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/connection.js +235 -0
  60. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/devtools.html +12 -0
  61. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/index.js +323 -0
  62. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/logger.js +42 -0
  63. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/logo.png +0 -0
  64. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/manifest.json +13 -0
  65. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/client/session.js +305 -0
  66. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/index.js +10 -0
  67. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/lib/flo.js +226 -0
  68. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/lib/server.js +94 -0
  69. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/README.md +66 -0
  70. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/index.js +437 -0
  71. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/LICENSE +23 -0
  72. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/README.md +218 -0
  73. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/minimatch.js +1055 -0
  74. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS +14 -0
  75. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/LICENSE +23 -0
  76. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/README.md +97 -0
  77. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js +252 -0
  78. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/package.json +33 -0
  79. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/test/basic.js +369 -0
  80. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/test/foreach.js +52 -0
  81. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js +50 -0
  82. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/LICENSE +27 -0
  83. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/README.md +53 -0
  84. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/bench.js +283 -0
  85. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/package.json +42 -0
  86. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/sigmund.js +39 -0
  87. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/node_modules/sigmund/test/basic.js +24 -0
  88. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/package.json +40 -0
  89. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/basic.js +399 -0
  90. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/brace-expand.js +33 -0
  91. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/caching.js +14 -0
  92. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/defaults.js +274 -0
  93. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/minimatch/test/extglob-ending-with-state-char.js +8 -0
  94. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/lib/walker.js +111 -0
  95. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/license +13 -0
  96. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/lib/makeerror.js +87 -0
  97. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/node_modules/tmpl/lib/tmpl.js +17 -0
  98. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/node_modules/tmpl/package.json +31 -0
  99. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/node_modules/tmpl/readme.md +10 -0
  100. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/package.json +34 -0
  101. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/node_modules/makeerror/readme.md +77 -0
  102. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/package.json +36 -0
  103. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/walker/readme.md +52 -0
  104. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/LICENSE +55 -0
  105. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/main.js +128 -0
  106. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/package.json +34 -0
  107. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/readme.mkd +72 -0
  108. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/d/d/t +0 -0
  109. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/d/t +0 -0
  110. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_monitor.js +31 -0
  111. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_monitorRootDirectory.js +28 -0
  112. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/node_modules/watch/test/test_watchTree.js +20 -0
  113. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/package.json +45 -0
  114. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/sane/test/test.js +243 -0
  115. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/CHANGELOG.md +81 -0
  116. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/LICENSE +177 -0
  117. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/Makefile +11 -0
  118. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/README.md +245 -0
  119. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/binding.gyp +14 -0
  120. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Makefile +355 -0
  121. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/linker.lock +0 -0
  122. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/obj.target/validation/src/validation.o +0 -0
  123. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/obj.target/xor/src/xor.o +0 -0
  124. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/validation.node +0 -0
  125. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/Release/xor.node +0 -0
  126. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/binding.Makefile +6 -0
  127. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/config.gypi +115 -0
  128. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/gyp-mac-tool +512 -0
  129. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/validation.target.mk +154 -0
  130. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/build/xor.target.mk +154 -0
  131. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/index.js +1 -0
  132. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/install.js +31 -0
  133. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Constants.js +23 -0
  134. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Deprecation.js +38 -0
  135. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Validation.fallback.js +12 -0
  136. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/Validation.js +18 -0
  137. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketClient.js +359 -0
  138. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketConnection.js +717 -0
  139. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketFrame.js +282 -0
  140. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRequest.js +478 -0
  141. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRouter.js +154 -0
  142. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketRouterRequest.js +59 -0
  143. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/WebSocketServer.js +216 -0
  144. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/utils.js +7 -0
  145. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/websocket.js +11 -0
  146. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/xor.fallback.js +13 -0
  147. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/lib/xor.js +18 -0
  148. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/package.json +43 -0
  149. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/src/validation.cc +144 -0
  150. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/src/xor.cpp +86 -0
  151. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/FastBufferList.js +192 -0
  152. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/node-ctype/LICENSE +18 -0
  153. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/node_modules/websocket/vendor/node-ctype/ctio-faster.js +1126 -0
  154. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/package.json +25 -0
  155. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/browser_websocket.js +63 -0
  156. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/connection_test.js +57 -0
  157. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/logger_mock.js +15 -0
  158. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/client/session_test.js +194 -0
  159. data/vendor/assets/javascripts/batman_rails_flo/node_modules/fb-flo/test/server/flo_test.js +211 -0
  160. data/vendor/assets/javascripts/batman_rails_flo/package.json +15 -0
  161. data/vendor/assets/javascripts/batman_rails_flo/reload_event_handler.js.coffee +56 -0
  162. metadata +232 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright (c) 2014, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ module.exports = require('./lib/flo');
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Copyright (c) 2014, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ var fs = require('fs');
13
+ var path = require('path');
14
+ var sane = require('sane');
15
+ var assert = require('assert');
16
+ var Server = require('./server');
17
+ var EventEmitter = require('events').EventEmitter;
18
+
19
+ module.exports = flo;
20
+
21
+ /**
22
+ * Top-level API for flo. Defaults params and instantiates `Flo`.
23
+ *
24
+ * @param {string} dir
25
+ * @param {object} options
26
+ * @param {function} callback
27
+ * @return {Flo}
28
+ * @public
29
+ */
30
+
31
+ function flo(dir, options, callback) {
32
+ if (typeof options === 'function') {
33
+ callback = options;
34
+ options = null;
35
+ }
36
+
37
+ dir = path.resolve(dir);
38
+
39
+ options = options || {};
40
+ options = {
41
+ port: options.port || 8888,
42
+ host: options.host || 'localhost',
43
+ verbose: options.verbose || false,
44
+ glob: options.glob || [],
45
+ useFilePolling: options.useFilePolling || false,
46
+ pollingInterval: options.pollingInterval
47
+ };
48
+
49
+ callback = callback || noBuildCallback(dir);
50
+
51
+ return new Flo(dir, options, callback);
52
+ }
53
+
54
+ /**
55
+ * Time before we emit the ready event.
56
+ */
57
+
58
+ var DELAY = 200;
59
+
60
+ /**
61
+ * Starts the server and the watcher and handles the piping between both and the
62
+ * resolver callback.
63
+ *
64
+ * @param {string} dir
65
+ * @param {object} options
66
+ * @param {function} callback
67
+ * @class Flo
68
+ * @private
69
+ */
70
+
71
+ function Flo(dir, options, callback) {
72
+ this.log = logger(options.verbose, 'Flo');
73
+ this.dir = dir;
74
+ this.realpathdir = fs.realpathSync(dir);
75
+ this.resolver = callback;
76
+ this.server = new Server({
77
+ port: options.port,
78
+ host: options.host,
79
+ log: logger(options.verbose, 'Server')
80
+ });
81
+
82
+ this.watcher = new sane.Watcher(dir, {
83
+ glob: options.glob,
84
+ poll: options.useFilePolling,
85
+ pollingInterval: options.pollingInterval
86
+ });
87
+ this.watcher.on('change', this.onFileChange.bind(this));
88
+ this.watcher.on(
89
+ 'ready',
90
+ this.emit.bind(this, 'ready')
91
+ );
92
+ }
93
+
94
+ Flo.prototype.__proto__ = EventEmitter.prototype;
95
+
96
+ /**
97
+ * Handles file changes.
98
+ *
99
+ * @param {string} filepath
100
+ * @private
101
+ */
102
+
103
+ Flo.prototype.onFileChange = function(filepath) {
104
+ this.log('File changed', filepath);
105
+ var server = this.server;
106
+ this.resolver(filepath, function(resource) {
107
+ resource = normalizeResource(resource);
108
+ server.broadcast(resource);
109
+ });
110
+ };
111
+
112
+ /**
113
+ * Closes the server and the watcher.
114
+ *
115
+ * @public
116
+ */
117
+
118
+ Flo.prototype.close = function() {
119
+ this.log('Shutting down flo');
120
+ this.watcher.close();
121
+ this.server.close();
122
+ };
123
+
124
+ /**
125
+ * Creates a logger for a given module.
126
+ *
127
+ * @param {boolean} verbose
128
+ * @param {string} moduleName
129
+ * @private
130
+ */
131
+
132
+ function logger(verbose, moduleName) {
133
+ var slice = [].slice;
134
+ return function() {
135
+ var args = slice.call(arguments);
136
+ args[0] = '[' + moduleName + '] ' + args[0];
137
+ if (verbose) {
138
+ console.log.apply(console, args);
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Check if an object is of type RegExp.
145
+ *
146
+ * @param {*} o
147
+ * @return {boolean}
148
+ * @private
149
+ */
150
+
151
+ function isRegExp(o) {
152
+ return o && typeof o === 'object' &&
153
+ Object.prototype.toString.call(o) === '[object RegExp]';
154
+ }
155
+
156
+ /**
157
+ * Given a resource, enforce type checks, default params, and prepare for
158
+ * serialization.
159
+ *
160
+ * @param {*} o
161
+ * @return {boolean}
162
+ * @private
163
+ */
164
+
165
+ function normalizeResource(resource) {
166
+ if (resource.reload) {
167
+ return resource;
168
+ }
169
+
170
+ assert(resource.resourceURL, 'expecting resourceURL');
171
+ assert(resource.contents != null, 'expecting contents');
172
+
173
+ var ret = {};
174
+
175
+ ret.match = resource.match || 'indexOf';
176
+ ret.contents = resource.contents.toString();
177
+ ret.resourceURL = resource.resourceURL;
178
+
179
+ if (path.sep === '\\') {
180
+ ret.resourceURL = ret.resourceURL.replace(/\\/g, '/');
181
+ }
182
+
183
+ assert.ok(
184
+ isRegExp(ret.match) ||
185
+ ['indexOf', 'equal'].indexOf(ret.match) > -1,
186
+ 'resource.match must be of type function, or regexp, ' +
187
+ 'or a string either "indexOf" or "equal'
188
+ );
189
+
190
+ if (isRegExp(ret.match)) {
191
+ var r = ret.match;
192
+ ret.match = {
193
+ type: 'regexp',
194
+ source: r.source,
195
+ global: r.global,
196
+ multiline: r.multiline,
197
+ ignoreCase: r.ignoreCase
198
+ };
199
+ }
200
+
201
+ return ret;
202
+ }
203
+
204
+ /**
205
+ * Default resolver callback that will simply read the file and pass back the
206
+ * filename relative to the watched dir as the url.
207
+ *
208
+ * @param {string} dir
209
+ * @return {function}
210
+ * @private
211
+ */
212
+
213
+ function noBuildCallback(dir) {
214
+ return function(filepath, callback) {
215
+ fs.readFile(path.join(dir, filepath), function(err, data) {
216
+ // Todo better error handling.
217
+ if (err) {
218
+ throw err;
219
+ }
220
+ callback({
221
+ resourceURL: filepath,
222
+ contents: data.toString()
223
+ });
224
+ });
225
+ };
226
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Copyright (c) 2014, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ var http = require('http');
13
+ var WSS = require('websocket').server;
14
+
15
+ module.exports = Server;
16
+
17
+ /**
18
+ * Starts an http server with the given options and attaches a websocket server
19
+ * to it.
20
+ *
21
+ * @class Server
22
+ * @param {object} options
23
+ */
24
+
25
+ function Server(options) {
26
+ this.log = options.log || function() {};
27
+ this.httpServer = http.createServer(function(req, res) {
28
+ res.writeHead(404);
29
+ res.end();
30
+ });
31
+ this.httpServer.listen(options.port);
32
+ this.wsServer = new WSS({
33
+ httpServer: this.httpServer,
34
+ autoAcceptConnections: false
35
+ });
36
+ this.wsServer.on('request', this.onRequest.bind(this));
37
+ this.connections = [];
38
+ }
39
+
40
+ /**
41
+ * Request handler.
42
+ *
43
+ * @param {object} req
44
+ * @private
45
+ */
46
+
47
+ Server.prototype.onRequest = function(req) {
48
+ this.log('Client connected', req.socket.address());
49
+ var ws = req.accept();
50
+ this.connections.push(ws);
51
+ ws.on('close', this.onClose.bind(this, ws));
52
+ };
53
+
54
+ /**
55
+ * Websocket connection close handler.
56
+ *
57
+ * @param {object} ws
58
+ * @private
59
+ */
60
+
61
+ Server.prototype.onClose = function(ws) {
62
+ this.log('Client disconnected', ws.remoteAddress);
63
+ if (this.connections) {
64
+ this.connections.splice(this.connections.indexOf(ws), 1);
65
+ }
66
+ };
67
+
68
+ /**
69
+ * Message handler.
70
+ *
71
+ * @param {object} msg
72
+ * @public
73
+ */
74
+
75
+ Server.prototype.broadcast = function(msg) {
76
+ this.log('Broadcasting', msg);
77
+ msg = JSON.stringify(msg);
78
+ this.connections.forEach(function(ws) {
79
+ ws.send(msg);
80
+ });
81
+ };
82
+
83
+ /**
84
+ * Close the server.
85
+ *
86
+ * @public
87
+ */
88
+
89
+ Server.prototype.close = function() {
90
+ this.log('Shutting down WebSocket server');
91
+ this.connections = null;
92
+ this.wsServer.shutDown();
93
+ this.httpServer.close();
94
+ };
@@ -0,0 +1,66 @@
1
+ sane
2
+ ----
3
+
4
+ I've been driven to insanity by node filesystem watcher wrappers.
5
+ Sane aims to be fast, small, and reliable file system watcher. It does that by:
6
+
7
+ * Always use fs.watch (unless polling is forced) and sensibly workaround the various issues with it
8
+ * Sane is all JavaScript, no native components
9
+ * Stay away from polling because it's very slow and cpu intensive
10
+ * Support polling for environments like Vagrant shared directory where there are no native filesystem events
11
+
12
+ ## Install
13
+
14
+ Requires node >= v0.10.0.
15
+
16
+ ```
17
+ $ npm install sane
18
+ ```
19
+
20
+ ## API
21
+
22
+ ### sane(dir, globs, options)
23
+
24
+ Watches a directory and all it's descendant directorys for changes, deletions, and additions on files and directories.
25
+ Shortcut for `new sane.Watcher(dir, {glob: globs, ..options})`.
26
+
27
+ ```js
28
+ var watcher = sane('path/to/dir', ['**/*.js, '**/*.css']);
29
+ watcher.on('ready', function () { console.log('ready') });
30
+ watcher.on('change', function (filepath) { console.log('file changed', filepath); });
31
+ watcher.on('add', function (filepath) { console.log('file added', filepath); });
32
+ watcher.on('delete', function (filepath) { console.log('file deleted', filepath); });
33
+ // close
34
+ watcher.close();
35
+ ```
36
+
37
+ For `options` see `sane.Watcher`.
38
+
39
+ ### sane.Watcher(dir, options)
40
+
41
+ options:
42
+
43
+ * `persistent`: boolean indicating that the process shouldn't die while we're watching files.
44
+ * `glob`: a single string glob pattern or an array of them.
45
+ * `poll`: puts the watcher in polling mode. Under the hood that means `fs.watchFile`.
46
+ * `interval`: indicates how often the files should be polled. (passed to `fs.watchFile`)
47
+
48
+ For the glob pattern documentation, see [minimatch](https://github.com/isaacs/minimatch).
49
+
50
+ ### sane.Watcher#close
51
+
52
+ Stops watching.
53
+
54
+ ### sane.Watcher events
55
+
56
+ Emits the following events:
57
+
58
+ All events are passed the file/dir path relative to the root directory
59
+ * `ready` when the program is ready to detect events in the directory
60
+ * `change` when a file changes
61
+ * `add` when a file or directory has been added
62
+ * `delete` when a file or directory has been deleted
63
+
64
+ ## License
65
+
66
+ MIT
@@ -0,0 +1,437 @@
1
+ var fs = require('fs');
2
+ var path = require('path');
3
+ var watch = require('watch');
4
+ var walker = require('walker');
5
+ var platform = require('os').platform();
6
+ var minimatch = require('minimatch');
7
+ var EventEmitter = require('events').EventEmitter;
8
+
9
+ module.exports = sane;
10
+
11
+ /**
12
+ * Sugar for creating a watcher.
13
+ *
14
+ * @param {String} dir
15
+ * @param {Array<String>} glob
16
+ * @param {Object} opts
17
+ * @return {Watcher}
18
+ * @public
19
+ */
20
+
21
+ function sane(dir, glob, opts) {
22
+ opts = opts || {};
23
+ opts.glob = glob;
24
+ return new Watcher(dir, opts);
25
+ }
26
+
27
+ /**
28
+ * Export `Watcher` class.
29
+ */
30
+
31
+ sane.Watcher = Watcher;
32
+
33
+ /**
34
+ * Watches `dir`.
35
+ *
36
+ * @class Watcher
37
+ * @param String dir
38
+ * @param {Object} opts
39
+ * @public
40
+ */
41
+
42
+ function Watcher(dir, opts) {
43
+ opts = opts || {};
44
+ this.persistent = opts.persistent != null
45
+ ? opts.persistent
46
+ : true;
47
+ this.globs = opts.glob || [];
48
+ if (!Array.isArray(this.globs)) this.globs = [this.globs];
49
+ this.watched = Object.create(null);
50
+ this.changeTimers = Object.create(null);
51
+ this.dirRegistery = Object.create(null);
52
+ this.root = path.resolve(dir);
53
+ this.watchdir = this.watchdir.bind(this);
54
+ this.register = this.register.bind(this);
55
+ this.stopWatching = this.stopWatching.bind(this);
56
+ this.filter = this.filter.bind(this);
57
+
58
+ if (opts.poll) {
59
+ this.polling = true;
60
+ watch.createMonitor(
61
+ dir,
62
+ { interval: opts.interval || DEFAULT_DELAY , filter: this.filter },
63
+ this.initPoller.bind(this)
64
+ );
65
+ } else {
66
+ this.watchdir(dir);
67
+ recReaddir(
68
+ dir,
69
+ this.watchdir,
70
+ this.register,
71
+ this.emit.bind(this, 'ready')
72
+ );
73
+ }
74
+ }
75
+
76
+ Watcher.prototype.__proto__ = EventEmitter.prototype;
77
+
78
+ /**
79
+ * Checks a file relative path against the globs array.
80
+ *
81
+ * @param {string} relativePath
82
+ * @return {boolean}
83
+ * @private
84
+ */
85
+
86
+ Watcher.prototype.isFileIncluded = function(relativePath) {
87
+ var globs = this.globs;
88
+ var matched;
89
+ if (globs.length) {
90
+ for (var i = 0; i < globs.length; i++) {
91
+ if (minimatch(relativePath, globs[i])) {
92
+ matched = true;
93
+ break;
94
+ }
95
+ }
96
+ } else {
97
+ matched = true;
98
+ }
99
+ return matched;
100
+ };
101
+
102
+ /**
103
+ * Register files that matches our globs to know what to type of event to
104
+ * emit in the future.
105
+ *
106
+ * Registery looks like the following:
107
+ *
108
+ * dirRegister => Map {
109
+ * dirpath => Map {
110
+ * filename => true
111
+ * }
112
+ * }
113
+ *
114
+ * @param {string} filepath
115
+ * @return {boolean} whether or not we have registered the file.
116
+ * @private
117
+ */
118
+
119
+ Watcher.prototype.register = function(filepath) {
120
+ var relativePath = path.relative(this.root, filepath);
121
+ if (!this.isFileIncluded(relativePath)) {
122
+ return false;
123
+ }
124
+
125
+ var dir = path.dirname(filepath);
126
+ if (!this.dirRegistery[dir]) {
127
+ this.dirRegistery[dir] = Object.create(null);
128
+ }
129
+
130
+ var filename = path.basename(filepath);
131
+ this.dirRegistery[dir][filename] = true;
132
+
133
+ return true;
134
+ };
135
+
136
+ /**
137
+ * Removes a file from the registery.
138
+ *
139
+ * @param {string} filepath
140
+ * @private
141
+ */
142
+
143
+ Watcher.prototype.unregister = function(filepath) {
144
+ var dir = path.dirname(filepath);
145
+ if (this.dirRegistery[dir]) {
146
+ var filename = path.basename(filepath);
147
+ delete this.dirRegistery[dir][filename];
148
+ }
149
+ };
150
+
151
+ /**
152
+ * Removes a dir from the registery.
153
+ *
154
+ * @param {string} dirpath
155
+ * @private
156
+ */
157
+
158
+ Watcher.prototype.unregisterDir = function(dirpath) {
159
+ if (this.dirRegistery[dirpath]) {
160
+ delete this.dirRegistery[dirpath];
161
+ }
162
+ };
163
+
164
+ /**
165
+ * Checks if a file or directory exists in the registery.
166
+ *
167
+ * @param {string} fullpath
168
+ * @return {boolean}
169
+ * @private
170
+ */
171
+
172
+ Watcher.prototype.registered = function(fullpath) {
173
+ var dir = path.dirname(fullpath);
174
+ return this.dirRegistery[fullpath] ||
175
+ this.dirRegistery[dir] && this.dirRegistery[dir][path.basename(fullpath)];
176
+ };
177
+
178
+ /**
179
+ * Watch a directory.
180
+ *
181
+ * @param {string} dir
182
+ * @private
183
+ */
184
+
185
+ Watcher.prototype.watchdir = function(dir) {
186
+ if (this.watched[dir]) return;
187
+ var watcher = fs.watch(
188
+ dir,
189
+ { persistent: this.persistent },
190
+ this.normalizeChange.bind(this, dir)
191
+ );
192
+ this.watched[dir] = watcher;
193
+
194
+ // Workaround Windows node issue #4337.
195
+ if (platform === 'win32') {
196
+ watcher.on('error', function(error) {
197
+ if (error.code !== 'EPERM') throw error;
198
+ });
199
+ }
200
+
201
+ if (this.root !== dir) {
202
+ this.register(dir);
203
+ }
204
+ };
205
+
206
+ /**
207
+ * In polling mode stop watching files and directories, in normal mode, stop
208
+ * watching files.
209
+ *
210
+ * @param {string} filepath
211
+ * @private
212
+ */
213
+
214
+ Watcher.prototype.stopWatching = function(filepath) {
215
+ if (this.polling) {
216
+ fs.unwatchFile(filepath);
217
+ } else if (this.watched[filepath]) {
218
+ this.watched[filepath].close();
219
+ delete this.watched[filepath];
220
+ }
221
+ };
222
+
223
+ /**
224
+ * End watching.
225
+ *
226
+ * @public
227
+ */
228
+
229
+ Watcher.prototype.close = function() {
230
+ Object.keys(this.watched).forEach(this.stopWatching);
231
+ this.removeAllListeners();
232
+ };
233
+
234
+ /**
235
+ * On some platforms, as pointed out on the fs docs (most likely just win32)
236
+ * the file argument might be missing from the fs event. Try to detect what
237
+ * change by detecting if something was deleted or the most recent file change.
238
+ *
239
+ * @param {string} dir
240
+ * @param {string} event
241
+ * @param {string} file
242
+ * @public
243
+ */
244
+
245
+ Watcher.prototype.detectChangedFile = function(dir, event, callback) {
246
+ var found = false;
247
+ var closest = {mtime: 0};
248
+ var c = 0;
249
+ Object.keys(this.dirRegistery[dir]).forEach(function(file, i, arr) {
250
+ fs.stat(path.join(dir, file), function(error, stat) {
251
+ if (found) return;
252
+ if (error) {
253
+ if (error.code === 'ENOENT') {
254
+ found = true;
255
+ callback(file);
256
+ } else {
257
+ this.emit('error', error);
258
+ }
259
+ } else {
260
+ if (stat.mtime > closest.mtime) {
261
+ stat.file = file;
262
+ closest = stat;
263
+ }
264
+ if (arr.length === ++c) {
265
+ callback(closest.file);
266
+ }
267
+ }
268
+ }.bind(this));
269
+ }, this);
270
+ };
271
+
272
+ /**
273
+ * Normalize fs events and pass it on to be processed.
274
+ *
275
+ * @param {string} dir
276
+ * @param {string} event
277
+ * @param {string} file
278
+ * @public
279
+ */
280
+
281
+ Watcher.prototype.normalizeChange = function(dir, event, file) {
282
+ if (!file) {
283
+ this.detectChangedFile(dir, event, function(actualFile) {
284
+ if (actualFile) {
285
+ this.processChange(dir, event, actualFile);
286
+ }
287
+ }.bind(this));
288
+ } else {
289
+ this.processChange(dir, event, path.normalize(file));
290
+ }
291
+ };
292
+
293
+ /**
294
+ * Process changes.
295
+ *
296
+ * @param {string} dir
297
+ * @param {string} event
298
+ * @param {string} file
299
+ * @public
300
+ */
301
+
302
+ Watcher.prototype.processChange = function(dir, event, file) {
303
+ var fullPath = path.join(dir, file);
304
+ var relativePath = path.join(path.relative(this.root, dir), file);
305
+ fs.stat(fullPath, function(error, stat) {
306
+ if (error && error.code !== 'ENOENT') {
307
+ this.emit('error', error);
308
+ } else if (!error && stat.isDirectory()) {
309
+ // win32 emits usless change events on dirs.
310
+ if (event !== 'change') {
311
+ this.watchdir(fullPath);
312
+ this.emitEvent(ADD_EVENT, relativePath);
313
+ }
314
+ } else {
315
+ var registered = this.registered(fullPath);
316
+ if (error && error.code === 'ENOENT') {
317
+ this.unregister(fullPath);
318
+ this.stopWatching(fullPath);
319
+ this.unregisterDir(fullPath);
320
+ if (registered) {
321
+ this.emitEvent(DELETE_EVENT, relativePath);
322
+ }
323
+ } else if (registered) {
324
+ this.emitEvent(CHANGE_EVENT, relativePath);
325
+ } else {
326
+ if (this.register(fullPath)) {
327
+ this.emitEvent(ADD_EVENT, relativePath);
328
+ }
329
+ }
330
+ }
331
+ }.bind(this));
332
+ };
333
+
334
+ /**
335
+ * Triggers a 'change' event after debounding it to take care of duplicate
336
+ * events on os x.
337
+ *
338
+ * @private
339
+ */
340
+
341
+ Watcher.prototype.emitEvent = function(type, file) {
342
+ var key = type + '-' + file;
343
+ clearTimeout(this.changeTimers[key]);
344
+ this.changeTimers[key] = setTimeout(function() {
345
+ delete this.changeTimers[key];
346
+ this.emit(type, file);
347
+ }.bind(this), DEFAULT_DELAY);
348
+ };
349
+
350
+ /**
351
+ * Initiate the polling file watcher with the event emitter passed from
352
+ * `watch.watchTree`.
353
+ *
354
+ * @param {EventEmitter} monitor
355
+ * @public
356
+ */
357
+
358
+ Watcher.prototype.initPoller = function(monitor) {
359
+ this.watched = monitor.files;
360
+ monitor.on('changed', this.pollerEmit.bind(this, CHANGE_EVENT));
361
+ monitor.on('removed', this.pollerEmit.bind(this, DELETE_EVENT));
362
+ monitor.on('created', this.pollerEmit.bind(this, ADD_EVENT));
363
+ // 1 second wait because mtime is second-based.
364
+ setTimeout(this.emit.bind(this, 'ready'), 1000);
365
+ };
366
+
367
+ /**
368
+ * Transform and emit an event comming from the poller.
369
+ *
370
+ * @param {EventEmitter} monitor
371
+ * @public
372
+ */
373
+
374
+ Watcher.prototype.pollerEmit = function(type, file) {
375
+ file = path.relative(this.root, file);
376
+ this.emit(type, file);
377
+ };
378
+
379
+ /**
380
+ * Given a fullpath of a file or directory check if we need to watch it.
381
+ *
382
+ * @param {string} filepath
383
+ * @param {object} stat
384
+ * @public
385
+ */
386
+
387
+ Watcher.prototype.filter = function(filepath, stat) {
388
+ return stat.isDirectory() || this.isFileIncluded(
389
+ path.relative(this.root, filepath)
390
+ );
391
+ };
392
+
393
+ /**
394
+ * Constants
395
+ */
396
+
397
+ var DEFAULT_DELAY = 100;
398
+ var CHANGE_EVENT = 'change';
399
+ var DELETE_EVENT = 'delete';
400
+ var ADD_EVENT = 'add';
401
+
402
+ /**
403
+ * Traverse a directory recursively calling `callback` on every directory.
404
+ *
405
+ * @param {string} dir
406
+ * @param {function} callback
407
+ * @param {function} endCallback
408
+ * @private
409
+ */
410
+
411
+ function recReaddir(dir, dirCallback, fileCallback, endCallback) {
412
+ walker(dir)
413
+ .on('dir', normalizeProxy(dirCallback))
414
+ .on('file', normalizeProxy(fileCallback))
415
+ .on('end', function() {
416
+ if (platform === 'win32') {
417
+ setTimeout(endCallback, 1000);
418
+ } else {
419
+ endCallback();
420
+ }
421
+ });
422
+ }
423
+
424
+ /**
425
+ * Returns a callback that when called will normalize a path and call the
426
+ * original callback
427
+ *
428
+ * @param {function} callback
429
+ * @return {function}
430
+ * @private
431
+ */
432
+
433
+ function normalizeProxy(callback) {
434
+ return function(filepath) {
435
+ return callback(path.normalize(filepath));
436
+ }
437
+ }