noflo
Version:
Flow-Based Programming environment for JavaScript
422 lines (395 loc) • 13.6 kB
JavaScript
(function() {
var ComponentLoader, EventEmitter, internalSocket, nofloGraph, utils,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
internalSocket = require('./InternalSocket');
nofloGraph = require('./Graph');
utils = require('./Utils');
EventEmitter = require('events').EventEmitter;
ComponentLoader = (function(_super) {
__extends(ComponentLoader, _super);
function ComponentLoader(baseDir, options) {
this.baseDir = baseDir;
this.options = options != null ? options : {};
this.components = null;
this.componentLoaders = [];
this.checked = [];
this.revalidate = false;
this.libraryIcons = {};
this.processing = false;
this.ready = false;
}
ComponentLoader.prototype.getModulePrefix = function(name) {
if (!name) {
return '';
}
if (name === 'noflo') {
return '';
}
if (name[0] === '@') {
name = name.replace(/\@[a-z\-]+\//, '');
}
return name.replace('noflo-', '');
};
ComponentLoader.prototype.getModuleComponents = function(moduleName) {
var cPath, definition, dependency, e, loader, loaderPath, name, prefix, _ref, _ref1, _results;
if (this.checked.indexOf(moduleName) !== -1) {
return;
}
this.checked.push(moduleName);
try {
definition = require("/" + moduleName + "/component.json");
} catch (_error) {
e = _error;
if (moduleName.substr(0, 1) === '/') {
return this.getModuleComponents("noflo-" + (moduleName.substr(1)));
}
return;
}
for (dependency in definition.dependencies) {
this.getModuleComponents(dependency.replace('/', '-'));
}
if (!definition.noflo) {
return;
}
prefix = this.getModulePrefix(definition.name);
if (definition.noflo.icon) {
this.libraryIcons[prefix] = definition.noflo.icon;
}
if (moduleName[0] === '/') {
moduleName = moduleName.substr(1);
}
if (definition.noflo.loader) {
loaderPath = "/" + moduleName + "/" + definition.noflo.loader;
this.componentLoaders.push(loaderPath);
loader = require(loaderPath);
this.registerLoader(loader, function() {});
}
if (definition.noflo.components) {
_ref = definition.noflo.components;
for (name in _ref) {
cPath = _ref[name];
if (cPath.indexOf('.coffee') !== -1) {
cPath = cPath.replace('.coffee', '.js');
}
if (cPath.substr(0, 2) === './') {
cPath = cPath.substr(2);
}
this.registerComponent(prefix, name, "/" + moduleName + "/" + cPath);
}
}
if (definition.noflo.graphs) {
_ref1 = definition.noflo.graphs;
_results = [];
for (name in _ref1) {
cPath = _ref1[name];
_results.push(this.registerGraph(prefix, name, "/" + moduleName + "/" + cPath));
}
return _results;
}
};
ComponentLoader.prototype.listComponents = function(callback) {
if (this.processing) {
this.once('ready', (function(_this) {
return function() {
return callback(null, _this.components);
};
})(this));
return;
}
if (this.components) {
return callback(null, this.components);
}
this.ready = false;
this.processing = true;
return setTimeout((function(_this) {
return function() {
_this.components = {};
_this.getModuleComponents(_this.baseDir);
_this.processing = false;
_this.ready = true;
_this.emit('ready', true);
if (callback) {
return callback(null, _this.components);
}
};
})(this), 1);
};
ComponentLoader.prototype.load = function(name, callback, metadata) {
var component, componentName;
if (!this.ready) {
this.listComponents((function(_this) {
return function(err) {
if (err) {
return callback(err);
}
return _this.load(name, callback, metadata);
};
})(this));
return;
}
component = this.components[name];
if (!component) {
for (componentName in this.components) {
if (componentName.split('/')[1] === name) {
component = this.components[componentName];
break;
}
}
if (!component) {
callback(new Error("Component " + name + " not available with base " + this.baseDir));
return;
}
}
if (this.isGraph(component)) {
if (typeof process !== 'undefined' && process.execPath && process.execPath.indexOf('node') !== -1) {
process.nextTick((function(_this) {
return function() {
return _this.loadGraph(name, component, callback, metadata);
};
})(this));
} else {
setTimeout((function(_this) {
return function() {
return _this.loadGraph(name, component, callback, metadata);
};
})(this), 0);
}
return;
}
return this.createComponent(name, component, metadata, (function(_this) {
return function(err, instance) {
if (err) {
return callback(err);
}
if (!instance) {
callback(new Error("Component " + name + " could not be loaded."));
return;
}
if (name === 'Graph') {
instance.baseDir = _this.baseDir;
}
_this.setIcon(name, instance);
return callback(null, instance);
};
})(this));
};
ComponentLoader.prototype.createComponent = function(name, component, metadata, callback) {
var e, implementation, instance;
implementation = component;
if (typeof implementation === 'string') {
try {
implementation = require(implementation);
} catch (_error) {
e = _error;
return callback(e);
}
}
if (typeof implementation.getComponent === 'function') {
instance = implementation.getComponent(metadata);
} else if (typeof implementation === 'function') {
instance = implementation(metadata);
} else {
callback(new Error("Invalid type " + (typeof implementation) + " for component " + name + "."));
return;
}
if (typeof name === 'string') {
instance.componentName = name;
}
return callback(null, instance);
};
ComponentLoader.prototype.isGraph = function(cPath) {
if (typeof cPath === 'object' && cPath instanceof nofloGraph.Graph) {
return true;
}
if (typeof cPath !== 'string') {
return false;
}
return cPath.indexOf('.fbp') !== -1 || cPath.indexOf('.json') !== -1;
};
ComponentLoader.prototype.loadGraph = function(name, component, callback, metadata) {
var graph, graphImplementation, graphSocket;
graphImplementation = require(this.components['Graph']);
graphSocket = internalSocket.createSocket();
graph = graphImplementation.getComponent(metadata);
graph.loader = this;
graph.baseDir = this.baseDir;
graph.inPorts.graph.attach(graphSocket);
if (typeof name === 'string') {
graph.componentName = name;
}
graphSocket.send(component);
graphSocket.disconnect();
graph.inPorts.remove('graph');
this.setIcon(name, graph);
return callback(null, graph);
};
ComponentLoader.prototype.setIcon = function(name, instance) {
var componentName, library, _ref;
if (!instance.getIcon || instance.getIcon()) {
return;
}
_ref = name.split('/'), library = _ref[0], componentName = _ref[1];
if (componentName && this.getLibraryIcon(library)) {
instance.setIcon(this.getLibraryIcon(library));
return;
}
if (instance.isSubgraph()) {
instance.setIcon('sitemap');
return;
}
instance.setIcon('square');
};
ComponentLoader.prototype.getLibraryIcon = function(prefix) {
if (this.libraryIcons[prefix]) {
return this.libraryIcons[prefix];
}
return null;
};
ComponentLoader.prototype.normalizeName = function(packageId, name) {
var fullName, prefix;
prefix = this.getModulePrefix(packageId);
fullName = "" + prefix + "/" + name;
if (!packageId) {
fullName = name;
}
return fullName;
};
ComponentLoader.prototype.registerComponent = function(packageId, name, cPath, callback) {
var fullName;
fullName = this.normalizeName(packageId, name);
this.components[fullName] = cPath;
if (callback) {
return callback();
}
};
ComponentLoader.prototype.registerGraph = function(packageId, name, gPath, callback) {
return this.registerComponent(packageId, name, gPath, callback);
};
ComponentLoader.prototype.registerLoader = function(loader, callback) {
return loader(this, callback);
};
ComponentLoader.prototype.setSource = function(packageId, name, source, language, callback) {
var e, implementation;
if (!this.ready) {
this.listComponents((function(_this) {
return function(err) {
if (err) {
return callback(err);
}
return _this.setSource(packageId, name, source, language, callback);
};
})(this));
return;
}
if (language === 'coffeescript') {
if (!window.CoffeeScript) {
return callback(new Error('CoffeeScript compiler not available'));
}
try {
source = CoffeeScript.compile(source, {
bare: true
});
} catch (_error) {
e = _error;
return callback(e);
}
} else if (language === 'es6' || language === 'es2015') {
if (!window.babel) {
return callback(new Error('Babel compiler not available'));
}
try {
source = babel.transform(source).code;
} catch (_error) {
e = _error;
return callback(e);
}
}
try {
source = source.replace("require('noflo')", "require('./NoFlo')");
source = source.replace('require("noflo")', 'require("./NoFlo")');
implementation = eval("(function () { var exports = {}; " + source + "; return exports; })()");
} catch (_error) {
e = _error;
return callback(e);
}
if (!(implementation || implementation.getComponent)) {
return callback(new Error('Provided source failed to create a runnable component'));
}
return this.registerComponent(packageId, name, implementation, function() {
return callback(null);
});
};
ComponentLoader.prototype.getSource = function(name, callback) {
var component, componentName, nameParts, path;
if (!this.ready) {
this.listComponents((function(_this) {
return function(err) {
if (err) {
return callback(err);
}
return _this.getSource(name, callback);
};
})(this));
return;
}
component = this.components[name];
if (!component) {
for (componentName in this.components) {
if (componentName.split('/')[1] === name) {
component = this.components[componentName];
name = componentName;
break;
}
}
if (!component) {
return callback(new Error("Component " + name + " not installed"));
}
}
if (typeof component !== 'string') {
return callback(new Error("Can't provide source for " + name + ". Not a file"));
}
nameParts = name.split('/');
if (nameParts.length === 1) {
nameParts[1] = nameParts[0];
nameParts[0] = '';
}
if (this.isGraph(component)) {
nofloGraph.loadFile(component, function(err, graph) {
if (err) {
return callback(err);
}
if (!graph) {
return callback(new Error('Unable to load graph'));
}
return callback(null, {
name: nameParts[1],
library: nameParts[0],
code: JSON.stringify(graph.toJSON()),
language: 'json'
});
});
return;
}
path = window.require.resolve(component);
if (!path) {
return callback(new Error("Component " + name + " is not resolvable to a path"));
}
return callback(null, {
name: nameParts[1],
library: nameParts[0],
code: window.require.modules[path].toString(),
language: utils.guessLanguageFromFilename(component)
});
};
ComponentLoader.prototype.clear = function() {
this.components = null;
this.checked = [];
this.revalidate = true;
this.ready = false;
return this.processing = false;
};
return ComponentLoader;
})(EventEmitter);
exports.ComponentLoader = ComponentLoader;
}).call(this);