| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 | "use strict";var hooks = require("./hooks");var asyncTasks = require("./async-tasks");var config = require("./config");var connectUtils = require("./connect-utils");var utils = require("./utils");var logger = require("./logger");var eachSeries = utils.eachSeries;var _ = require("./lodash.custom");var EE = require("easy-extender");/** * Required internal plugins. * Any of these can be overridden by deliberately * causing a name-clash. */var defaultPlugins = {    logger: logger,    socket: require("./sockets"),    "file:watcher": require("./file-watcher"),    server: require("./server"),    tunnel: require("./tunnel"),    "client:script": require("browser-sync-client"),    UI: require("browser-sync-ui")};/** * @constructor */var BrowserSync = function (emitter) {    var bs = this;    bs.cwd = process.cwd();    bs.active = false;    bs.paused = false;    bs.config = config;    bs.utils = utils;    bs.events = bs.emitter = emitter;    bs._userPlugins = [];    bs._reloadQueue = [];    bs._cleanupTasks = [];    bs._browserReload = false;    // Plugin management    bs.pluginManager = new EE(defaultPlugins, hooks);};/** * Call a user-options provided callback * @param name */BrowserSync.prototype.callback = function (name) {    var bs = this;    var cb = bs.options.getIn(["callbacks", name]);    if (_.isFunction(cb)) {        cb.apply(bs.publicInstance, _.toArray(arguments).slice(1));    }};/** * @param {Map} options * @param {Function} cb * @returns {BrowserSync} */BrowserSync.prototype.init = function (options, cb) {    /**     * Safer access to `this`     * @type {BrowserSync}     */    var bs = this;    /**     * Set user-provided callback, or assign a noop     * @type {Function}     */    bs.cb = cb || utils.defaultCallback;    /**     * Verify provided config.     * Some options are not compatible and will cause us to     * end the process.     */    if (!utils.verifyConfig(options, bs.cb)) {        return;    }    /**     * Save a reference to the original options     * @type {Map}     * @private     */    bs._options = options;    /**     * Set additional options that depend on what the     * user may of provided     * @type {Map}     */    bs.options = options;    /**     * Kick off default plugins.     */    bs.pluginManager.init();    /**     * Create a base logger & debugger.     */    bs.logger = bs.pluginManager.get("logger")(bs.events, bs);    bs.debugger = bs.logger.clone({ useLevelPrefixes: true });    bs.debug = bs.debugger.debug;    /**     * Run each setup task in sequence     */    eachSeries(asyncTasks, taskRunner(bs), tasksComplete(bs));    return this;};/** * Run 1 setup task. * Each task is a pure function. * They can return options or instance properties to set, * but they cannot set them directly. * @param {BrowserSync} bs * @returns {Function} */function taskRunner(bs) {    return function (item, cb) {        bs.debug("-> {yellow:Starting Step: " + item.step);        /**         * Execute the current task.         */        item.fn(bs, executeTask);        function executeTask(err, out) {            /**             * Exit early if any task returned an error.             */            if (err) {                return cb(err);            }            /**             * Act on return values (such as options to be set,             * or instance properties to be set             */            if (out) {                handleOut(bs, out);            }            bs.debug("+  {green:Step Complete: " + item.step);            cb();        }    };}/** * @param bs * @param out */function handleOut(bs, out) {    /**     * Set a single/many option.     */    if (out.options) {        setOptions(bs, out.options);    }    /**     * Any options returned that require path access?     */    if (out.optionsIn) {        out.optionsIn.forEach(function (item) {            bs.setOptionIn(item.path, item.value);        });    }    /**     * Any instance properties returned?     */    if (out.instance) {        Object.keys(out.instance).forEach(function (key) {            bs[key] = out.instance[key];        });    }}/** * Update the options Map * @param bs * @param options */function setOptions(bs, options) {    /**     * If multiple options were set, act on the immutable map     * in an efficient way     */    if (Object.keys(options).length > 1) {        bs.setMany(function (item) {            Object.keys(options).forEach(function (key) {                item.set(key, options[key]);                return item;            });        });    }    else {        Object.keys(options).forEach(function (key) {            bs.setOption(key, options[key]);        });    }}/** * At this point, ALL async tasks have completed * @param {BrowserSync} bs * @returns {Function} */function tasksComplete(bs) {    return function (err) {        if (err) {            bs.logger.setOnce("useLevelPrefixes", true).error(err.message);        }        /**         * Set active flag         */        bs.active = true;        /**         * @deprecated         */        bs.events.emit("init", bs);        /**         * This is no-longer needed as the Callback now only resolves         * when everything (including slow things, like the tunnel) is ready.         * It's here purely for backwards compatibility.         * @deprecated         */        bs.events.emit("service:running", {            options: bs.options,            baseDir: bs.options.getIn(["server", "baseDir"]),            type: bs.options.get("mode"),            port: bs.options.get("port"),            url: bs.options.getIn(["urls", "local"]),            urls: bs.options.get("urls").toJS(),            tunnel: bs.options.getIn(["urls", "tunnel"])        });        /**         * Call any option-provided callbacks         */        bs.callback("ready", null, bs);        /**         * Finally, call the user-provided callback given as last arg         */        bs.cb(null, bs);    };}/** * @param module * @param opts * @param cb */BrowserSync.prototype.registerPlugin = function (module, opts, cb) {    var bs = this;    bs.pluginManager.registerPlugin(module, opts, cb);    if (module["plugin:name"]) {        bs._userPlugins.push(module);    }};/** * Get a plugin by name * @param name */BrowserSync.prototype.getUserPlugin = function (name) {    var bs = this;    var items = bs.getUserPlugins(function (item) {        return item["plugin:name"] === name;    });    if (items && items.length) {        return items[0];    }    return false;};/** * @param {Function} [filter] */BrowserSync.prototype.getUserPlugins = function (filter) {    var bs = this;    filter =        filter ||            function () {                return true;            };    /**     * Transform Plugins option     */    bs.userPlugins = bs._userPlugins.filter(filter).map(function (plugin) {        return {            name: plugin["plugin:name"],            active: plugin._enabled,            opts: bs.pluginManager.pluginOptions[plugin["plugin:name"]]        };    });    return bs.userPlugins;};/** * Get middleware * @returns {*} */BrowserSync.prototype.getMiddleware = function (type) {    var types = {        connector: connectUtils.socketConnector(this.options)    };    if (type in types) {        return function (req, res) {            res.setHeader("Content-Type", "text/javascript");            res.end(types[type]);        };    }};/** * Shortcut for pushing a file-serving middleware * onto the stack * @param {String} path * @param {{type: string, content: string}} props */var _serveFileCount = 0;BrowserSync.prototype.serveFile = function (path, props) {    var bs = this;    var mode = bs.options.get("mode");    var entry = {        handle: function (req, res) {            res.setHeader("Content-Type", props.type);            res.end(props.content);        },        id: "Browsersync - " + _serveFileCount++,        route: path    };    bs._addMiddlewareToStack(entry);};/** * Add middlewares on the fly */BrowserSync.prototype._addMiddlewareToStack = function (entry) {    var bs = this;    /**     * additional middlewares are always appended -1,     * this is to allow the proxy middlewares to remain,     * and the directory index to remain in serveStatic/snippet modes     */    bs.app.stack.splice(bs.app.stack.length - 1, 0, entry);};var _addMiddlewareCount = 0;BrowserSync.prototype.addMiddleware = function (route, handle, opts) {    var bs = this;    if (!bs.app) {        return;    }    opts = opts || {};    if (!opts.id) {        opts.id = "bs-mw-" + _addMiddlewareCount++;    }    if (route === "*") {        route = "";    }    var entry = {        id: opts.id,        route: route,        handle: handle    };    if (opts.override) {        entry.override = true;    }    bs.options = bs.options.update("middleware", function (mw) {        if (bs.options.get("mode") === "proxy") {            return mw.insert(mw.size - 1, entry);        }        return mw.concat(entry);    });    bs.resetMiddlewareStack();};/** * Remove middlewares on the fly * @param {String} id * @returns {Server} */BrowserSync.prototype.removeMiddleware = function (id) {    var bs = this;    if (!bs.app) {        return;    }    bs.options = bs.options.update("middleware", function (mw) {        return mw.filter(function (mw) {            return mw.id !== id;        });    });    bs.resetMiddlewareStack();};/** * Middleware for socket connection (external usage) * @param opts * @returns {*} */BrowserSync.prototype.getSocketConnector = function (opts) {    var bs = this;    return function (req, res) {        res.setHeader("Content-Type", "text/javascript");        res.end(bs.getExternalSocketConnector(opts));    };};/** * Socket connector as a string * @param {Object} opts * @returns {*} */BrowserSync.prototype.getExternalSocketConnector = function (opts) {    var bs = this;    return connectUtils.socketConnector(bs.options.withMutations(function (item) {        item.set("socket", item.get("socket").merge(opts));        if (!bs.options.getIn(["proxy", "ws"])) {            item.set("mode", "snippet");        }    }));};/** * Callback helper * @param name */BrowserSync.prototype.getOption = function (name) {    this.debug("Getting option: {magenta:%s", name);    return this.options.get(name);};/** * Callback helper * @param path */BrowserSync.prototype.getOptionIn = function (path) {    this.debug("Getting option via path: {magenta:%s", path);    return this.options.getIn(path);};/** * @returns {BrowserSync.options} */BrowserSync.prototype.getOptions = function () {    return this.options;};/** * @returns {BrowserSync.options} */BrowserSync.prototype.getLogger = logger.getLogger;/** * @param {String} name * @param {*} value * @returns {BrowserSync.options|*} */BrowserSync.prototype.setOption = function (name, value, opts) {    var bs = this;    opts = opts || {};    bs.debug("Setting Option: {cyan:%s} - {magenta:%s", name, value.toString());    bs.options = bs.options.set(name, value);    if (!opts.silent) {        bs.events.emit("options:set", {            path: name,            value: value,            options: bs.options        });    }    return this.options;};/** * @param path * @param value * @param opts * @returns {Map|*|BrowserSync.options} */BrowserSync.prototype.setOptionIn = function (path, value, opts) {    var bs = this;    opts = opts || {};    bs.debug("Setting Option: {cyan:%s} - {magenta:%s", path.join("."), value.toString());    bs.options = bs.options.setIn(path, value);    if (!opts.silent) {        bs.events.emit("options:set", {            path: path,            value: value,            options: bs.options        });    }    return bs.options;};/** * Set multiple options with mutations * @param fn * @param opts * @returns {Map|*} */BrowserSync.prototype.setMany = function (fn, opts) {    var bs = this;    opts = opts || {};    bs.debug("Setting multiple Options");    bs.options = bs.options.withMutations(fn);    if (!opts.silent) {        bs.events.emit("options:set", { options: bs.options.toJS() });    }    return this.options;};BrowserSync.prototype.addRewriteRule = function (rule) {    var bs = this;    bs.options = bs.options.update("rewriteRules", function (rules) {        return rules.concat(rule);    });    bs.resetMiddlewareStack();};BrowserSync.prototype.removeRewriteRule = function (id) {    var bs = this;    bs.options = bs.options.update("rewriteRules", function (rules) {        return rules.filter(function (rule) {            return rule.id !== id;        });    });    bs.resetMiddlewareStack();};BrowserSync.prototype.setRewriteRules = function (rules) {    var bs = this;    bs.options = bs.options.update("rewriteRules", function (_) {        return rules;    });    bs.resetMiddlewareStack();};/** * Add a new rewrite rule to the stack * @param {Object} rule */BrowserSync.prototype.resetMiddlewareStack = function () {    var bs = this;    var middlewares = require("./server/utils").getMiddlewares(bs, bs.options);    bs.app.stack = middlewares;};/** * @param fn */BrowserSync.prototype.registerCleanupTask = function (fn) {    this._cleanupTasks.push(fn);};/** * Instance Cleanup */BrowserSync.prototype.cleanup = function (cb) {    var bs = this;    if (!bs.active) {        return;    }    // Remove all event listeners    if (bs.events) {        bs.debug("Removing event listeners...");        bs.events.removeAllListeners();    }    // Close any core file watchers    if (bs.watchers) {        Object.keys(bs.watchers).forEach(function (key) {            bs.watchers[key].watchers.forEach(function (watcher) {                watcher.close();            });        });    }    // Run any additional clean up tasks    bs._cleanupTasks.forEach(function (fn) {        if (_.isFunction(fn)) {            fn(bs);        }    });    // Reset the flag    bs.debug("Setting {magenta:active: false");    bs.active = false;    bs.paused = false;    bs.pluginManager.plugins = {};    bs.pluginManager.pluginOptions = {};    bs.pluginManager.defaultPlugins = defaultPlugins;    bs._userPlugins = [];    bs.userPlugins = [];    bs._reloadTimer = undefined;    bs._reloadQueue = [];    bs._cleanupTasks = [];    if (_.isFunction(cb)) {        cb(null, bs);    }};module.exports = BrowserSync;//# sourceMappingURL=browser-sync.js.map
 |