index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #! /usr/bin/env node
  2. "use strict";
  3. /**
  4. * @module BrowserSync
  5. */
  6. var pjson = require("../package.json");
  7. var BrowserSync = require("./browser-sync");
  8. var publicUtils = require("./public/public-utils");
  9. var events = require("events");
  10. var PassThrough = require("stream").PassThrough;
  11. var logger = require("eazy-logger").Logger({
  12. useLevelPrefixes: true
  13. });
  14. var singleton = false;
  15. var singletonPlugins = [];
  16. var instances = [];
  17. /**
  18. * @type {boolean|EventEmitter}
  19. */
  20. var singletonEmitter = false;
  21. module.exports = initSingleton;
  22. /**
  23. * Create a Browsersync instance
  24. * @method create
  25. * @param {String} name an identifier that can used for retrieval later
  26. */
  27. /**
  28. * Get a single instance by name. This is useful if you have your
  29. * build scripts in separate files
  30. * @method get
  31. * @param {String} name
  32. * @returns {Object|Boolean}
  33. */
  34. module.exports.get = function (name) {
  35. var instance = getSingle(name);
  36. if (instance) {
  37. return instance;
  38. }
  39. throw new Error("An instance with the name `%s` was not found.".replace("%s", name));
  40. };
  41. /**
  42. * Check if an instance has been created.
  43. * @method has
  44. * @param {String} name
  45. * @returns {Boolean}
  46. */
  47. module.exports.has = function (name) {
  48. var instance = getSingle(name);
  49. if (instance) {
  50. return true;
  51. }
  52. return false;
  53. };
  54. /**
  55. * Start the Browsersync service. This will launch a server, proxy or start the snippet
  56. * mode depending on your use-case.
  57. * @method init
  58. * @param {Object} [config] This is the main configuration for your Browsersync instance and can contain any of the [available options]({{site.links.options}})
  59. * If you do not pass a config an argument for configuration, Browsersync will still run; but it will be in the `snippet` mode
  60. * @param {Function} [cb] If you pass a callback function, it will be called when Browsersync has completed all setup tasks and is ready to use. This
  61. * is useful when you need to wait for information (for example: urls, port etc) or perform other tasks synchronously.
  62. * @returns {BrowserSync}
  63. */
  64. module.exports.init = initSingleton;
  65. /**
  66. * Register a plugin. Must implement at least a 'plugin' method that returns a
  67. * callable function.
  68. *
  69. * @method use
  70. * @param {String} name The name of the plugin
  71. * @param {Object} module The object to be `required`.
  72. * @param {Function} [cb] A callback function that will return any errors.
  73. */
  74. module.exports.use = function () {
  75. var args = Array.prototype.slice.call(arguments);
  76. singletonPlugins.push({
  77. args: args
  78. });
  79. };
  80. /**
  81. * The `reload` method will inform all browsers about changed files and will either cause the browser to refresh, or inject the files where possible.
  82. *
  83. * @method reload
  84. * @param {String|Array|Object} [arg] The file or files to be reloaded.
  85. * @returns {*}
  86. */
  87. module.exports.reload = noop("reload");
  88. /**
  89. * The `stream` method returns a transform stream and can act once or on many files.
  90. *
  91. * @method stream
  92. * @param {Object} [opts] Configuration for the stream method
  93. * @param {Object} [opts.match] Resulting files to reload. The path is from the
  94. * root of the site (not the root of your project). You can use '**' to recurse
  95. * directories.
  96. * @param {Object} [opts.once] Only reload on the first changed file in the stream.
  97. * @since 2.6.0
  98. * @returns {*}
  99. */
  100. module.exports.stream = noop("stream");
  101. /**
  102. * Helper method for browser notifications
  103. *
  104. * @method notify
  105. * @param {String|HTML} msg Can be a simple message such as 'Connected' or HTML
  106. * @param {Number} [timeout] How long the message will remain in the browser. @since 1.3.0
  107. */
  108. module.exports.notify = noop("notify");
  109. /**
  110. * This method will close any running server, stop file watching & exit the current process.
  111. *
  112. * @method exit
  113. */
  114. module.exports.exit = noop("exit");
  115. /**
  116. * Stand alone file-watcher. Use this along with Browsersync to create your own, minimal build system
  117. * @method watch
  118. * @param {string} patterns Glob patterns for files to watch
  119. * @param {object} [opts] Options to be passed to Chokidar - check what's available in [their docs](https://github.com/paulmillr/chokidar#getting-started)
  120. * @param {function} [fn] Callback function for each event.
  121. * @since 2.6.0
  122. */
  123. module.exports.watch = noop("watch");
  124. /**
  125. * Method to pause file change events
  126. *
  127. * @method pause
  128. */
  129. module.exports.pause = noop("pause");
  130. /**
  131. * Method to resume paused watchers
  132. *
  133. * @method resume
  134. */
  135. module.exports.resume = noop("resume");
  136. /**
  137. * Add properties fo
  138. */
  139. Object.defineProperties(module.exports, {
  140. /**
  141. * The internal Event Emitter used by the running Browsersync instance (if there is one).
  142. * You can use this to emit your own events, such as changed files, logging etc.
  143. *
  144. * @property emitter
  145. */
  146. emitter: {
  147. get: function () {
  148. if (!singletonEmitter) {
  149. singletonEmitter = newEmitter();
  150. return singletonEmitter;
  151. }
  152. return singletonEmitter;
  153. }
  154. },
  155. /**
  156. * A simple true/false flag that you can use to determine if there's a currently-running Browsersync instance.
  157. *
  158. * @property active
  159. */
  160. active: {
  161. get: getSingletonValue.bind(null, "active")
  162. },
  163. /**
  164. * A simple true/false flag to determine if the current instance is paused
  165. *
  166. * @property paused
  167. */
  168. paused: {
  169. get: getSingletonValue.bind(null, "paused")
  170. }
  171. });
  172. /**
  173. * Event emitter factory
  174. * @returns {EventEmitter}
  175. */
  176. function newEmitter() {
  177. var emitter = new events.EventEmitter();
  178. emitter.setMaxListeners(20);
  179. return emitter;
  180. }
  181. /**
  182. * Get the singleton's emitter, or a new one.
  183. * @returns {EventEmitter}
  184. */
  185. function getSingletonEmitter() {
  186. if (singletonEmitter) {
  187. return singletonEmitter;
  188. }
  189. singletonEmitter = newEmitter();
  190. return singletonEmitter;
  191. }
  192. /**
  193. * Helper to allow methods to be called on the module export
  194. * before there's a running instance
  195. * @param {String} name
  196. * @returns {Function}
  197. */
  198. function noop(name) {
  199. return function () {
  200. var args = Array.prototype.slice.call(arguments);
  201. if (singleton) {
  202. return singleton[name].apply(singleton, args);
  203. }
  204. else {
  205. if (publicUtils.isStreamArg(name, args)) {
  206. return new PassThrough({ objectMode: true });
  207. }
  208. }
  209. };
  210. }
  211. /**
  212. * Create a single instance when module export is used directly via browserSync({});
  213. * This is mostly for back-compatibility, for also for the nicer api.
  214. * This will never be removed to ensure we never break user-land, but
  215. * we should discourage it's use.
  216. * @returns {*}
  217. */
  218. function initSingleton() {
  219. var instance;
  220. if (instances.length) {
  221. instance = instances.filter(function (item) {
  222. return item.name === "singleton";
  223. });
  224. if (instance.length) {
  225. logger.error("{yellow:You tried to start Browsersync twice!} To create multiple instances, use {cyan:browserSync.create().init()");
  226. return instance;
  227. }
  228. }
  229. var args = Array.prototype.slice.call(arguments);
  230. singleton = create("singleton", getSingletonEmitter());
  231. if (singletonPlugins.length) {
  232. singletonPlugins.forEach(function (obj) {
  233. singleton.instance.registerPlugin.apply(singleton.instance, obj.args);
  234. });
  235. }
  236. singleton.init.apply(null, args);
  237. return singleton;
  238. }
  239. /**
  240. * @param {String} prop
  241. * @returns {Object|Boolean}
  242. */
  243. function getSingletonValue(prop) {
  244. var single = getSingle("singleton");
  245. if (single) {
  246. return single[prop];
  247. }
  248. return false;
  249. }
  250. /**
  251. * Get a single instance by name
  252. * @param {String} name
  253. * @returns {Object|Boolean}
  254. */
  255. function getSingle(name) {
  256. if (instances.length) {
  257. var match = instances.filter(function (item) {
  258. return item.name === name;
  259. });
  260. if (match.length) {
  261. return match[0];
  262. }
  263. }
  264. return false;
  265. }
  266. /**
  267. * Create an instance of Browsersync
  268. * @param {String} [name]
  269. * @param {EventEmitter} [emitter]
  270. * @returns {{init: *, exit: (exit|exports), notify: *, reload: *, cleanup: *, emitter: (Browsersync.events|*), use: *}}
  271. */
  272. /**
  273. * Reset the state of the module.
  274. * (should only be needed for test environments)
  275. */
  276. module.exports.reset = function () {
  277. instances.forEach(function (item) {
  278. item.cleanup();
  279. });
  280. instances = [];
  281. singletonPlugins = [];
  282. singletonEmitter = false;
  283. singleton = false;
  284. };
  285. /**
  286. * @type {Array}
  287. */
  288. module.exports.instances = instances;
  289. /**
  290. * Create an instance of Browsersync
  291. * @param {String} [name]
  292. * @param {EventEmitter} [emitter]
  293. * @returns {{init: *, exit: (exit|exports), notify: *, reload: *, cleanup: *, emitter: (Browsersync.events|*), use: *}}
  294. */
  295. module.exports.create = create;
  296. function create(name, emitter) {
  297. name = name || new Date().getTime();
  298. emitter = emitter || newEmitter();
  299. var browserSync = new BrowserSync(emitter);
  300. var instance = {
  301. name: name,
  302. instance: browserSync,
  303. exit: require("./public/exit")(browserSync),
  304. notify: require("./public/notify")(browserSync),
  305. pause: require("./public/pause")(browserSync),
  306. resume: require("./public/resume")(browserSync),
  307. reload: require("./public/reload")(emitter),
  308. stream: require("./public/stream")(emitter),
  309. cleanup: browserSync.cleanup.bind(browserSync),
  310. use: browserSync.registerPlugin.bind(browserSync),
  311. getOption: browserSync.getOption.bind(browserSync),
  312. emitter: browserSync.events,
  313. watch: require("./file-watcher").watch
  314. };
  315. browserSync.publicInstance = instance;
  316. instance.init = require("./public/init")(browserSync, name, pjson);
  317. Object.defineProperty(instance, "active", {
  318. get: function () {
  319. return browserSync.active;
  320. }
  321. });
  322. Object.defineProperty(instance, "paused", {
  323. get: function () {
  324. return browserSync.paused;
  325. }
  326. });
  327. /**
  328. * Access to client-side socket for emitting events
  329. *
  330. * @property sockets
  331. */
  332. Object.defineProperty(instance, "sockets", {
  333. get: function () {
  334. if (!browserSync.active) {
  335. return {
  336. emit: function () { },
  337. on: function () { }
  338. };
  339. }
  340. else {
  341. return browserSync.io.sockets;
  342. }
  343. }
  344. });
  345. instances.push(instance);
  346. return instance;
  347. }
  348. //# sourceMappingURL=index.js.map