UNPKG

@edenjs/cli

Version:

Web Application Framework built on Express.js, Redis and RiotJS

282 lines (229 loc) 6.37 kB
// Require environment require('./lib/env'); // Require dependencies const fs = require('fs-extra'); const gulp = require('gulp'); const glob = require('@edenjs/glob'); const deepMerge = require('deepmerge'); const Cp = require('child_process'); const util = require('util'); // Require local dependencies const loader = require('lib/loader'); const parser = require('lib/utilities/parser'); /** * Create Loader class */ class Loader { /** * Construct Loader class */ constructor() { // Bind public methods this.build = this.build.bind(this); this.files = this.files.bind(this); this.restart = this.restart.bind(this); this.merge = util.deprecate(this.merge, 'Please use a custom method instead').bind(this); // Bind private methods this._task = this._task.bind(this); this._watch = this._watch.bind(this); this.server = null; this.serverRestartingPromise = null; this.serverRestartWaiting = false; // Run build this.build(); // Add dev server task gulp.task('server', gulp.series('install', () => { this.restart(true); gulp.task('watch')(); })); // Build default task gulp.task('default', gulp.series('server')); } /** * Build Loader * * This has to be a sync method because gulp won't change core to allow async task loading */ build() { fs.ensureDirSync(`${global.appRoot}/data/cache`); // Glob tasks let done = []; this._locations = global.bundleLocations; // Get files const tasks = glob.sync(this.files('tasks/*.js')); const watchers = []; const installers = []; // Loop tasks for (const rawTask of tasks) { // Load task const task = parser.task(rawTask); // Create task const Task = this._task(task); // Add to default task if (done.indexOf(task.task) === -1) installers.push(task.task); // Push to done done.push(task.task); // Check befores if (task.before) { // Push to dones done = done.concat(task.before); // Remove before from defaults for (const taskBefore of task.before) { // Set index const index = installers.indexOf(taskBefore); // Check defaults if (index > -1) installers.splice(index, 1); } } // Check afters if (task.after) { // Push to dones done = done.concat(task.after); // Remove after from defaults for (const taskAfter of task.after) { // Set index const index = installers.indexOf(taskAfter); // Check defaults if (index > -1) installers.splice(index, 1); } } // Add watch to watchers if (Task.watch) watchers.push(`${task.task}.watch`); } // Create tasks gulp.task('watch', gulp.parallel(...watchers)); gulp.task('install', gulp.series(...installers)); } async _restart() { this.serverRestartingPromise = true; if (this.server !== null) { const deadPromise = new Promise(resolve => this.server.once('exit', resolve)); this.server.kill(); await deadPromise; } this.server = Cp.fork(`${__dirname}/index.js`, ['start']); } /** * Restarts dev server */ restart(create = false) { // Clearly not a production env process.env.NODE_ENV = 'development'; if (this.server === null && !create) { // Nothing to restart, and not initial start return; } if (this.serverRestartWaiting) { // Wait for other queue'd task to run instead of ours, they are just as good return; } (async () => { let didSetWaiting = false; // Already ongoing, lets wait if (this.serverRestartingPromise !== null) { // Note that we set waiting, so we can unset it didSetWaiting = true; // Let other callers know we're already waiting this.serverRestartWaiting = true; // Wait for ongoing task await this.serverRestartingPromise; } // Set ongoing to be our task this.serverRestartingPromise = this._restart(); // Let other callers know we're done waiting if (didSetWaiting) this.serverRestartWaiting = false; // Await our task await this.serverRestartingPromise; // Reset ongoing task promise this.serverRestartingPromise = null; })(); } /** * Writes config file * * @param {string} name * @param {object} obj */ async write(name, obj) { // Write file await fs.writeJson(`${global.appRoot}/data/cache/${name}.json`, obj); } /** * Merges two objects * * @param {object} obj1 * @param {object} obj2 * * @returns {object} */ merge(obj1, obj2) { return deepMerge(obj1, obj2); } files(files) { return loader.getFiles(files, this._locations); } /** * Runs gulp task * * @param {object} task * * @returns {*} * * @private */ _task(task) { // Create task let Task = require(task.file); // eslint-disable-line global-require, import/no-dynamic-require // New task Task = new Task(this); // Create gulp task gulp.task(`${task.task}.run`, () => { // return task return Task.run(Task.watch ? this.files(Task.watch()) : undefined); }); // Create task args let args = []; // Check before if (task.before && task.before.length) { // Push to args args = args.concat(task.before); } // Push actual function args.push(`${task.task}.run`); // Check after if (task.after && task.after.length) { // Push to args args = args.concat(task.after); } // Create task gulp.task(task.task, gulp.series(...args)); // Check watch if (Task.watch) { // Create watch task this._watch(task.task, Task); } // Return task return Task; } /** * Creates watch task * * @param {string} task * @param {*} Task * * @private */ _watch(task, Task) { // Create watch task gulp.task(`${task}.watch`, () => { // return watch return gulp.watch(this.files(Task.watch()), gulp.series(task)); }); } } /** * Export new Loader instance * * @type {Loader} */ module.exports = new Loader();