UNPKG

6.37 kBJavaScriptView Raw
1// Require environment
2require('./lib/env');
3
4// Require dependencies
5const fs = require('fs-extra');
6const gulp = require('gulp');
7const glob = require('@edenjs/glob');
8const deepMerge = require('deepmerge');
9const Cp = require('child_process');
10const util = require('util');
11
12// Require local dependencies
13const loader = require('lib/loader');
14const parser = require('lib/utilities/parser');
15
16/**
17 * Create Loader class
18 */
19class Loader {
20 /**
21 * Construct Loader class
22 */
23 constructor() {
24 // Bind public methods
25 this.build = this.build.bind(this);
26 this.files = this.files.bind(this);
27 this.restart = this.restart.bind(this);
28 this.merge = util.deprecate(this.merge, 'Please use a custom method instead').bind(this);
29
30 // Bind private methods
31 this._task = this._task.bind(this);
32 this._watch = this._watch.bind(this);
33
34 this.server = null;
35 this.serverRestartingPromise = null;
36 this.serverRestartWaiting = false;
37
38 // Run build
39 this.build();
40
41 // Add dev server task
42 gulp.task('server', gulp.series('install', () => {
43 this.restart(true);
44
45 gulp.task('watch')();
46 }));
47
48 // Build default task
49 gulp.task('default', gulp.series('server'));
50 }
51
52 /**
53 * Build Loader
54 *
55 * This has to be a sync method because gulp won't change core to allow async task loading
56 */
57 build() {
58 fs.ensureDirSync(`${global.appRoot}/data/cache`);
59
60 // Glob tasks
61 let done = [];
62
63 this._locations = global.bundleLocations;
64
65 // Get files
66 const tasks = glob.sync(this.files('tasks/*.js'));
67 const watchers = [];
68 const installers = [];
69
70 // Loop tasks
71 for (const rawTask of tasks) {
72 // Load task
73 const task = parser.task(rawTask);
74
75 // Create task
76 const Task = this._task(task);
77
78 // Add to default task
79 if (done.indexOf(task.task) === -1) installers.push(task.task);
80
81 // Push to done
82 done.push(task.task);
83
84 // Check befores
85 if (task.before) {
86 // Push to dones
87 done = done.concat(task.before);
88
89 // Remove before from defaults
90 for (const taskBefore of task.before) {
91 // Set index
92 const index = installers.indexOf(taskBefore);
93
94 // Check defaults
95 if (index > -1) installers.splice(index, 1);
96 }
97 }
98
99 // Check afters
100 if (task.after) {
101 // Push to dones
102 done = done.concat(task.after);
103
104 // Remove after from defaults
105 for (const taskAfter of task.after) {
106 // Set index
107 const index = installers.indexOf(taskAfter);
108
109 // Check defaults
110 if (index > -1) installers.splice(index, 1);
111 }
112 }
113
114 // Add watch to watchers
115 if (Task.watch) watchers.push(`${task.task}.watch`);
116 }
117
118 // Create tasks
119 gulp.task('watch', gulp.parallel(...watchers));
120 gulp.task('install', gulp.series(...installers));
121 }
122
123 async _restart() {
124 this.serverRestartingPromise = true;
125
126 if (this.server !== null) {
127 const deadPromise = new Promise(resolve => this.server.once('exit', resolve));
128 this.server.kill();
129 await deadPromise;
130 }
131
132 this.server = Cp.fork(`${__dirname}/index.js`, ['start']);
133 }
134
135 /**
136 * Restarts dev server
137 */
138 restart(create = false) {
139 // Clearly not a production env
140 process.env.NODE_ENV = 'development';
141
142 if (this.server === null && !create) {
143 // Nothing to restart, and not initial start
144 return;
145 }
146
147 if (this.serverRestartWaiting) {
148 // Wait for other queue'd task to run instead of ours, they are just as good
149 return;
150 }
151
152 (async () => {
153 let didSetWaiting = false;
154
155 // Already ongoing, lets wait
156 if (this.serverRestartingPromise !== null) {
157 // Note that we set waiting, so we can unset it
158 didSetWaiting = true;
159 // Let other callers know we're already waiting
160 this.serverRestartWaiting = true;
161 // Wait for ongoing task
162 await this.serverRestartingPromise;
163 }
164
165 // Set ongoing to be our task
166 this.serverRestartingPromise = this._restart();
167
168 // Let other callers know we're done waiting
169 if (didSetWaiting) this.serverRestartWaiting = false;
170
171 // Await our task
172 await this.serverRestartingPromise;
173
174 // Reset ongoing task promise
175 this.serverRestartingPromise = null;
176 })();
177 }
178
179 /**
180 * Writes config file
181 *
182 * @param {string} name
183 * @param {object} obj
184 */
185 async write(name, obj) {
186 // Write file
187 await fs.writeJson(`${global.appRoot}/data/cache/${name}.json`, obj);
188 }
189
190 /**
191 * Merges two objects
192 *
193 * @param {object} obj1
194 * @param {object} obj2
195 *
196 * @returns {object}
197 */
198 merge(obj1, obj2) {
199 return deepMerge(obj1, obj2);
200 }
201
202 files(files) {
203 return loader.getFiles(files, this._locations);
204 }
205
206 /**
207 * Runs gulp task
208 *
209 * @param {object} task
210 *
211 * @returns {*}
212 *
213 * @private
214 */
215 _task(task) {
216 // Create task
217 let Task = require(task.file); // eslint-disable-line global-require, import/no-dynamic-require
218
219 // New task
220 Task = new Task(this);
221
222 // Create gulp task
223 gulp.task(`${task.task}.run`, () => {
224 // return task
225 return Task.run(Task.watch ? this.files(Task.watch()) : undefined);
226 });
227
228 // Create task args
229 let args = [];
230
231 // Check before
232 if (task.before && task.before.length) {
233 // Push to args
234 args = args.concat(task.before);
235 }
236
237 // Push actual function
238 args.push(`${task.task}.run`);
239
240 // Check after
241 if (task.after && task.after.length) {
242 // Push to args
243 args = args.concat(task.after);
244 }
245
246 // Create task
247 gulp.task(task.task, gulp.series(...args));
248
249 // Check watch
250 if (Task.watch) {
251 // Create watch task
252 this._watch(task.task, Task);
253 }
254
255 // Return task
256 return Task;
257 }
258
259 /**
260 * Creates watch task
261 *
262 * @param {string} task
263 * @param {*} Task
264 *
265 * @private
266 */
267 _watch(task, Task) {
268 // Create watch task
269 gulp.task(`${task}.watch`, () => {
270 // return watch
271 return gulp.watch(this.files(Task.watch()), gulp.series(task));
272 });
273 }
274}
275
276/**
277 * Export new Loader instance
278 *
279 * @type {Loader}
280 */
281module.exports = new Loader();