@edenjs/cli
Version:
Web Application Framework built on Express.js and Redis
261 lines (218 loc) • 6.74 kB
text/typescript
// json5
import JSON5 from 'json5';
/**
* Create Daemons Task class
*
* @task daemons
* @parallel
*/
export default class DaemonsTask {
/**
* Construct Daemons Task class
*
* @param {Loader} runner
*/
constructor(cli) {
// Set private variables
this.cli = cli;
// Bind public methods
this.run = this.run.bind(this);
this.watch = this.watch.bind(this);
}
/**
* run in background
*
* @param {*} files
*/
async run(files) {
// run models in background
const threadData = await this.cli.thread(this.thread, {
files,
parser : require.resolve(`${global.edenRoot}/lib/parser`),
appRoot : global.appRoot,
});
// config
const clusters = Array.from((threadData.daemons || []).reduce((accum, daemon) => {
// clusters
const subClusters = Array.isArray(daemon.cluster) ? daemon.cluster : [daemon.cluster];
// daemon clusters
accum.add(...subClusters);
// return accumulator
return accum;
}, new Set(['front', 'back']))).filter((c) => c);
// create matrix
const daemons = threadData.daemons.sort((a, b) => {
// check priority
if ((a.priority || 0) > (b.priority || 0)) return 1;
if ((a.priority || 0) < (b.priority || 0)) return -1;
// return 0
return 0;
}).reduce((accum, daemon) => {
// check daemon
if (!accum[daemon.file]) accum[daemon.file] = daemon;
// return accumulator
return accum;
}, {});
// create actual daemon file
const matrix = clusters.reduce((accum, c) => {
// clusters
// add cluster to accumulator
accum[c] = Object.values(daemons).filter((v) => v.cluster && (v.cluster.includes('all') || v.cluster.includes(c))).map((daemon) => {
// return file
return `
// ${daemon.file} START
exporting['${daemon.file}'] = {
ctrl : require('${daemon.path}'),
data : ${JSON5.stringify(daemon)},
${['hooks', 'events', 'endpoints'].map((type) => {
return ` ${type} : ${JSON5.stringify(threadData[type].filter((item) => item.file === daemon.file).map((item) => {
// new item
const newItem = Object.assign({}, item);
// result
delete newItem.file;
// return item
return newItem;
}).sort((a, b) => {
// check priority
if ((a.priority || 0) > (b.priority || 0)) return 1;
if ((a.priority || 0) < (b.priority || 0)) return -1;
// return 0
return 0;
}))},`;
}).join(' \n')}
};
// ${daemon.file} END
`;
}).join('\n\n// -------------------------\n\n');
// return accum
return accum;
}, {});
// keys
await Promise.all(Object.keys(matrix).map((key) => {
// set cluster imports
this.cli.set(`cluster.${key}.daemons`, `${key}/daemons.js`);
// create indexed matrix
return this.cli.write(`${key}/daemons.js`, `const exporting = {};\n\n${matrix[key]}\n\nmodule.exports = exporting;`);
}));
// Restart server
this.cli.emit('restart');
// show loaded
return `${Object.keys(daemons).length.toLocaleString()} daemons loaded!`;
}
/**
* Run assets task
*
* @param {array} files
*
* @returns {Promise}
*/
async thread(data) {
// Require dependencies
const glob = require('@edenjs/glob');
const deepMerge = require('deepmerge');
// Require local dependencies
const parser = require(data.parser);
// create parse function
const parse = (file, path) => {
// get mount
const hooks = [];
const events = [];
const cluster = file.tags.cluster ? file.tags.cluster.map(c => c.value) : ['back'];
const priority = file.tags.priority ? parseInt(file.tags.priority[0].value, 10) : 10;
const endpoints = [];
// daemon
const daemon = Object.assign({}, file, {
path,
cluster,
priority,
});
// delete
delete daemon.type;
delete daemon.tags;
delete daemon.method;
delete daemon.methods;
// set classes
const daemons = [daemon];
// forEach
file.methods.forEach((method) => {
// combine tags
const combinedTags = deepMerge(file.tags || {}, method.tags);
// parse endpoints
[...(method.tags.endpoint || [])].forEach((tag) => {
// Comply with max-length 100 (TravicCI)
const methodPriority = method.tags.priority;
// create route
const endpoint = Object.assign({
fn : method.method,
all : !!method.tags.all,
file : file.file,
endpoint : (tag.value || '').trim(),
priority : methodPriority ? parseInt(methodPriority[0].value, 10) : priority,
}, parser.acl(combinedTags));
// push endpoint
endpoints.push(endpoint);
});
// parse events
[...(method.tags.on || [])].forEach((tag) => {
// Comply with max-length 100 (TravicCI)
const methodPriority = method.tags.priority;
// create route
const e = Object.assign({
fn : method.method,
all : !!method.tags.all,
file : file.file,
event : (tag.value || '').trim(),
priority : methodPriority ? parseInt(methodPriority[0].value, 10) : priority,
}, parser.acl(combinedTags));
// push event
events.push(e);
});
// parse endpoints
['pre', 'post'].forEach((type) => {
// pre/post
[...(method.tags[type] || [])].forEach((tag) => {
// Comply with max-length 100 (TravicCI)
const methodPriority = method.tags.priority;
// create route
const hook = Object.assign({
type,
fn : method.method,
file : file.file,
hook : (tag.value || '').trim(),
priority : methodPriority ? parseInt(methodPriority[0].value, 10) : priority,
}, parser.acl(combinedTags));
// push hook
hooks.push(hook);
});
});
});
// return daemons
return {
hooks,
events,
daemons,
endpoints,
};
};
// Set config
let config = {};
// run through files
const files = await glob(data.files);
// loop files
files.forEach((file) => {
// parse file
config = deepMerge(config, parse(parser.file(file), file));
});
// return config
return config;
}
/**
* Watch task
*
* @return {string[]}
*/
watch () {
// return find string
return '/daemons/**/*.{js,jsx,ts,tsx}';
}
}