1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | const { BO, computed } = require ('base-object');
|
18 | const debug = require ('debug') ('blueprint:application-module');
|
19 | const path = require ('path');
|
20 | const assert = require ('assert');
|
21 |
|
22 | const lookup = require ('./-lookup');
|
23 |
|
24 | const { merge, reduce, omit, get } = require ('lodash');
|
25 | const { readFile, readFileSync, statSync } = require ('fs-extra');
|
26 |
|
27 | const Loader = require ('./loader');
|
28 | const ListenerLoader = require ('./listener-loader');
|
29 | const Router = require ('./router');
|
30 |
|
31 | module.exports = BO.extend ({
|
32 |
|
33 | name: null,
|
34 |
|
35 |
|
36 | app: null,
|
37 |
|
38 |
|
39 | modulePath: null,
|
40 |
|
41 |
|
42 | _viewsPath: null,
|
43 |
|
44 |
|
45 | _defaultLoader: new Loader (),
|
46 |
|
47 | _entities: null,
|
48 |
|
49 |
|
50 | _resources: null,
|
51 |
|
52 | viewsPath: computed ({
|
53 | get () {
|
54 | return path.join (this.modulePath, 'views');
|
55 | }
|
56 | }),
|
57 |
|
58 | hasViews: computed ({
|
59 | get () {
|
60 | try {
|
61 | return statSync (this.viewsPath).isDirectory ();
|
62 | }
|
63 | catch (ex) {
|
64 | return false;
|
65 | }
|
66 | }
|
67 | }),
|
68 |
|
69 | assetsPath: computed ({
|
70 | get () { return path.resolve (this.modulePath, 'assets') }
|
71 | }),
|
72 |
|
73 | resources: computed ({
|
74 | get () { return this._resources; }
|
75 | }),
|
76 |
|
77 | init () {
|
78 | this._super.call (this, ...arguments);
|
79 | this._resources = {};
|
80 |
|
81 | assert (!!this.app, "You must define the 'app' property.");
|
82 | assert (!!this.modulePath, "You must define the 'modulePath' property.");
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | this._entities = [
|
89 | { name: 'models'},
|
90 | {
|
91 | name: 'services',
|
92 | opts: {
|
93 | resolve: this._instantiateComponent.bind (this)
|
94 | }
|
95 | },
|
96 | { name: 'listeners', loader: new ListenerLoader ({app: this.app}) },
|
97 | {
|
98 | name: 'controllers',
|
99 | opts: {
|
100 | resolve: this._instantiateComponent.bind (this)
|
101 | }
|
102 | },
|
103 | { name: 'policies'},
|
104 | { name: 'validators'},
|
105 | { name: 'sanitizers'},
|
106 | {
|
107 | name: 'routers',
|
108 | mergeable: false,
|
109 | opts: {
|
110 | resolve (router) {
|
111 | return router.prototype && !!router.prototype.build ? new router () : new Router ({specification: router});
|
112 | }
|
113 | }
|
114 | }
|
115 | ];
|
116 | },
|
117 |
|
118 | _instantiateComponent (Component) {
|
119 | return new Component ({app: this.app});
|
120 | },
|
121 |
|
122 | |
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | configure () {
|
129 | debug (`configuring the application module ${this.modulePath}`);
|
130 |
|
131 | let promise = reduce (this._entities, (promise, entity) => {
|
132 | return promise.then (() => {
|
133 |
|
134 |
|
135 | let {name} = entity;
|
136 | let location = entity.location || name;
|
137 | let rcPath = path.resolve (this.modulePath, location);
|
138 | let opts = merge ({dirname: rcPath}, entity.opts);
|
139 |
|
140 | debug (`loading ${name} in ${rcPath}`);
|
141 | let loader = entity.loader || this._defaultLoader;
|
142 |
|
143 | return loader.load (opts).then (resources => {
|
144 |
|
145 |
|
146 |
|
147 |
|
148 | debug (`merging ${name} into both application module and application`);
|
149 | const mergeable = get (entity, 'mergeable', true);
|
150 |
|
151 | this._resources[name] = merge (this._resources[name] || {}, resources);
|
152 |
|
153 | if (mergeable) {
|
154 | this.app.resources[name] = merge (this.app.resources[name] || {}, resources);
|
155 | }
|
156 | });
|
157 | });
|
158 | }, Promise.resolve ());
|
159 |
|
160 | return promise.then (() => this);
|
161 | },
|
162 |
|
163 | |
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 | lookup (component) {
|
175 | return lookup (this.resources, component);
|
176 | },
|
177 |
|
178 | |
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | asset (filename, opts) {
|
185 | let fullPath = path.resolve (this.assetsPath, filename);
|
186 | return readFile (fullPath, opts);
|
187 | },
|
188 |
|
189 | assetSync (filename, opts) {
|
190 | let fullPath = path.resolve (this.assetsPath, filename);
|
191 | return readFileSync (fullPath, opts);
|
192 | },
|
193 |
|
194 | |
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 | merge (module) {
|
202 | return merge (this._resources, omit (module.resources, ['routers']));
|
203 | }
|
204 | });
|