UNPKG

3.29 kBJavaScriptView Raw
1/*
2 * Copyright (c) 2018 One Hill Technologies, LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17const assert = require ('assert');
18const path = require ('path');
19
20const Loader = require ('./loader');
21
22const {
23 stat,
24 readdir
25} = require ('fs-extra');
26
27const {
28 forOwn
29} = require ('lodash');
30
31const LegacyListener = require ('./messaging/legacy-listener');
32
33/**
34 * @class ListenerLoader
35 *
36 * Loader class designed for loading application listeners.
37 */
38module.exports = Loader.extend ({
39 /// The target messenger where the listeners are loaded. The messenger
40 /// must support the following interface/methods:
41 ///
42 /// * emit
43 /// * on
44 /// * once
45 ///
46 app: null,
47
48 init () {
49 this._super.call (this, ...arguments);
50
51 assert (!!this.app, "You must define the 'app' property");
52 },
53
54 load (opts) {
55 let {dirname} = opts;
56
57 assert (!!dirname, 'Your options must have a `dirname` property');
58
59 return stat (dirname).then (stats => {
60 return stats.isDirectory () ? readdir (dirname) : {};
61 }).then (eventNames => {
62 // The name of each directory represents the name of an event. Each file
63 // inside the directory is a listener for the event.
64
65 let promises = [];
66 let listeners = {};
67
68 // Load the listeners for each event in parallel.
69
70 eventNames.forEach (eventName => {
71 const eventPath = path.join (dirname, eventName);
72 promises.push (this._loadListenersFromPath (eventPath));
73 });
74
75 // Merge the loaded listeners into a single object.
76
77 return Promise.all (promises).then (results => {
78 eventNames.forEach ((eventName, i) => {
79 let loaded = results[i];
80 listeners[eventName] = loaded;
81
82 forOwn (loaded, (listener, name) => {
83 listener.name = name;
84
85 this.app.on (eventName, listener);
86 })
87 });
88
89 return listeners;
90 });
91 }).catch (err => {
92 if (err.code && err.code === 'ENOENT')
93 return {};
94
95 return Promise.reject (err);
96 });
97 },
98
99 _loadListenersFromPath (eventPath) {
100 const app = this.app;
101
102 return stat (eventPath).then (stats => {
103 if (!stats.isDirectory ())
104 return {};
105
106 let loader = new Loader ();
107
108 return loader.load ({
109 dirname: eventPath,
110 recursive: false,
111 resolve (listener) {
112 // The listener exported from this module is a Listener class. We need to
113 // instantiate the type and store it. Otherwise, we are working with a legacy
114 // listener and need to wrap it in a LegacyListener object.
115 return listener.prototype && !!listener.prototype.handleEvent ? new listener ({app}) : new LegacyListener ({app, listener});
116 }
117 });
118 });
119 }
120});