348 lines
10 KiB
JavaScript
348 lines
10 KiB
JavaScript
|
#! /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
|