var Immutable = require("immutable");
var Map = Immutable.Map;
var isMap = Immutable.Map.isMap;
var List = Immutable.List;
var qs = require("qs");
var path = require("path");
var fs = require("fs");
var Plugin = Immutable.Record({
    moduleName: "",
    name: "",
    active: true,
    module: undefined,
    options: Map({}),
    via: "inline",
    dir: process.cwd(),
    init: undefined,
    errors: List([])
});
/**
 * Accept a string/object
 * and resolve it into the plugin format above
 * @param item
 * @returns {*}
 */
function resolvePlugin(item) {
    /**
     * Handle when string was given, such as plugins: ['bs-html-injector']
     */
    if (typeof item === "string") {
        return getFromString(item);
    }
    if (!isMap(item)) {
        return new Plugin().mergeDeep({
            errors: [new Error("Plugin not supported in this format")]
        });
    }
    if (item.has("module")) {
        var nameOrObj = item.get("module");
        var options = item.get("options");
        /**
         * The 'module' key can be a string, this allows
         * inline plugin references, but with options
         * eg:
         *
         * bs.init({
         *     plugins: [
         *         {
         *             module: './myjs-file.js'
         *             options: {
         *                 files: "*.html"
         *             }
         *         }
         *     ]
         * });
         */
        if (typeof nameOrObj === "string") {
            return getFromString(nameOrObj).mergeDeep({
                options: options
            });
        }
        /**
         * If the plugin was given completely inline (because it needs options)
         * eg:
         *
         * bs.init({
         *     plugins: [
         *         {
         *             module: {
         *                 plugin: function() {
         *                     console.log('My plugin code')
         *                 }
         *             },
         *             options: {
         *                 files: "*.html"
         *             }
         *         }
         *     ]
         * })
         */
        if (Immutable.Map.isMap(nameOrObj)) {
            return new Plugin({
                module: nameOrObj,
                options: options
            });
        }
    }
    /**
     * If a module was given directly. For example, ater calling require.
     *
     * eg:
     *    var myplugin = require('./some-js');
     *    bs.init({plugins: [myplugin]});
     */
    if (item.has("plugin")) {
        return new Plugin({
            module: item
        });
    }
    /**
     * If we reach here, the plugin option was used incorrectly
     */
    return new Plugin().mergeDeep({
        errors: [new Error("Plugin was not configured correctly")]
    });
}
module.exports.resolvePlugin = resolvePlugin;
/**
 * Load a plugin from disk
 * @param item
 * @returns {*}
 */
function requirePlugin(item) {
    /**
     * if the "module" property already exists and
     * is not a string, then we bail and don't bother looking
     * for the file
     */
    if (item.get("module") && typeof item.get("module") !== "string") {
        return item;
    }
    try {
        /**
         * Try a raw node require() call - this will be how
         * regular "npm installed" plugins wil work
         */
        var maybe = require.resolve(item.get("name"));
        return item.set("module", require(maybe));
    }
    catch (e) {
        /**
         * If require threw an MODULE_NOT_FOUND error, try again
         * by resolving from cwd. This is needed since cli
         * users will not add ./ to the front of a path (which
         * node requires to resolve from cwd)
         */
        if (e.code === "MODULE_NOT_FOUND") {
            var maybe = path.resolve(process.cwd(), item.get("name"));
            if (fs.existsSync(maybe)) {
                return item.set("module", require(maybe));
            }
            else {
                /**
                 * Finally return a plugin that contains the error
                 * this will be picked up later and discarded
                 */
                return item.update("errors", function (errors) {
                    return errors.concat(e);
                });
            }
        }
        throw e;
    }
}
module.exports.requirePlugin = requirePlugin;
function getFromString(string) {
    /**
     * We allow query strings for plugins, so always split on ?
     */
    var split = string.split("?");
    var outGoing = new Plugin({
        moduleName: split[0],
        name: split[0]
    });
    if (split.length > 1) {
        return outGoing.update("options", function (opts) {
            return opts.mergeDeep(qs.parse(split[1]));
        });
    }
    return outGoing;
}
//# sourceMappingURL=plugins.js.map