#! /usr/bin/env node "use strict"; /** * @module BrowserSync */ var pjson = require("../package.json"); var BrowserSync = require("./browser-sync"); var publicUtils = require("./public/public-utils"); var events = require("events"); var PassThrough = require("stream").PassThrough; var logger = require("eazy-logger").Logger({ useLevelPrefixes: true }); var singleton = false; var singletonPlugins = []; var instances = []; /** * @type {boolean|EventEmitter} */ var singletonEmitter = false; module.exports = initSingleton; /** * Create a Browsersync instance * @method create * @param {String} name an identifier that can used for retrieval later */ /** * Get a single instance by name. This is useful if you have your * build scripts in separate files * @method get * @param {String} name * @returns {Object|Boolean} */ module.exports.get = function (name) { var instance = getSingle(name); if (instance) { return instance; } throw new Error("An instance with the name `%s` was not found.".replace("%s", name)); }; /** * Check if an instance has been created. * @method has * @param {String} name * @returns {Boolean} */ module.exports.has = function (name) { var instance = getSingle(name); if (instance) { return true; } return false; }; /** * Start the Browsersync service. This will launch a server, proxy or start the snippet * mode depending on your use-case. * @method init * @param {Object} [config] This is the main configuration for your Browsersync instance and can contain any of the [available options]({{site.links.options}}) * If you do not pass a config an argument for configuration, Browsersync will still run; but it will be in the `snippet` mode * @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 * is useful when you need to wait for information (for example: urls, port etc) or perform other tasks synchronously. * @returns {BrowserSync} */ module.exports.init = initSingleton; /** * Register a plugin. Must implement at least a 'plugin' method that returns a * callable function. * * @method use * @param {String} name The name of the plugin * @param {Object} module The object to be `required`. * @param {Function} [cb] A callback function that will return any errors. */ module.exports.use = function () { var args = Array.prototype.slice.call(arguments); singletonPlugins.push({ args: args }); }; /** * The `reload` method will inform all browsers about changed files and will either cause the browser to refresh, or inject the files where possible. * * @method reload * @param {String|Array|Object} [arg] The file or files to be reloaded. * @returns {*} */ module.exports.reload = noop("reload"); /** * The `stream` method returns a transform stream and can act once or on many files. * * @method stream * @param {Object} [opts] Configuration for the stream method * @param {Object} [opts.match] Resulting files to reload. The path is from the * root of the site (not the root of your project). You can use '**' to recurse * directories. * @param {Object} [opts.once] Only reload on the first changed file in the stream. * @since 2.6.0 * @returns {*} */ module.exports.stream = noop("stream"); /** * Helper method for browser notifications * * @method notify * @param {String|HTML} msg Can be a simple message such as 'Connected' or HTML * @param {Number} [timeout] How long the message will remain in the browser. @since 1.3.0 */ module.exports.notify = noop("notify"); /** * This method will close any running server, stop file watching & exit the current process. * * @method exit */ module.exports.exit = noop("exit"); /** * Stand alone file-watcher. Use this along with Browsersync to create your own, minimal build system * @method watch * @param {string} patterns Glob patterns for files to watch * @param {object} [opts] Options to be passed to Chokidar - check what's available in [their docs](https://github.com/paulmillr/chokidar#getting-started) * @param {function} [fn] Callback function for each event. * @since 2.6.0 */ module.exports.watch = noop("watch"); /** * Method to pause file change events * * @method pause */ module.exports.pause = noop("pause"); /** * Method to resume paused watchers * * @method resume */ module.exports.resume = noop("resume"); /** * Add properties fo */ Object.defineProperties(module.exports, { /** * The internal Event Emitter used by the running Browsersync instance (if there is one). * You can use this to emit your own events, such as changed files, logging etc. * * @property emitter */ emitter: { get: function () { if (!singletonEmitter) { singletonEmitter = newEmitter(); return singletonEmitter; } return singletonEmitter; } }, /** * A simple true/false flag that you can use to determine if there's a currently-running Browsersync instance. * * @property active */ active: { get: getSingletonValue.bind(null, "active") }, /** * A simple true/false flag to determine if the current instance is paused * * @property paused */ paused: { get: getSingletonValue.bind(null, "paused") } }); /** * Event emitter factory * @returns {EventEmitter} */ function newEmitter() { var emitter = new events.EventEmitter(); emitter.setMaxListeners(20); return emitter; } /** * Get the singleton's emitter, or a new one. * @returns {EventEmitter} */ function getSingletonEmitter() { if (singletonEmitter) { return singletonEmitter; } singletonEmitter = newEmitter(); return singletonEmitter; } /** * Helper to allow methods to be called on the module export * before there's a running instance * @param {String} name * @returns {Function} */ function noop(name) { return function () { var args = Array.prototype.slice.call(arguments); if (singleton) { return singleton[name].apply(singleton, args); } else { if (publicUtils.isStreamArg(name, args)) { return new PassThrough({ objectMode: true }); } } }; } /** * Create a single instance when module export is used directly via browserSync({}); * This is mostly for back-compatibility, for also for the nicer api. * This will never be removed to ensure we never break user-land, but * we should discourage it's use. * @returns {*} */ function initSingleton() { var instance; if (instances.length) { instance = instances.filter(function (item) { return item.name === "singleton"; }); if (instance.length) { logger.error("{yellow:You tried to start Browsersync twice!} To create multiple instances, use {cyan:browserSync.create().init()"); return instance; } } var args = Array.prototype.slice.call(arguments); singleton = create("singleton", getSingletonEmitter()); if (singletonPlugins.length) { singletonPlugins.forEach(function (obj) { singleton.instance.registerPlugin.apply(singleton.instance, obj.args); }); } singleton.init.apply(null, args); return singleton; } /** * @param {String} prop * @returns {Object|Boolean} */ function getSingletonValue(prop) { var single = getSingle("singleton"); if (single) { return single[prop]; } return false; } /** * Get a single instance by name * @param {String} name * @returns {Object|Boolean} */ function getSingle(name) { if (instances.length) { var match = instances.filter(function (item) { return item.name === name; }); if (match.length) { return match[0]; } } return false; } /** * Create an instance of Browsersync * @param {String} [name] * @param {EventEmitter} [emitter] * @returns {{init: *, exit: (exit|exports), notify: *, reload: *, cleanup: *, emitter: (Browsersync.events|*), use: *}} */ /** * Reset the state of the module. * (should only be needed for test environments) */ module.exports.reset = function () { instances.forEach(function (item) { item.cleanup(); }); instances = []; singletonPlugins = []; singletonEmitter = false; singleton = false; }; /** * @type {Array} */ module.exports.instances = instances; /** * Create an instance of Browsersync * @param {String} [name] * @param {EventEmitter} [emitter] * @returns {{init: *, exit: (exit|exports), notify: *, reload: *, cleanup: *, emitter: (Browsersync.events|*), use: *}} */ module.exports.create = create; function create(name, emitter) { name = name || new Date().getTime(); emitter = emitter || newEmitter(); var browserSync = new BrowserSync(emitter); var instance = { name: name, instance: browserSync, exit: require("./public/exit")(browserSync), notify: require("./public/notify")(browserSync), pause: require("./public/pause")(browserSync), resume: require("./public/resume")(browserSync), reload: require("./public/reload")(emitter), stream: require("./public/stream")(emitter), cleanup: browserSync.cleanup.bind(browserSync), use: browserSync.registerPlugin.bind(browserSync), getOption: browserSync.getOption.bind(browserSync), emitter: browserSync.events, watch: require("./file-watcher").watch }; browserSync.publicInstance = instance; instance.init = require("./public/init")(browserSync, name, pjson); Object.defineProperty(instance, "active", { get: function () { return browserSync.active; } }); Object.defineProperty(instance, "paused", { get: function () { return browserSync.paused; } }); /** * Access to client-side socket for emitting events * * @property sockets */ Object.defineProperty(instance, "sockets", { get: function () { if (!browserSync.active) { return { emit: function () { }, on: function () { } }; } else { return browserSync.io.sockets; } } }); instances.push(instance); return instance; } //# sourceMappingURL=index.js.map