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
|