2016-06-26 11:53:19 +00:00
|
|
|
/*global requirejs*/
|
|
|
|
define(function(require, exports, module) {
|
|
|
|
main.consumes = [
|
2017-05-09 13:51:25 +00:00
|
|
|
"app", "ext", "c9", "Plugin", "proc", "fs", "vfs", "dialog.error",
|
|
|
|
"util"
|
2016-06-26 11:53:19 +00:00
|
|
|
];
|
2017-02-12 20:04:05 +00:00
|
|
|
main.provides = ["pluginManager", "plugin.manager", "plugin.debug"];
|
2016-06-26 11:53:19 +00:00
|
|
|
return main;
|
|
|
|
|
|
|
|
|
|
|
|
function main(options, imports, register) {
|
|
|
|
var c9 = imports.c9;
|
|
|
|
var fs = imports.fs;
|
|
|
|
var ext = imports.ext;
|
|
|
|
var proc = imports.proc;
|
2017-02-12 20:04:05 +00:00
|
|
|
var Plugin = imports.Plugin;
|
2016-06-26 11:53:19 +00:00
|
|
|
var util = imports.util;
|
2017-02-26 20:21:10 +00:00
|
|
|
var vfs = imports.vfs;
|
2016-06-26 11:53:19 +00:00
|
|
|
var showError = imports["dialog.error"].show;
|
2017-05-02 17:29:09 +00:00
|
|
|
var architectApp = imports.app;
|
2016-06-26 11:53:19 +00:00
|
|
|
|
|
|
|
var join = require("path").join;
|
|
|
|
var basename = require("path").basename;
|
|
|
|
var dirname = require("path").dirname;
|
2017-02-26 20:21:10 +00:00
|
|
|
var async = require("async");
|
2017-02-12 20:04:05 +00:00
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
var staticPrefix = options.staticPrefix;
|
2017-02-12 20:04:05 +00:00
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
var TEMPLATES = {
|
|
|
|
"plugin.simple": "Empty Plugin",
|
|
|
|
"plugin.default": "Full Plugin",
|
|
|
|
"plugin.installer": "Installer Plugin",
|
|
|
|
"plugin.bundle": "Cloud9 Bundle"
|
|
|
|
};
|
|
|
|
|
|
|
|
/***** Initialization *****/
|
|
|
|
|
2017-05-01 14:46:15 +00:00
|
|
|
var plugin = new Plugin();
|
2017-03-14 12:17:29 +00:00
|
|
|
var emit = plugin.getEmitter();
|
2016-06-26 11:53:19 +00:00
|
|
|
|
2017-02-26 20:21:10 +00:00
|
|
|
var disabledPlugins = Object.create(null);
|
2017-03-14 12:17:29 +00:00
|
|
|
var packages = Object.create(null);
|
2017-05-09 13:51:25 +00:00
|
|
|
|
|
|
|
function load() {
|
2017-05-11 10:40:49 +00:00
|
|
|
function loadDefaultPlugins() {
|
|
|
|
// do not allow errors here to interfer with connect event
|
|
|
|
setTimeout(function() {
|
2017-05-09 13:51:25 +00:00
|
|
|
loadPackage(options.loadFromDisk, function(err) {
|
|
|
|
if (err) return showError(err);
|
|
|
|
});
|
2017-05-11 10:40:49 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
if (options.loadFromDisk) {
|
2017-05-09 13:51:25 +00:00
|
|
|
if (vfs.connected) loadDefaultPlugins();
|
2017-05-09 14:19:37 +00:00
|
|
|
else vfs.once("connect", loadDefaultPlugins);
|
2017-05-09 13:51:25 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-26 11:53:19 +00:00
|
|
|
|
|
|
|
/***** Methods *****/
|
2017-02-12 20:04:05 +00:00
|
|
|
|
|
|
|
function readAvailablePlugins(callback) {
|
|
|
|
fs.readdir("~/.c9/plugins", function(err, list) {
|
|
|
|
if (err) return callback && callback(err);
|
|
|
|
|
|
|
|
var available = [];
|
|
|
|
list.forEach(function(stat) {
|
|
|
|
var name = stat.name;
|
|
|
|
if (!/(directory|folder)$/.test(stat.mime)) return;
|
|
|
|
if (!/[._]/.test(name[0])) available.push(name);
|
|
|
|
});
|
|
|
|
callback && callback(null, available);
|
|
|
|
});
|
|
|
|
}
|
2016-06-26 11:53:19 +00:00
|
|
|
|
2017-01-30 11:32:54 +00:00
|
|
|
function createNewPlugin(template) {
|
2016-06-26 11:53:19 +00:00
|
|
|
if (!template)
|
|
|
|
template = "c9.ide.default";
|
2017-02-26 20:21:10 +00:00
|
|
|
|
2017-03-14 12:17:29 +00:00
|
|
|
var tarSourcePath = join("templates", template + ".tar.gz");
|
2017-02-26 20:21:10 +00:00
|
|
|
var url = staticPrefix + "/" + tarSourcePath;
|
2016-06-26 11:53:19 +00:00
|
|
|
if (!url.match(/^http/))
|
|
|
|
url = location.origin + url;
|
|
|
|
|
2017-01-30 11:32:54 +00:00
|
|
|
function getPath(callback, i) {
|
2016-06-26 11:53:19 +00:00
|
|
|
i = i || 0;
|
|
|
|
var path = join("~", ".c9/plugins/", template + (i ? "." + i : ""));
|
2017-01-30 11:32:54 +00:00
|
|
|
fs.exists(path, function(exists) {
|
|
|
|
if (exists) return getPath(callback, i + 1);
|
2016-06-26 11:53:19 +00:00
|
|
|
callback(null, path);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-01-30 11:32:54 +00:00
|
|
|
function handleError(err) {
|
2016-06-26 11:53:19 +00:00
|
|
|
showError("Could not create plugin.");
|
|
|
|
console.error(err);
|
|
|
|
}
|
|
|
|
|
2017-01-30 11:32:54 +00:00
|
|
|
getPath(function(err, path) {
|
2016-06-26 11:53:19 +00:00
|
|
|
if (err)
|
|
|
|
return handleError(err);
|
|
|
|
|
|
|
|
var pluginsDir = join("~", ".c9/plugins/_/");
|
|
|
|
var pluginsDirAbsolute = pluginsDir.replace(/^~/, c9.home);
|
|
|
|
var tarPath = join(pluginsDir, template + ".tar.gz");
|
|
|
|
var tarPathAbsolute = tarPath.replace(/^~/, c9.home);
|
|
|
|
|
|
|
|
// Download tar file with template for plugin
|
|
|
|
proc.execFile("bash", {
|
|
|
|
args: ["-c", [
|
|
|
|
// using mkdirp since "--create-dirs" is broken on windows
|
|
|
|
"mkdir", "-p", util.escapeShell(dirname(tarPathAbsolute)), ";",
|
2017-02-26 20:21:10 +00:00
|
|
|
].concat(
|
|
|
|
c9.sourceDir
|
|
|
|
? [ "cp", util.escapeShell(c9.sourceDir + "/plugins/c9.ide.plugins/" + tarSourcePath),
|
|
|
|
util.escapeShell(tarPathAbsolute) ]
|
2017-05-01 14:46:15 +00:00
|
|
|
: [ "curl", "-L",
|
|
|
|
(architectApp.services.onlinedev_helper ? "-k" : ""), // ignore certificate errors in dev mode
|
|
|
|
util.escapeShell(url),
|
|
|
|
"-o", util.escapeShell(tarPathAbsolute)
|
|
|
|
].filter(Boolean)
|
2017-02-26 20:21:10 +00:00
|
|
|
).join(" ")]
|
2017-01-30 11:32:54 +00:00
|
|
|
}, function(err, stderr, stdout) {
|
2016-06-26 11:53:19 +00:00
|
|
|
if (err)
|
|
|
|
return handleError(err);
|
|
|
|
|
|
|
|
// Untar tar file
|
|
|
|
proc.execFile("bash", {
|
|
|
|
args: ["-c", ["tar", "-zxvf", util.escapeShell(tarPath), "-C", util.escapeShell(pluginsDirAbsolute)].join(" ")]
|
2017-01-30 11:32:54 +00:00
|
|
|
}, function(err, stderr, stdout) {
|
2016-06-26 11:53:19 +00:00
|
|
|
if (err)
|
|
|
|
return handleError(err);
|
|
|
|
|
|
|
|
// Move template to the right folder
|
|
|
|
var dirPath = join(dirname(tarPath), template);
|
2017-01-30 11:32:54 +00:00
|
|
|
fs.rename(dirPath, path, function(err) {
|
2016-06-26 11:53:19 +00:00
|
|
|
if (err)
|
|
|
|
return handleError(err);
|
|
|
|
|
|
|
|
// Remove .tar.gz
|
2017-01-30 11:32:54 +00:00
|
|
|
fs.unlink(tarPath, function() {
|
2017-05-03 20:55:20 +00:00
|
|
|
// using this to allow reloading the tree
|
|
|
|
var tree = architectApp.services["tree"];
|
|
|
|
var favs = architectApp.services["tree.favorites"];
|
2016-06-26 11:53:19 +00:00
|
|
|
|
|
|
|
// Add plugin to favorites
|
|
|
|
favs.addFavorite(dirname(pluginsDir), "plugins");
|
|
|
|
|
|
|
|
// Select and expand the folder of the plugin
|
|
|
|
tree.expandAndSelect(path);
|
2017-02-26 20:21:10 +00:00
|
|
|
|
2017-05-01 14:46:15 +00:00
|
|
|
readAvailablePlugins(function(e) {
|
|
|
|
emit("change");
|
|
|
|
});
|
2016-06-26 11:53:19 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-05-01 14:46:15 +00:00
|
|
|
function reload(nodes, mode) {
|
2017-04-04 16:35:49 +00:00
|
|
|
var reloadLast = [];
|
2017-03-26 20:29:41 +00:00
|
|
|
nodes.forEach(function(node) {
|
2017-04-04 16:35:49 +00:00
|
|
|
var id;
|
2017-03-26 20:29:41 +00:00
|
|
|
if (node.packageConfig) {
|
2017-04-04 16:35:49 +00:00
|
|
|
var config = node.packageConfig;
|
2017-03-26 20:29:41 +00:00
|
|
|
if (!mode)
|
2017-04-04 16:35:49 +00:00
|
|
|
unloadPackage(config.name);
|
2017-03-26 20:29:41 +00:00
|
|
|
if (mode != false)
|
2017-04-04 16:35:49 +00:00
|
|
|
loadPackage(config.filePath || config.url);
|
|
|
|
id = config.name;
|
2017-03-26 20:29:41 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!mode)
|
|
|
|
unloadPlugins({ path: node.path });
|
|
|
|
if (mode != false)
|
|
|
|
loadPlugins({ path: node.path });
|
2017-04-04 16:35:49 +00:00
|
|
|
id = node.path;
|
2017-03-26 20:29:41 +00:00
|
|
|
}
|
2017-04-04 16:35:49 +00:00
|
|
|
|
|
|
|
reloadLast.push(id);
|
|
|
|
if (mode == false)
|
|
|
|
disabledPlugins[id] = true;
|
|
|
|
else
|
|
|
|
delete disabledPlugins[id];
|
|
|
|
});
|
2017-05-01 14:46:15 +00:00
|
|
|
return reloadLast;
|
2017-04-04 16:35:49 +00:00
|
|
|
}
|
2017-05-01 14:46:15 +00:00
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
function checkPluginsWithMissingDependencies() {
|
|
|
|
var services = architectApp.services;
|
|
|
|
var plugins = [];
|
|
|
|
getAllPlugins().forEach(function(p) {
|
|
|
|
if (!p.provides.length) return;
|
|
|
|
var packagePath = p.packagePath;
|
|
|
|
var isDisabled = p.provides.every(function(name) {
|
|
|
|
if (!services[name]) return true;
|
|
|
|
if (!services[name].loaded && typeof services[name].load == "function")
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
if (isDisabled && packagePath) {
|
|
|
|
var packageName = packagePath.split("/")[1];
|
|
|
|
if (disabledPlugins[packageName]) return;
|
|
|
|
while (packagePath && !disabledPlugins[packagePath]) {
|
|
|
|
var i = packagePath.lastIndexOf("/");
|
|
|
|
packagePath = packagePath.slice(0, i > 0 ? i : 0);
|
|
|
|
}
|
|
|
|
if (!packagePath) plugins.push(p);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (!plugins.length) return;
|
|
|
|
architectApp.loadAdditionalPlugins(plugins, function(err) {
|
|
|
|
if (err) return showError(err);
|
2017-03-26 20:29:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
function getAllPlugins(includeDisabled) {
|
2017-02-12 20:04:05 +00:00
|
|
|
var config = architectApp.config;
|
2017-03-14 12:17:29 +00:00
|
|
|
Object.keys(packages).forEach(function(n) {
|
|
|
|
if (packages[n] && packages[n].c9 && packages[n].c9.plugins)
|
|
|
|
config = config.concat(packages[n].c9.plugins);
|
|
|
|
});
|
2017-04-04 16:35:49 +00:00
|
|
|
return includeDisabled ? config : config.filter(function(x) {
|
2017-03-14 12:17:29 +00:00
|
|
|
return x.consumes && x.provides;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function addAllDependents(plugins) {
|
|
|
|
var config = getAllPlugins();
|
2017-02-12 20:04:05 +00:00
|
|
|
var level = 0;
|
|
|
|
do {
|
|
|
|
level++;
|
|
|
|
var changed = false;
|
2017-03-14 12:17:29 +00:00
|
|
|
var packageNames = Object.keys(plugins);
|
2017-02-12 20:04:05 +00:00
|
|
|
config.forEach(function(x) {
|
|
|
|
packageNames.forEach(function(p) {
|
|
|
|
if (x.consumes.indexOf(p) != -1) {
|
|
|
|
x.provides.forEach(function(name) {
|
2017-03-14 12:17:29 +00:00
|
|
|
if (plugins[name] == null) {
|
2017-02-12 20:04:05 +00:00
|
|
|
changed = true;
|
2017-03-14 12:17:29 +00:00
|
|
|
plugins[name] = level;
|
2017-02-12 20:04:05 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2017-04-02 12:12:51 +00:00
|
|
|
});
|
2017-02-12 20:04:05 +00:00
|
|
|
} while (changed);
|
2017-03-14 12:17:29 +00:00
|
|
|
return plugins;
|
2017-02-12 20:04:05 +00:00
|
|
|
}
|
|
|
|
|
2017-03-14 12:17:29 +00:00
|
|
|
function addAllProviders(plugins) {
|
2017-02-12 20:04:05 +00:00
|
|
|
var serviceToPlugin = architectApp.serviceToPlugin;
|
|
|
|
var level = 0;
|
|
|
|
do {
|
|
|
|
level--;
|
|
|
|
var changed = false;
|
2017-03-14 12:17:29 +00:00
|
|
|
var packageNames = Object.keys(plugins);
|
2017-02-12 20:04:05 +00:00
|
|
|
packageNames.forEach(function(p) {
|
|
|
|
var service = serviceToPlugin[p];
|
|
|
|
if (service && service.consumes) {
|
|
|
|
service.consumes.forEach(function(name) {
|
2017-03-14 12:17:29 +00:00
|
|
|
if (plugins[name] == null) {
|
2017-02-12 20:04:05 +00:00
|
|
|
changed = true;
|
2017-03-14 12:17:29 +00:00
|
|
|
plugins[name] = level;
|
2017-02-12 20:04:05 +00:00
|
|
|
}
|
|
|
|
});
|
2017-04-02 12:12:51 +00:00
|
|
|
}
|
|
|
|
});
|
2017-02-12 20:04:05 +00:00
|
|
|
} while (changed);
|
2017-03-14 12:17:29 +00:00
|
|
|
return plugins;
|
2017-02-12 20:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function unloadPackage(options, callback) {
|
2017-03-26 20:29:41 +00:00
|
|
|
if (Array.isArray(options))
|
|
|
|
return async.map(options, unloadPackage, callback || function() {});
|
|
|
|
var name = typeof options == "object" ? options.name : options;
|
|
|
|
if (packages[name]) {
|
|
|
|
packages[name].enabled = false;
|
|
|
|
unloadPlugins(packages[name].path);
|
|
|
|
emit("disablePackage");
|
2017-04-02 12:12:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 20:04:05 +00:00
|
|
|
function loadPackage(options, callback) {
|
2017-02-26 20:21:10 +00:00
|
|
|
if (Array.isArray(options))
|
2017-03-26 20:29:41 +00:00
|
|
|
return async.map(options, loadPackage, callback || function() {});
|
2017-02-26 20:21:10 +00:00
|
|
|
|
|
|
|
if (typeof options == "string") {
|
2017-04-04 16:35:49 +00:00
|
|
|
if (/^https?:/.test(options)) {
|
|
|
|
options = { url: options };
|
|
|
|
} else if (/^[~\/]/.test(options)) {
|
|
|
|
options = { path: options };
|
|
|
|
} else if (/^[~\/]/.test(options)) {
|
|
|
|
options = { url: require.toUrl(options) };
|
|
|
|
}
|
2017-02-26 20:21:10 +00:00
|
|
|
}
|
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
var url = options.url;
|
|
|
|
|
2017-05-23 16:19:05 +00:00
|
|
|
if (!options.url && options.path) {
|
|
|
|
if (!vfs.connected) {
|
|
|
|
// wait until vfs.url is available
|
|
|
|
return vfs.once("connect", function() {
|
|
|
|
loadPackage(options, callback);
|
|
|
|
});
|
|
|
|
}
|
2017-02-26 20:21:10 +00:00
|
|
|
options.url = vfs.url(options.path);
|
2017-05-23 16:19:05 +00:00
|
|
|
}
|
2017-02-26 20:21:10 +00:00
|
|
|
|
|
|
|
var parts = options.url.split("/");
|
|
|
|
var root = parts.pop();
|
|
|
|
options.url = parts.join("/");
|
|
|
|
|
|
|
|
if (!options.name) {
|
2017-04-04 16:35:49 +00:00
|
|
|
// try to find the name from file name
|
|
|
|
options.name = /^package\.(.*)\.js$|$/.exec(root)[1];
|
|
|
|
// try folder name
|
|
|
|
if (!options.name || options.name == "json")
|
|
|
|
options.name = parts[parts.length - 1];
|
|
|
|
// try parent folder name
|
|
|
|
if (/^(.?build|master|c9build)/.test(options.name))
|
2017-02-26 20:21:10 +00:00
|
|
|
options.name = parts[parts.length - 2];
|
2017-04-04 16:35:49 +00:00
|
|
|
// remove version from the name
|
2017-02-26 20:21:10 +00:00
|
|
|
options.name = options.name.replace(/@.*$/, "");
|
|
|
|
}
|
|
|
|
if (!options.packageName)
|
2017-04-04 16:35:49 +00:00
|
|
|
options.packageName = root.replace(/\.js$/, "");
|
2017-02-26 20:21:10 +00:00
|
|
|
|
|
|
|
if (!options.rootDir)
|
|
|
|
options.rootDir = "plugins";
|
|
|
|
|
2017-03-26 20:29:41 +00:00
|
|
|
var name = options.name;
|
|
|
|
var id = options.rootDir + "/" + name;
|
2017-02-26 20:21:10 +00:00
|
|
|
var pathMappings = {};
|
|
|
|
|
2017-04-08 10:22:30 +00:00
|
|
|
if (packages[name]) packages[name].loading = true;
|
|
|
|
|
2017-03-26 20:29:41 +00:00
|
|
|
unloadPlugins("plugins/" + options.name);
|
|
|
|
|
2017-02-26 20:21:10 +00:00
|
|
|
pathMappings[id] = options.url;
|
|
|
|
requirejs.config({ paths: pathMappings });
|
|
|
|
requirejs.undef(id + "/", true);
|
|
|
|
|
|
|
|
if (/\.js$/.test(root)) {
|
|
|
|
require([options.url + "/" + root], function(json) {
|
2017-04-08 10:22:30 +00:00
|
|
|
json = json || require(id + "/" + options.packageName);
|
|
|
|
if (!json) {
|
|
|
|
var err = new Error("Didn't provide " + id + "/" + options.packageName);
|
|
|
|
return addError("Error loading plugin", err);
|
|
|
|
}
|
2017-05-09 13:51:25 +00:00
|
|
|
if (Array.isArray(json))
|
|
|
|
json = { plugins: json };
|
2017-04-08 10:22:30 +00:00
|
|
|
if (json.name && json.name != name)
|
|
|
|
name = json.name;
|
2017-02-26 20:21:10 +00:00
|
|
|
getPluginsFromPackage(json, callback);
|
|
|
|
}, function(err) {
|
2017-03-26 20:29:41 +00:00
|
|
|
addError("Error loading plugin", err);
|
2017-02-12 20:04:05 +00:00
|
|
|
});
|
2017-02-26 20:21:10 +00:00
|
|
|
}
|
|
|
|
else if (options.path && /\.json$/.test(root)) {
|
2017-03-26 20:29:41 +00:00
|
|
|
fs.readFile(options.path, function(err, value) {
|
|
|
|
if (err) return addError("Error reading " + options.path, err);
|
2017-02-26 20:21:10 +00:00
|
|
|
try {
|
|
|
|
var json = JSON.parse(value);
|
|
|
|
} catch (e) {
|
2017-03-26 20:29:41 +00:00
|
|
|
return addError("Error parsing package.json", e);
|
2017-02-26 20:21:10 +00:00
|
|
|
}
|
2017-03-26 20:29:41 +00:00
|
|
|
json.fromVfs = true;
|
2017-04-04 16:35:49 +00:00
|
|
|
// handle the old format
|
|
|
|
if (!json.c9) loadBundleFiles(json, options);
|
2017-03-14 12:17:29 +00:00
|
|
|
getPluginsFromPackage(json, callback);
|
2017-02-26 20:21:10 +00:00
|
|
|
});
|
|
|
|
}
|
2017-03-26 20:29:41 +00:00
|
|
|
else if (options.url && /\.json$/.test(root)) {
|
2017-09-27 20:18:16 +00:00
|
|
|
require(["text!" + options.url + "/" + root], function(value) {
|
2017-03-26 20:29:41 +00:00
|
|
|
try {
|
|
|
|
var json = JSON.parse(value);
|
|
|
|
} catch (e) {
|
|
|
|
return addError("Error parsing package.json", e);
|
|
|
|
}
|
|
|
|
getPluginsFromPackage(json, callback);
|
|
|
|
}, function(err) {
|
|
|
|
addError("Error loading plugin", err);
|
|
|
|
});
|
|
|
|
}
|
2017-02-26 20:21:10 +00:00
|
|
|
else {
|
2017-03-26 20:29:41 +00:00
|
|
|
callback && callback(new Error("Missing path and url"));
|
|
|
|
}
|
|
|
|
|
|
|
|
function addError(message, err) {
|
|
|
|
if (!packages[name])
|
|
|
|
packages[name] = {};
|
|
|
|
packages[name].filePath = options.path;
|
2017-04-04 16:35:49 +00:00
|
|
|
packages[name].url = url;
|
2017-03-26 20:29:41 +00:00
|
|
|
packages[name].__error = new Error(message + "\n" + err.message);
|
2017-04-08 10:22:30 +00:00
|
|
|
packages[name].loading = false;
|
2017-03-26 20:29:41 +00:00
|
|
|
|
2017-05-01 14:46:15 +00:00
|
|
|
emit("change");
|
2017-03-26 20:29:41 +00:00
|
|
|
|
|
|
|
callback && callback(err);
|
2017-02-26 20:21:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getPluginsFromPackage(json, callback) {
|
|
|
|
var plugins = [];
|
2017-03-26 20:29:41 +00:00
|
|
|
if (json.name != name)
|
|
|
|
json.name = name;
|
2017-04-04 16:35:49 +00:00
|
|
|
var unhandledPlugins = json.c9 && json.c9.plugins || json.plugins;
|
|
|
|
if (unhandledPlugins) {
|
|
|
|
Object.keys(unhandledPlugins).forEach(function(name) {
|
|
|
|
var plugin = unhandledPlugins[name];
|
2017-02-26 20:21:10 +00:00
|
|
|
if (typeof plugin == "string")
|
|
|
|
plugin = { packagePath: plugin };
|
|
|
|
if (!plugin.packagePath)
|
|
|
|
plugin.packagePath = id + "/" + name;
|
|
|
|
plugin.staticPrefix = options.url;
|
|
|
|
plugins.push(plugin);
|
|
|
|
});
|
|
|
|
}
|
2017-03-14 12:17:29 +00:00
|
|
|
|
|
|
|
packages[json.name] = json;
|
2017-03-26 20:29:41 +00:00
|
|
|
json.filePath = options.path;
|
2017-04-04 16:35:49 +00:00
|
|
|
json.url = url;
|
2017-03-26 20:29:41 +00:00
|
|
|
|
2017-03-14 12:17:29 +00:00
|
|
|
if (!json.c9)
|
|
|
|
json.c9 = {};
|
2017-04-04 16:35:49 +00:00
|
|
|
|
2017-03-14 12:17:29 +00:00
|
|
|
json.c9.plugins = plugins;
|
2017-03-26 20:29:41 +00:00
|
|
|
json.enabled = true;
|
|
|
|
json.path = id;
|
2017-03-14 12:17:29 +00:00
|
|
|
|
2017-03-26 20:29:41 +00:00
|
|
|
emit("enablePackage", json);
|
2017-04-08 10:22:30 +00:00
|
|
|
loadPlugins(plugins, function(err, result) {
|
|
|
|
if (err) return addError("Error loading plugins", err);
|
|
|
|
if (packages[name])
|
|
|
|
packages[name].loading = false;
|
2017-05-01 14:46:15 +00:00
|
|
|
emit("change");
|
2017-04-08 10:22:30 +00:00
|
|
|
callback && callback(err, result);
|
|
|
|
});
|
2017-02-26 20:21:10 +00:00
|
|
|
}
|
2017-04-04 16:35:49 +00:00
|
|
|
|
|
|
|
function loadBundleFiles(json, options, callback) {
|
|
|
|
var cwd = dirname(options.path);
|
|
|
|
var resourceHolder = new Plugin();
|
|
|
|
fs.readdir(cwd, function(err, files) {
|
|
|
|
if (err) return callback && callback(err);
|
|
|
|
function forEachFile(dir, fn) {
|
|
|
|
fs.readdir(dir, function(err, files) {
|
|
|
|
if (err) return callback && callback(err);
|
|
|
|
files.forEach(function(stat) {
|
|
|
|
fs.readFile(dir + "/" + stat.name, function(err, value) {
|
|
|
|
if (err) return callback && callback(err);
|
|
|
|
fn(stat.name, value);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function parseHeader(data, filename) {
|
|
|
|
var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim();
|
|
|
|
var info = {};
|
|
|
|
firstLine.split(";").forEach(function(n) {
|
|
|
|
var key = n.split(":");
|
|
|
|
if (key.length != 2)
|
|
|
|
return console.error("Ignoring invalid key " + n + " in " + filename);
|
|
|
|
info[key[0].trim()] = key[1].trim();
|
|
|
|
});
|
|
|
|
info.data = firstLine;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
function addResource(type) {
|
|
|
|
forEachFile(cwd + "/" + type, function(filename, data) {
|
|
|
|
addStaticPlugin(type, options.name, filename, data, plugin);
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function addMode(type) {
|
|
|
|
forEachFile(cwd + "/modes", function(filename, data) {
|
|
|
|
if (/(?:_highlight_rules|_test|_worker|_fold|_behaviou?r)\.js$/.test(filename))
|
|
|
|
return;
|
|
|
|
if (!/\.js$/.test(filename))
|
|
|
|
return;
|
|
|
|
var info = parseHeader(data, cwd + "/modes/" + filename);
|
|
|
|
|
|
|
|
if (!info.caption) info.caption = filename;
|
|
|
|
|
|
|
|
info.type = "modes";
|
|
|
|
info.filename = filename;
|
|
|
|
addStaticPlugin(type, options.name, filename, data, plugin);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
var handlers = {
|
|
|
|
templates: addResource,
|
|
|
|
snippets: addResource,
|
|
|
|
builders: addResource,
|
|
|
|
keymaps: addResource,
|
|
|
|
outline: addResource,
|
|
|
|
runners: addResource,
|
|
|
|
themes: addResource,
|
|
|
|
modes: addMode,
|
|
|
|
};
|
|
|
|
files.forEach(function(stat) {
|
|
|
|
var type = stat.name;
|
|
|
|
if (handlers.hasOwnProperty(type))
|
|
|
|
handlers[type](type);
|
|
|
|
});
|
|
|
|
|
|
|
|
var name = options.name + ".bundle";
|
|
|
|
var bundle = {
|
|
|
|
packagePath: id + "/" + name,
|
|
|
|
consumes: [],
|
|
|
|
provides: [name],
|
|
|
|
setup: function(imports, options, register) {
|
|
|
|
var ret = {};
|
|
|
|
ret[name] = resourceHolder;
|
|
|
|
register(null, ret);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
json.c9.plugins.push(bundle);
|
|
|
|
architectApp.loadAdditionalPlugins([bundle], function() {});
|
|
|
|
});
|
|
|
|
}
|
2017-02-26 20:21:10 +00:00
|
|
|
}
|
|
|
|
|
2017-03-14 12:17:29 +00:00
|
|
|
function loadPlugins(plugins, callback) {
|
2017-04-04 16:35:49 +00:00
|
|
|
if (!Array.isArray(plugins)) {
|
|
|
|
options = plugins;
|
|
|
|
plugins = [];
|
|
|
|
Object.keys(getServiceNamesByPath(options)).forEach(function(n) {
|
|
|
|
var plugin = architectApp.serviceToPlugin[n];
|
|
|
|
if (plugin && plugin.packagePath) {
|
|
|
|
unloadPluginConfig(plugin);
|
|
|
|
plugins.push(plugin);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-02-26 20:21:10 +00:00
|
|
|
architectApp.loadAdditionalPlugins(plugins, function(err) {
|
2017-04-04 16:35:49 +00:00
|
|
|
setTimeout(checkPluginsWithMissingDependencies);
|
2017-03-26 20:29:41 +00:00
|
|
|
callback && callback && callback(err);
|
2017-04-02 12:12:51 +00:00
|
|
|
});
|
|
|
|
}
|
2017-02-12 20:04:05 +00:00
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
function getServiceNamesByPath(options) {
|
2017-03-26 20:29:41 +00:00
|
|
|
var toUnload = Object.create(null);
|
|
|
|
var config = getAllPlugins();
|
|
|
|
function addPath(path) {
|
|
|
|
config.forEach(function(p) {
|
|
|
|
if (!p.packagePath) return;
|
|
|
|
if (p.packagePath.startsWith(path)) {
|
|
|
|
p.provides.forEach(function(name) {
|
|
|
|
toUnload[name] = 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-05-02 18:10:38 +00:00
|
|
|
if (!options) {
|
|
|
|
return toUnload;
|
|
|
|
}
|
2017-03-26 20:29:41 +00:00
|
|
|
if (typeof options == "string") {
|
|
|
|
addPath(options);
|
|
|
|
}
|
|
|
|
if (options.path) {
|
|
|
|
addPath(options.path);
|
|
|
|
}
|
|
|
|
if (options.paths) {
|
|
|
|
options.path.forEach(addPath);
|
|
|
|
}
|
|
|
|
if (options.services) {
|
|
|
|
options.services.forEach(function(name) {
|
|
|
|
toUnload[name] = 0;
|
|
|
|
});
|
|
|
|
}
|
2017-04-04 16:35:49 +00:00
|
|
|
return toUnload;
|
|
|
|
}
|
|
|
|
|
|
|
|
function unloadPlugins(options, callback) {
|
|
|
|
var toUnload = getServiceNamesByPath(options);
|
2017-03-26 20:29:41 +00:00
|
|
|
addAllDependents(toUnload);
|
2017-03-14 12:17:29 +00:00
|
|
|
|
2017-03-26 20:29:41 +00:00
|
|
|
var services = architectApp.services;
|
|
|
|
var serviceToPlugin = architectApp.serviceToPlugin;
|
|
|
|
Object.keys(toUnload).forEach(function(name) {
|
|
|
|
recurUnload(name);
|
|
|
|
});
|
|
|
|
|
|
|
|
function recurUnload(name) {
|
|
|
|
var service = services[name];
|
|
|
|
|
|
|
|
if (!service || !service.loaded)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Find all the dependencies
|
|
|
|
var deps = ext.getDependents(service.name);
|
|
|
|
|
|
|
|
// Unload all the dependencies (and their deps)
|
|
|
|
deps.forEach(function(name) {
|
|
|
|
recurUnload(name);
|
|
|
|
});
|
|
|
|
|
|
|
|
console.log(name);
|
|
|
|
|
|
|
|
// Unload plugin
|
|
|
|
service.unload();
|
|
|
|
|
|
|
|
var pluginConfig = serviceToPlugin[name];
|
|
|
|
if (pluginConfig && toUnload[name] == 0)
|
|
|
|
pluginConfig.__userDisabled = true;
|
|
|
|
}
|
|
|
|
|
2017-05-01 14:46:15 +00:00
|
|
|
emit("change");
|
2017-03-14 12:17:29 +00:00
|
|
|
}
|
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
function unloadPluginConfig(plugin) {
|
|
|
|
var url = requirejs.toUrl(plugin.packagePath, ".js");
|
2017-02-12 20:04:05 +00:00
|
|
|
if (!define.fetchedUrls[url]) return false;
|
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
delete plugin.provides;
|
|
|
|
delete plugin.consumes;
|
|
|
|
delete plugin.setup;
|
2017-02-12 20:04:05 +00:00
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
requirejs.undef(plugin.packagePath);
|
2017-02-12 20:04:05 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-04 16:35:49 +00:00
|
|
|
// TODO optimize this
|
2017-02-12 20:04:05 +00:00
|
|
|
function addStaticPlugin(type, pluginName, filename, data, plugin) {
|
|
|
|
var services = architectApp.services;
|
|
|
|
var path = "plugins/" + pluginName + "/"
|
|
|
|
+ (type == "installer" ? "" : type + "/")
|
|
|
|
+ filename.replace(/\.js$/, "");
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case "builders":
|
|
|
|
data = util.safeParseJson(data, function() {});
|
|
|
|
if (!data) return;
|
|
|
|
if (!services.build.addBuilder) return;
|
|
|
|
|
|
|
|
services.build.addBuilder(filename, data, plugin);
|
|
|
|
break;
|
|
|
|
case "keymaps":
|
|
|
|
data = util.safeParseJson(data, function() {});
|
|
|
|
if (!data) return;
|
|
|
|
if (!services["preferences.keybindings"].addCustomKeymap) return;
|
|
|
|
|
|
|
|
services["preferences.keybindings"].addCustomKeymap(filename, data, plugin);
|
|
|
|
break;
|
|
|
|
case "modes":
|
|
|
|
if (!services.ace) return;
|
|
|
|
var mode = {};
|
|
|
|
var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim();
|
|
|
|
firstLine.split(";").forEach(function(n) {
|
|
|
|
var info = n.split(":");
|
2017-04-04 16:35:49 +00:00
|
|
|
if (info.length != 2) return;
|
2017-02-12 20:04:05 +00:00
|
|
|
mode[info[0].trim()] = info[1].trim();
|
|
|
|
});
|
|
|
|
|
|
|
|
services.ace.defineSyntax({
|
|
|
|
name: path,
|
2017-04-04 16:35:49 +00:00
|
|
|
caption: mode.caption || filename,
|
2017-02-12 20:04:05 +00:00
|
|
|
extensions: (mode.extensions || "").trim()
|
|
|
|
.replace(/\s*,\s*/g, "|").replace(/(^|\|)\./g, "$1")
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "outline":
|
|
|
|
if (!data) return;
|
|
|
|
if (!services.outline.addOutlinePlugin) return;
|
|
|
|
|
|
|
|
services.outline.addOutlinePlugin(path, data, plugin);
|
|
|
|
break;
|
|
|
|
case "runners":
|
|
|
|
data = util.safeParseJson(data, function() {});
|
|
|
|
if (!data) return;
|
|
|
|
|
|
|
|
services.run.addRunner(data.caption || filename, data, plugin);
|
|
|
|
break;
|
|
|
|
case "snippets":
|
|
|
|
services["language.complete"].addSnippet(data, plugin);
|
|
|
|
break;
|
|
|
|
case "themes":
|
|
|
|
services.ace.addTheme(data, plugin);
|
|
|
|
break;
|
|
|
|
case "templates":
|
|
|
|
services.newresource.addFileTemplate(data, plugin);
|
|
|
|
break;
|
|
|
|
case "installer":
|
2017-04-04 16:35:49 +00:00
|
|
|
if (data) {
|
|
|
|
services.installer.createSession(pluginName, data, function(v, o) {
|
|
|
|
require([path], function(fn) {
|
|
|
|
fn(v, o);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
require([path], function(fn) {
|
|
|
|
services.installer.createSession(pluginName, fn.version, function(v, o) {
|
|
|
|
fn(v, o);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2017-02-12 20:04:05 +00:00
|
|
|
default:
|
|
|
|
console.error("Unsupported type", type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
|
|
|
|
/***** Lifecycle *****/
|
|
|
|
|
|
|
|
plugin.on("load", function() {
|
2017-05-09 13:51:25 +00:00
|
|
|
load();
|
2016-06-26 11:53:19 +00:00
|
|
|
});
|
|
|
|
plugin.on("unload", function() {
|
2017-02-26 20:21:10 +00:00
|
|
|
disabledPlugins = null;
|
2017-05-01 14:46:15 +00:00
|
|
|
plugin = null;
|
2016-06-26 11:53:19 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
/***** Register and define API *****/
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
**/
|
|
|
|
plugin.freezePublicAPI({
|
2017-05-01 14:46:15 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
readAvailablePlugins: readAvailablePlugins,
|
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
createNewPlugin: createNewPlugin,
|
2017-05-01 14:46:15 +00:00
|
|
|
|
2017-04-02 12:12:51 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
2017-05-01 14:46:15 +00:00
|
|
|
getAllPlugins: getAllPlugins,
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
addAllDependents: addAllDependents,
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
addAllProviders: addAllProviders,
|
2017-04-02 12:12:51 +00:00
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
2017-02-12 20:04:05 +00:00
|
|
|
unloadPackage: unloadPackage,
|
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
2017-05-01 14:46:15 +00:00
|
|
|
loadPackage: loadPackage,
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
loadPlugins: loadPlugins,
|
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
2017-02-12 20:04:05 +00:00
|
|
|
reload: reload,
|
2017-03-26 20:29:41 +00:00
|
|
|
|
2017-05-01 14:46:15 +00:00
|
|
|
/**
|
|
|
|
*
|
2017-03-26 20:29:41 +00:00
|
|
|
*/
|
2017-05-01 14:46:15 +00:00
|
|
|
getServiceNamesByPath: getServiceNamesByPath,
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
unloadPlugins: unloadPlugins,
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
unloadPluginConfig: unloadPluginConfig,
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
addStaticPlugin: addStaticPlugin,
|
|
|
|
|
|
|
|
|
2017-03-26 20:29:41 +00:00
|
|
|
get packages() { return packages; },
|
2017-05-01 14:46:15 +00:00
|
|
|
get disabledPlugins() { return disabledPlugins; },
|
2016-06-26 11:53:19 +00:00
|
|
|
});
|
|
|
|
|
2017-02-12 20:04:05 +00:00
|
|
|
var shim = new Plugin();
|
2017-04-04 16:35:49 +00:00
|
|
|
shim.addStaticPlugin = addStaticPlugin;
|
2017-02-12 20:04:05 +00:00
|
|
|
|
2016-06-26 11:53:19 +00:00
|
|
|
register(null, {
|
2017-04-02 12:12:51 +00:00
|
|
|
"pluginManager": plugin,
|
2017-02-12 20:04:05 +00:00
|
|
|
"plugin.manager": shim,
|
|
|
|
"plugin.debug": shim,
|
2016-06-26 11:53:19 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|