pp-parachute
Version:
Airdrop JS Applications
248 lines (221 loc) • 8.7 kB
JavaScript
var Errors = require('././errors');
var Promise = require('bluebird');
var UrlPatternPairing = require('./url_env_pair');
var explode = require('././explosives');
var fs = require('fs');
var path = require('path');
var config = require('./config');
var toArray = require('../util/array').toArray;
var promisify = require('../util/promisify');
var applicationInstances = {};
var servedUrlPatterns = [];
var currentEnvironment = null;
var matchedUrl = null;
var initialize = function () {
var manifest = config.getApplications();
var applicationNames = Object.keys(manifest);
for (var i = 0; i < applicationNames.length; i++) {
var applicationName = applicationNames[i];
var applicationPath = manifest[applicationName];
loadApplication(applicationName, applicationPath);
}
};
var getApplication = function (applicationName) {
return applicationInstances[applicationName];
};
var loadApplication = function (applicationName, applicationPath) {
if (!fs.existsSync(applicationPath)) {
return explode(Errors.ApplicationMissing, applicationName, applicationPath);
}
if (applicationInstances[applicationName]) {
return; //todo maybe explode nicely to notify user application was not loaded?
}
try {
var application = require(applicationPath);
application.getEnvironments().forEach(function (env) {
mapEnvironmentUrlPatterns(env);
});
applicationInstances[applicationName] = application;
} catch (e) {
return explode(e);
}
};
var hasExactUrl = function(url) {
if(!currentEnvironment) return false;
var servedUrls = currentEnvironment.getServedUrls();
for(var i = 0; i < servedUrls.length; i++) {
if(servedUrls[i] === url || servedUrls[i] + '/' === url) return true;
}
return false;
};
var mapEnvironmentUrlPatterns = function (environment) {
environment.getServedUrls().forEach(function (url) {
servedUrlPatterns.push(new UrlPatternPairing(url, environment));
});
};
var setEnvironmentForUrl = function (url) {
currentEnvironment = null;
matchedUrl = null;
for (var i = 0; i < servedUrlPatterns.length; i++) {
var patternPair = servedUrlPatterns[i];
if (patternPair.match(url)) {
matchedUrl = patternPair.pattern;
return (currentEnvironment = patternPair.environment);
}
}
return currentEnvironment;
};
var getCurrentEnvironment = function () {
return currentEnvironment;
};
var getAssetRoot = function () {
return currentEnvironment && currentEnvironment.getAssetRoot();
};
var getViewData = function () {
if (!currentEnvironment) return null;
var viewFile = currentEnvironment.getViewFile();
var engine = path.extname(viewFile).substring(1);
return {
viewFile: path.basename(viewFile, '.' + engine),
viewDirectory: path.dirname(viewFile),
viewEngine: engine
}
};
var getMatchedUrl = function () {
return matchedUrl;
};
var renameApplication = function (appName, newName) {
var application = applicationInstances[appName];
if (!application) return explode(Errors.ApplicationMissingByName, appName);
application.name = newName;
config.renameApplication(appName, newName);
};
var addApplication = function (applicationPath) {
if (!fs.existsSync(applicationPath)) {
return explode(Errors.NoApplicationAtPath, applicationPath);
}
var appAtPath = toArray(applicationInstances).filter(function (app) {
return app.absolutePath + '/parachute.js' === applicationPath;
});
if (appAtPath.length !== 0) {
return console.log('Application already exists -- no problems here');
}
try {
var application = require(applicationPath);
if (applicationInstances[application.name]) {
var name = application.name;
var abspath = applicationInstances[application.name].absolutePath;
return explode(Errors.ApplicationAlreadyExists, name, abspath);
}
application.getEnvironments().forEach(function (env) {
mapEnvironmentUrlPatterns(env);
});
applicationInstances[application.name] = application;
} catch (e) {
return explode(e);
}
config.addApplication(application.name, applicationPath);
};
var removeApplication = function (appName) {
//go through all envs and kill them if they are part of removed application
var removedApplication = applicationInstances[appName];
if (!removedApplication) return explode();
servedUrlPatterns = servedUrlPatterns.filter(function (patternPair) {
return patternPair.environment.application !== removedApplication;
});
removedApplication.shutdown();
config.removeApplication(appName);
};
var buildApplication = function (appName, envName) {
var application = applicationInstances[appName];
if (!application) return explode(Errors.ApplicationMissingByName, appName);
var env = application.getEnvironment(envName);
if (!env) return explode(Errors.EnvironmentMissingByName, appName, envName);
return env.build();
};
var watchApplication = function (appName, envName) {
var application = applicationInstances[appName];
if (!application) return explode(Errors.ApplicationMissingByName, appName);
var env = application.getEnvironment(envName);
if (!env) return explode(Errors.EnvironmentMissing, appName, envName);
return env.watch();
};
var buildForServing = function (req, res, server) {
var originalBuildPromise = currentEnvironment.buildPromise;
var watchPromise = currentEnvironment.watch();
if (originalBuildPromise !== watchPromise) {
var passed;
return watchPromise.then(function (_passed) {
passed = _passed;
var passedHandler = currentEnvironment.getServedBuildSucceededHandler();
var failedHandler = currentEnvironment.getServedBuildFailedHandler();
if (passed && passedHandler) {
return promisify(passedHandler(currentEnvironment, req, res, server));
} else if (!passed && failedHandler) {
return promisify(failedHandler(currentEnvironment, req, res, server));
}
return Promise.resolve();
}).then(function () {
var completedHandler = currentEnvironment.getServedBuildCompletedHandler();
if (!res.headersSent && completedHandler) {
return promisify(completedHandler(currentEnvironment, passed, req, res, server));
}
return Promise.resolve(passed);
});
} else {
return watchPromise;
}
};
var serveCurrentEnvironment = function (req, res, next, server) {
if (!currentEnvironment) {
return Promise.reject("Current Environment is not defined");
}
if (currentEnvironment.buildPromise === null) {
var initializedHandler = currentEnvironment.getServedBuildInitializedHandler();
if (initializedHandler) {
var initialBuildPromise = promisify(initializedHandler(currentEnvironment, req, server));
return initialBuildPromise(currentEnvironment, req, server).finally(function () {
return buildForServing(req, res, server);
});
}
}
return buildForServing(req, res, server);
};
var watchCurrentEnvironment = function () {
return currentEnvironment.watch();
};
//lists all known applications, their paths and their environments
var listApplications = function () {
var appNames = Object.keys(applicationInstances);
for (var i = 0; i < appNames.length; i++) {
var app = applicationInstances[appNames[i]];
console.log(app.name, app.absolutePath, Object.keys(app.environments));
}
};
//only used for testing
var clear = function () {
applicationInstances = {};
servedUrlPatterns = [];
currentEnvironment = null;
matchedUrl = null;
};
initialize();
module.exports = {
loadApplication: loadApplication,
getApplication: getApplication,
setEnvironmentForUrl: setEnvironmentForUrl,
getCurrentEnvironment: getCurrentEnvironment,
getAssetRoot: getAssetRoot,
getViewData: getViewData,
getMatchedUrl: getMatchedUrl,
buildApplication: buildApplication,
watchApplication: watchApplication,
watchCurrentEnvironment: watchCurrentEnvironment,
serveCurrentEnvironment: serveCurrentEnvironment,
listApplications: listApplications,
addApplication: addApplication,
removeApplication: removeApplication,
renameApplication: renameApplication,
clear: clear,
hasExactUrl: hasExactUrl
};