alchemymvc 1.3.22 → 1.4.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/lib/app/behaviour/publishable_behaviour.js +5 -5
  4. package/lib/app/behaviour/revision_behaviour.js +96 -59
  5. package/lib/app/behaviour/sluggable_behaviour.js +14 -14
  6. package/lib/app/conduit/electron_conduit.js +9 -9
  7. package/lib/app/conduit/http_conduit.js +13 -13
  8. package/lib/app/conduit/loopback_conduit.js +15 -15
  9. package/lib/app/conduit/socket_conduit.js +43 -43
  10. package/lib/app/config/routes.js +26 -0
  11. package/lib/app/controller/00-default_app_controller.js +21 -0
  12. package/lib/app/controller/alchemy_info_controller.js +12 -12
  13. package/lib/app/datasource/mongo_datasource.js +27 -18
  14. package/lib/app/element/00-default_app_element.js +19 -0
  15. package/lib/app/element/time_ago.js +5 -5
  16. package/lib/app/helper/00-default_app_helper.js +11 -0
  17. package/lib/app/helper/alchemy_helper.js +22 -22
  18. package/lib/app/helper/backed_map.js +1 -1
  19. package/lib/app/helper/breadcrumb.js +10 -10
  20. package/lib/app/helper/client_collection.js +3 -3
  21. package/lib/app/helper/cron.js +29 -29
  22. package/lib/app/helper/enum_values.js +6 -6
  23. package/lib/app/helper/pagination_helper.js +36 -36
  24. package/lib/app/helper/router_helper.js +35 -35
  25. package/lib/app/helper/socket_helper.js +57 -57
  26. package/lib/app/helper/syncable.js +62 -62
  27. package/lib/app/helper_component/paginate_component.js +9 -9
  28. package/lib/app/helper_controller/component.js +1 -1
  29. package/lib/app/helper_controller/conduit.js +31 -31
  30. package/lib/app/helper_controller/controller.js +54 -39
  31. package/lib/app/helper_datasource/00-nosql_datasource.js +624 -70
  32. package/lib/app/helper_datasource/05-fallback_datasource.js +10 -10
  33. package/lib/app/helper_datasource/idb_datasource.js +6 -6
  34. package/lib/app/helper_datasource/indexed_db.js +22 -22
  35. package/lib/app/helper_datasource/remote_datasource.js +5 -5
  36. package/lib/app/helper_error/http_error.js +4 -4
  37. package/lib/app/helper_error/model_error.js +2 -2
  38. package/lib/app/helper_error/validation_error.js +12 -12
  39. package/lib/app/helper_field/00-objectid_field.js +7 -7
  40. package/lib/app/helper_field/05-string_field.js +16 -12
  41. package/lib/app/helper_field/06-text_field.js +2 -4
  42. package/lib/app/helper_field/10-number_field.js +9 -12
  43. package/lib/app/helper_field/11-date_field.js +15 -15
  44. package/lib/app/helper_field/15-local_temporal_field.js +10 -10
  45. package/lib/app/helper_field/20-decimal_field.js +8 -9
  46. package/lib/app/helper_field/belongsto_field.js +1 -1
  47. package/lib/app/helper_field/big_int_field.js +8 -8
  48. package/lib/app/helper_field/boolean_field.js +9 -11
  49. package/lib/app/helper_field/datetime_field.js +3 -3
  50. package/lib/app/helper_field/enum_field.js +13 -8
  51. package/lib/app/helper_field/fixed_decimal_field.js +6 -7
  52. package/lib/app/helper_field/geopoint_field.js +9 -10
  53. package/lib/app/helper_field/habtm_field.js +3 -3
  54. package/lib/app/helper_field/hasoneparent_field.js +1 -1
  55. package/lib/app/helper_field/html_field.js +2 -4
  56. package/lib/app/helper_field/integer_field.js +8 -11
  57. package/lib/app/helper_field/local_date_field.js +5 -5
  58. package/lib/app/helper_field/local_date_time_field.js +5 -5
  59. package/lib/app/helper_field/local_time_field.js +5 -5
  60. package/lib/app/helper_field/mixed_field.js +5 -5
  61. package/lib/app/helper_field/object_field.js +6 -6
  62. package/lib/app/helper_field/password_field.js +3 -3
  63. package/lib/app/helper_field/regexp_field.js +7 -9
  64. package/lib/app/helper_field/schema_field.js +91 -88
  65. package/lib/app/helper_field/settings_field.js +92 -0
  66. package/lib/app/helper_field/time_field.js +6 -6
  67. package/lib/app/helper_field/url_field.js +2 -4
  68. package/lib/app/helper_model/00-base_criteria.js +662 -0
  69. package/lib/app/helper_model/05-criteria_expressions.js +605 -0
  70. package/lib/app/helper_model/10-model_criteria.js +1182 -0
  71. package/lib/app/helper_model/data_provider.js +2 -2
  72. package/lib/app/helper_model/document.js +103 -92
  73. package/lib/app/helper_model/document_list.js +14 -14
  74. package/lib/app/helper_model/field_config.js +11 -11
  75. package/lib/app/helper_model/field_set.js +17 -17
  76. package/lib/app/helper_model/model.js +201 -123
  77. package/lib/app/helper_model/remote_data_provider.js +2 -2
  78. package/lib/app/helper_validator/00_validator.js +16 -16
  79. package/lib/app/helper_validator/not_empty_validator.js +9 -9
  80. package/lib/app/model/00-default_app_model.js +18 -0
  81. package/lib/app/model/05-system_model.js +27 -0
  82. package/lib/app/model/{alchemy_migration_model.js → system_migration_model.js} +4 -4
  83. package/lib/app/model/system_setting_model.js +154 -0
  84. package/lib/app/model/{alchemy_task_history_model.js → system_task_history_model.js} +7 -7
  85. package/lib/app/model/{alchemy_task_model.js → system_task_model.js} +11 -11
  86. package/lib/bootstrap.js +22 -312
  87. package/lib/class/accumulator.js +5 -5
  88. package/lib/class/behaviour.js +5 -5
  89. package/lib/class/component.js +3 -3
  90. package/lib/class/conduit.js +152 -150
  91. package/lib/class/controller.js +42 -42
  92. package/lib/class/datasource.js +74 -79
  93. package/lib/class/document.js +74 -95
  94. package/lib/class/document_list.js +5 -5
  95. package/lib/class/element.js +17 -17
  96. package/lib/class/error.js +3 -3
  97. package/lib/class/field.js +169 -91
  98. package/lib/class/field_value.js +6 -6
  99. package/lib/class/helper.js +3 -3
  100. package/lib/class/inode.js +17 -17
  101. package/lib/class/inode_dir.js +12 -12
  102. package/lib/class/inode_file.js +26 -26
  103. package/lib/class/inode_list.js +4 -4
  104. package/lib/class/migration.js +4 -4
  105. package/lib/class/model.js +146 -165
  106. package/lib/class/path_definition.js +22 -22
  107. package/lib/class/path_evaluator.js +5 -5
  108. package/lib/class/path_param_definition.js +7 -7
  109. package/lib/class/plugin.js +312 -0
  110. package/lib/class/postponement.js +29 -29
  111. package/lib/class/reciprocal.js +8 -8
  112. package/lib/class/route.js +33 -33
  113. package/lib/class/router.js +73 -73
  114. package/lib/class/schema.js +21 -21
  115. package/lib/class/schema_client.js +105 -82
  116. package/lib/class/session.js +63 -29
  117. package/lib/class/session_scene.js +4 -4
  118. package/lib/class/sitemap.js +16 -16
  119. package/lib/class/task.js +39 -39
  120. package/lib/class/task_service.js +43 -47
  121. package/lib/{init → core}/alchemy.js +411 -373
  122. package/lib/{init/functions.js → core/alchemy_functions.js} +113 -91
  123. package/lib/core/alchemy_load_functions.js +715 -0
  124. package/lib/core/base.js +50 -62
  125. package/lib/core/client_alchemy.js +144 -152
  126. package/lib/core/client_base.js +27 -50
  127. package/lib/core/discovery.js +16 -18
  128. package/lib/core/middleware.js +54 -43
  129. package/lib/core/{routing.js → prefix.js} +14 -16
  130. package/lib/core/setting.js +1684 -0
  131. package/lib/core/stage.js +758 -0
  132. package/lib/scripts/create_constants.js +119 -0
  133. package/lib/{init/languages.js → scripts/create_languages.js} +5 -5
  134. package/lib/scripts/create_settings.js +449 -0
  135. package/lib/scripts/create_shared_constants.js +95 -0
  136. package/lib/scripts/create_stages.js +55 -0
  137. package/lib/scripts/init_alchemy.js +51 -0
  138. package/lib/{init/requirements.js → scripts/preload_modules.js} +15 -2
  139. package/lib/scripts/setup_devwatch.js +238 -0
  140. package/lib/stages/00-load_core.js +342 -0
  141. package/lib/stages/05-load_app.js +57 -0
  142. package/lib/stages/10-datasource.js +61 -0
  143. package/lib/stages/15-tasks.js +27 -0
  144. package/lib/stages/20-settings.js +68 -0
  145. package/lib/stages/50-routes.js +218 -0
  146. package/lib/stages/90-server.js +347 -0
  147. package/package.json +5 -7
  148. package/lib/app/helper_model/criteria.js +0 -2294
  149. package/lib/app/helper_model/db_query.js +0 -1488
  150. package/lib/app/routes.js +0 -11
  151. package/lib/core/socket.js +0 -171
  152. package/lib/init/constants.js +0 -158
  153. package/lib/init/devwatch.js +0 -238
  154. package/lib/init/load_functions.js +0 -979
  155. package/lib/stages.js +0 -515
@@ -0,0 +1,715 @@
1
+ let fs = alchemy.use('fs'),
2
+ libpath = alchemy.use('path'),
3
+ LOADED = Symbol('LOADED'),
4
+ _duplicateCheck = {};
5
+
6
+ /**
7
+ * Require the given path.
8
+ * The path follows an app structure.
9
+ *
10
+ * @author Jelle De Loecker <jelle@elevenways.be>
11
+ * @since 1.4.0
12
+ * @version 1.4.0
13
+ *
14
+ * @param {string} dir_path
15
+ * @param {Object} options
16
+ *
17
+ * @return {boolean}
18
+ */
19
+ Alchemy.setMethod(function useAppPath(dir_path, options) {
20
+
21
+ if (!fs.existsSync(dir_path)) {
22
+ return false;
23
+ }
24
+
25
+ if (!options) {
26
+ options = {};
27
+ }
28
+
29
+ if (options.plugin && options.weight == null) {
30
+ options.weight = 15;
31
+ }
32
+
33
+ let context = options?.plugin || this;
34
+
35
+ let server_directories = new Map(),
36
+ helper_directories = new Map(),
37
+ files = new Map();
38
+
39
+ let config_directory,
40
+ asset_directory,
41
+ view_directory,
42
+ public_path,
43
+ root_path;
44
+
45
+ // Loop over all the entries in the app directory
46
+ eachDirectoryEntryStats(dir_path, (file_path, name, stat) => {
47
+
48
+ if (stat.isDirectory()) {
49
+ let corrected_name,
50
+ is_helper = false;
51
+
52
+ if (name == 'helper' || name == 'element') {
53
+ corrected_name = name;
54
+ is_helper = true;
55
+ } else if (name.startsWith('helper_')) {
56
+ corrected_name = name.slice(7);
57
+ is_helper = true;
58
+ } else if (name == 'assets') {
59
+ asset_directory = file_path;
60
+ return;
61
+ } else if (name == 'view') {
62
+ view_directory = file_path;
63
+ return;
64
+ } else if (name == 'public') {
65
+ public_path = file_path;
66
+ return;
67
+ } else if (name == 'root') {
68
+ root_path = file_path;
69
+ return;
70
+ } else if (name == 'config') {
71
+ config_directory = file_path;
72
+ return;
73
+ } else if (name == 'source') {
74
+ return;
75
+ }
76
+
77
+ if (is_helper) {
78
+ helper_directories.set(corrected_name, file_path);
79
+ } else {
80
+ server_directories.set(name, file_path);
81
+ }
82
+ }
83
+
84
+ files.set(name, file_path);
85
+ });
86
+
87
+ // Load the bootstrap file first
88
+ if (files.get('bootstrap.js')) {
89
+
90
+ if (!options.plugin) {
91
+ context.useOnce(files.get('bootstrap.js'));
92
+ }
93
+
94
+ files.delete('bootstrap.js');
95
+ }
96
+
97
+ if (config_directory) {
98
+ context.usePath(config_directory, options);
99
+ }
100
+
101
+ if (options.helpers !== false) {
102
+
103
+ if (helper_directories.get('error')) {
104
+ this.useHelperPath(helper_directories.get('error'));
105
+ helper_directories.delete('error');
106
+ }
107
+
108
+ if (helper_directories.get('datasource')) {
109
+ this.useHelperPath(helper_directories.get('datasource'));
110
+ helper_directories.delete('datasource');
111
+ }
112
+
113
+ if (helper_directories.get('field')) {
114
+ this.useHelperPath(helper_directories.get('field'));
115
+ helper_directories.delete('field');
116
+ }
117
+
118
+ if (helper_directories.get('helper')) {
119
+ this.useHelperPath(helper_directories.get('helper'));
120
+ helper_directories.delete('helper');
121
+ }
122
+ }
123
+
124
+ // Load the server-side code next
125
+ for (let [name, file_path] of server_directories) {
126
+ context.usePath(file_path);
127
+ }
128
+
129
+ if (options.helpers !== false) {
130
+
131
+ if (helper_directories.get('element')) {
132
+ this.useHelperPath(helper_directories.get('element'));
133
+ helper_directories.delete('element');
134
+ }
135
+
136
+ if (helper_directories.get('model')) {
137
+ this.useHelperPath(helper_directories.get('model'));
138
+ helper_directories.delete('model');
139
+ }
140
+
141
+ if (helper_directories.get('document')) {
142
+ this.useHelperPath(helper_directories.get('document'));
143
+ helper_directories.delete('document');
144
+ }
145
+
146
+ if (helper_directories.get('controller')) {
147
+ this.useHelperPath(helper_directories.get('controller'));
148
+ helper_directories.delete('controller');
149
+ }
150
+
151
+ if (helper_directories.get('component')) {
152
+ this.useHelperPath(helper_directories.get('component'));
153
+ helper_directories.delete('component');
154
+ }
155
+
156
+ if (helper_directories.get('validator')) {
157
+ this.useHelperPath(helper_directories.get('validator'));
158
+ helper_directories.delete('validator');
159
+ }
160
+
161
+ for (let [name, file_path] of helper_directories) {
162
+ this.useHelperPath(file_path);
163
+ }
164
+ }
165
+
166
+ if (view_directory && options.views !== false) {
167
+ alchemy.addViewDirectory(view_directory, options.weight);
168
+ }
169
+
170
+ if (asset_directory) {
171
+ // Add the main asset directory
172
+ alchemy.addAssetDirectory(asset_directory, options.weight);
173
+
174
+ if (options.scripts !== false) {
175
+ alchemy.addScriptDirectory(libpath.resolve(asset_directory, 'scripts'), options.weight);
176
+ }
177
+
178
+ if (options.stylesheets !== false) {
179
+ alchemy.addStylesheetDirectory(libpath.resolve(asset_directory, 'stylesheets'), options.weight);
180
+
181
+ // Also add the public folder, so less files in there can also be compiled
182
+ alchemy.addStylesheetDirectory(libpath.resolve(dir_path, 'public'), options.weight);
183
+ }
184
+
185
+ if (options.fonts !== false) {
186
+ alchemy.addFontDirectory(libpath.resolve(asset_directory, 'fonts'), options.weight);
187
+ }
188
+
189
+ if (options.images !== false) {
190
+ alchemy.addImageDirectory(libpath.resolve(asset_directory, 'images'), options.weight);
191
+ }
192
+ }
193
+
194
+ if (public_path && options.public !== false) {
195
+ alchemy.addPublicDirectory(public_path, options.weight);
196
+ }
197
+
198
+ if (root_path && options.root !== false) {
199
+ alchemy.addRootDirectory(root_path, options.weight);
200
+ }
201
+ });
202
+
203
+ /**
204
+ * Require the given helper path
205
+ *
206
+ * @author Jelle De Loecker <jelle@elevenways.be>
207
+ * @since 1.4.0
208
+ * @version 1.4.0
209
+ *
210
+ * @param {string} dir_path
211
+ * @param {Object} options
212
+ *
213
+ * @return {boolean}
214
+ */
215
+ Alchemy.setMethod(function useHelperPath(dir_path, options) {
216
+
217
+ let sub_directories = [];
218
+
219
+ eachDirectoryEntryStats(dir_path, (file_path, name, stat) => {
220
+
221
+ if (stat.isDirectory()) {
222
+ sub_directories.push(file_path);
223
+ return;
224
+ }
225
+
226
+ try {
227
+ alchemy.hawkejs.load(file_path, {
228
+ arguments : 'hawkejs'
229
+ });
230
+ } catch (err) {
231
+ alchemy.printLog('warning', ['Unable to add helper file ' + name + '\n' + String(err), err], {err: err, level: -2});
232
+ alchemy.printLog('warning', ['File was at', file_path], {err: err, level: -2});
233
+ }
234
+ });
235
+
236
+ for (let dir of sub_directories) {
237
+ this.useHelperPath(dir, options);
238
+ }
239
+ });
240
+
241
+ /**
242
+ * Require all the files in the given path.
243
+ *
244
+ * @author Jelle De Loecker <jelle@elevenways.be>
245
+ * @since 1.4.0
246
+ * @version 1.4.0
247
+ *
248
+ * @param {string} dir_path
249
+ * @param {Object} options
250
+ *
251
+ * @return {boolean}
252
+ */
253
+ Alchemy.setMethod(function usePath(dir_path, options) {
254
+
255
+ let recursive = options?.recursive !== false,
256
+ sub_directories = [];
257
+
258
+ let context = options?.plugin || this;
259
+
260
+ eachDirectoryEntryStats(dir_path, (file_path, name, stat) => {
261
+
262
+ if (stat.isDirectory()) {
263
+ if (recursive) {
264
+ sub_directories.push(file_path);
265
+ }
266
+ return;
267
+ }
268
+
269
+ context.useOnce(file_path);
270
+ });
271
+
272
+ if (recursive && sub_directories.length) {
273
+ for (let dir of sub_directories) {
274
+ context.usePath(dir, options);
275
+ }
276
+ }
277
+ });
278
+
279
+ /**
280
+ * Iterate over a directory's content, but only
281
+ *
282
+ * @author Jelle De Loecker <jelle@elevenways.be>
283
+ * @since 1.4.0
284
+ * @version 1.4.0
285
+ *
286
+ * @param {string} path
287
+ * @param {Function} callback
288
+ */
289
+ function eachDirectoryEntryStats(path, callback) {
290
+
291
+ let extension,
292
+ file_path,
293
+ files = fs.readdirSync(path),
294
+ stat;
295
+
296
+ for (let file of files) {
297
+
298
+ // Ignore all the hidden files
299
+ if (file[0] == '.') {
300
+ continue;
301
+ }
302
+
303
+ switch (file) {
304
+ case 'node_modules':
305
+ case 'test':
306
+ case 'empty':
307
+ case 'migrations':
308
+ continue;
309
+ }
310
+
311
+ // Ignore manual files
312
+ if (file.includes('.manual.')) {
313
+ continue;
314
+ }
315
+
316
+ extension = libpath.extname(file).toLowerCase();
317
+
318
+ switch (extension) {
319
+ case '.md':
320
+ case '.json':
321
+ continue;
322
+ }
323
+
324
+ file_path = libpath.resolve(path, file);
325
+ stat = fs.lstatSync(file_path);
326
+
327
+ callback(file_path, file, stat);
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Default _usePath options
333
+ *
334
+ * @author Jelle De Loecker <jelle@elevenways.be>
335
+ * @since 0.5.0
336
+ * @version 0.5.0
337
+ *
338
+ * @type {Object}
339
+ */
340
+ Alchemy.setProperty('default_use_path_options', {
341
+ ignore : false,
342
+ recursive : -1,
343
+ _level : -1,
344
+
345
+ // Load the bootstrap.js file first
346
+ bootstrap : true,
347
+
348
+ // Load the app_ file afterwards
349
+ app : true
350
+ });
351
+
352
+ /**
353
+ * Add an asset directory
354
+ *
355
+ * @author Jelle De Loecker <jelle@elevenways.be>
356
+ * @since 0.2.0
357
+ * @version 0.4.0
358
+ *
359
+ * @param {string} dirPath The path to load
360
+ * @param {number} weight The weighted importance [10]
361
+ */
362
+ Alchemy.setMethod(function addAssetDirectory(dirPath, weight) {
363
+
364
+ if (typeof weight !== 'number') {
365
+ weight = 10;
366
+ }
367
+
368
+ alchemy.shared('asset.directories').push(dirPath, weight);
369
+ });
370
+
371
+ /**
372
+ * Add a scripts directory
373
+ *
374
+ * @author Jelle De Loecker <jelle@elevenways.be>
375
+ * @since 0.2.0
376
+ * @version 0.4.0
377
+ *
378
+ * @param {string} dirPath The path to load
379
+ * @param {number} weight The weighted importance [10]
380
+ */
381
+ Alchemy.setMethod(function addScriptDirectory(dirPath, weight) {
382
+
383
+ if (typeof weight !== 'number') {
384
+ weight = 10;
385
+ }
386
+
387
+ alchemy.shared('script.directories').push(dirPath, weight);
388
+ });
389
+
390
+ /**
391
+ * Add a stylesheets directory
392
+ *
393
+ * @author Jelle De Loecker <jelle@elevenways.be>
394
+ * @since 0.2.0
395
+ * @version 0.4.0
396
+ *
397
+ * @param {string} dirPath The path to load
398
+ * @param {number} weight The weighted importance [10]
399
+ */
400
+ Alchemy.setMethod(function addStylesheetDirectory(dirPath, weight) {
401
+
402
+ if (typeof weight !== 'number') {
403
+ weight = 10;
404
+ }
405
+
406
+ alchemy.shared('stylesheet.directories').push(dirPath, weight);
407
+ });
408
+
409
+ /**
410
+ * Add a font directory
411
+ *
412
+ * @author Jelle De Loecker <jelle@elevenways.be>
413
+ * @since 1.1.3
414
+ * @version 1.1.3
415
+ *
416
+ * @param {string} dirPath The path to load
417
+ * @param {number} weight The weighted importance [10]
418
+ */
419
+ Alchemy.setMethod(function addFontDirectory(dirPath, weight) {
420
+
421
+ if (typeof weight !== 'number') {
422
+ weight = 10;
423
+ }
424
+
425
+ alchemy.shared('font.directories').push(dirPath, weight);
426
+ });
427
+
428
+ /**
429
+ * Add an image directory
430
+ *
431
+ * @author Jelle De Loecker <jelle@elevenways.be>
432
+ * @since 0.2.0
433
+ * @version 0.4.0
434
+ *
435
+ * @param {string} dirPath The path to load
436
+ * @param {number} weight The weighted importance [10]
437
+ */
438
+ Alchemy.setMethod(function addImageDirectory(dirPath, weight) {
439
+
440
+ if (typeof weight !== 'number') {
441
+ weight = 10;
442
+ }
443
+
444
+ alchemy.shared('images.directories').push(dirPath, weight);
445
+ });
446
+
447
+ /**
448
+ * Add a public directory
449
+ *
450
+ * @author Jelle De Loecker <jelle@elevenways.be>
451
+ * @since 0.2.0
452
+ * @version 0.4.0
453
+ *
454
+ * @param {string} dirPath The path to load
455
+ * @param {number} weight The weighted importance [10]
456
+ */
457
+ Alchemy.setMethod(function addPublicDirectory(dirPath, weight) {
458
+
459
+ if (typeof weight !== 'number') {
460
+ weight = 10;
461
+ }
462
+
463
+ alchemy.shared('public.directories').push(dirPath, weight);
464
+ });
465
+
466
+ /**
467
+ * Add a root directory
468
+ *
469
+ * @author Jelle De Loecker <jelle@elevenways.be>
470
+ * @since 0.2.0
471
+ * @version 0.4.0
472
+ *
473
+ * @param {string} dirPath The path to load
474
+ * @param {number} weight The weighted importance [10]
475
+ */
476
+ Alchemy.setMethod(function addRootDirectory(dirPath, weight) {
477
+
478
+ if (typeof weight !== 'number') {
479
+ weight = 10;
480
+ }
481
+
482
+ alchemy.shared('root.directories').push(dirPath, weight);
483
+ });
484
+
485
+ /**
486
+ * Tell hawkejs to use this path to look for views.
487
+ *
488
+ * @author Jelle De Loecker <jelle@elevenways.be>
489
+ * @since 0.0.1
490
+ * @version 0.4.0
491
+ *
492
+ * @param {string} dirPath The path to load
493
+ * @param {number} weight The weighted importance [10]
494
+ */
495
+ Alchemy.setMethod(function addViewDirectory(dirPath, weight) {
496
+
497
+ if (typeof weight !== 'number') {
498
+ weight = 10;
499
+ }
500
+
501
+ alchemy.hawkejs.addViewDirectory(dirPath, weight);
502
+ });
503
+
504
+ /**
505
+ * Prepare a plugin for use.
506
+ * This immediately executes the plugin's bootstrap.js file,
507
+ * but the loading of the app tree happens later.
508
+ *
509
+ * @author Jelle De Loecker <jelle@elevenways.be>
510
+ * @since 0.0.1
511
+ * @version 1.4.0
512
+ *
513
+ * @param {string} name The name of the plugin (which is its path)
514
+ * @param {Object} options Options to pass to the plugin
515
+ *
516
+ * @return {Alchemy.Plugin}
517
+ */
518
+ Alchemy.setMethod(function usePlugin(name, options) {
519
+
520
+ let full_name;
521
+
522
+ // Strip of the "alchemy-" from the name, should it be given
523
+ if (name.startsWith('alchemy-')) {
524
+ full_name = name;
525
+ name = name.slice(8);
526
+ } else {
527
+ // The "full" name starts with "alchemy-"
528
+ full_name = 'alchemy-' + name;
529
+ }
530
+
531
+ // Make sure the plugin hasn't been loaded already
532
+ if (alchemy.plugins[name] != null) {
533
+
534
+ if (options) {
535
+ log.warn('Tried to load plugin "' + name + '" with options twice!');
536
+ }
537
+
538
+ return true;
539
+ }
540
+
541
+ if (options == null) {
542
+ options = {};
543
+ }
544
+
545
+ // Create the possible paths to this plugin
546
+ let possible_paths = [];
547
+
548
+ if (options.path_to_plugin) {
549
+ possible_paths.push(options.path_to_plugin);
550
+ } else {
551
+ // Look for the "alchemy-" path first
552
+ possible_paths.push(alchemy.pathResolve(PATH_ROOT, 'node_modules', full_name));
553
+ possible_paths.push(alchemy.pathResolve(PATH_ROOT, 'node_modules', name));
554
+
555
+ // It's also allowed to be inside the app/plugins folder
556
+ possible_paths.push(alchemy.pathResolve(PATH_APP, 'plugins', full_name));
557
+ possible_paths.push(alchemy.pathResolve(PATH_APP, 'plugins', name));
558
+ }
559
+
560
+ let path_to_plugin,
561
+ is_dir = false;
562
+
563
+ for (let path of possible_paths) {
564
+ try {
565
+ let plugin_stat = fs.lstatSync(path);
566
+ is_dir = plugin_stat.isDirectory() || plugin_stat.isSymbolicLink();
567
+ path_to_plugin = path;
568
+ break;
569
+ } catch (err) {
570
+ // Ignore
571
+ }
572
+ }
573
+
574
+ if (!is_dir) {
575
+ log.error('Could not find ' + JSON.stringify(name) + ' plugin directory');
576
+ return false;
577
+ }
578
+
579
+ let instance = new Classes.Alchemy.Plugin(name, path_to_plugin, options);
580
+
581
+ // Set the given options
582
+ alchemy.plugins[name] = instance;
583
+
584
+ instance.doPreload();
585
+
586
+ return instance;
587
+ });
588
+
589
+ /**
590
+ * If a plugin hasn't been loaded yet, but it is required, die
591
+ *
592
+ * @author Jelle De Loecker <jelle@elevenways.be>
593
+ * @since 0.0.1
594
+ * @version 1.4.0
595
+ *
596
+ * @param {string|Array} names
597
+ * @param {boolean} attempt_require
598
+ */
599
+ Alchemy.setMethod(function requirePlugin(names, attempt_require) {
600
+
601
+ var message,
602
+ missing = '',
603
+ name,
604
+ temp,
605
+ i;
606
+
607
+ if (attempt_require == null) {
608
+ attempt_require = true;
609
+ }
610
+
611
+ if (!Array.isArray(names)) {
612
+ names = [names];
613
+ }
614
+
615
+ for (i = 0; i < names.length; i++) {
616
+
617
+ name = names[i];
618
+
619
+ if (typeof alchemy.plugins[name] === 'undefined') {
620
+
621
+ if (attempt_require) {
622
+ temp = alchemy.usePlugin(name);
623
+
624
+ if (temp) {
625
+ let plugin_stage = STAGES.getStage('load_app.plugins');
626
+
627
+ if (!plugin_stage || plugin_stage.started) {
628
+ // If the plugin stage has already started,
629
+ // manually start this plugin now
630
+ alchemy.startPlugins(name);
631
+ }
632
+ continue;
633
+ }
634
+ }
635
+
636
+ if (missing) {
637
+ missing += ', ';
638
+ }
639
+
640
+ missing += name;
641
+ }
642
+ }
643
+
644
+ if (missing) {
645
+ message = 'These required plugin(s) are missing: ' + missing;
646
+ die(message, {level: 2});
647
+ }
648
+ });
649
+
650
+ /**
651
+ * Load in a file only once
652
+ *
653
+ * @author Jelle De Loecker <jelle@elevenways.be>
654
+ * @since 0.0.1
655
+ * @version 1.1.0
656
+ */
657
+ Alchemy.setMethod(function useOnce(dirPath, options) {
658
+
659
+ if (typeof options == 'undefined') {
660
+ options = {};
661
+ }
662
+
663
+ dirPath = alchemy.pathResolve.apply(null, arguments);
664
+
665
+ if (typeof _duplicateCheck[dirPath] === 'undefined') {
666
+
667
+ // Mainly used for tidying up the unit tests
668
+ if (process.env.NO_ALCHEMY_LOAD_WARNING == 1) {
669
+ options.throwError = false;
670
+ options.silent = true;
671
+ }
672
+
673
+ if (options.client == null) {
674
+ options.client = false;
675
+ }
676
+
677
+ try {
678
+ Blast.require(dirPath, options);
679
+ _duplicateCheck[dirPath] = true;
680
+ //log.verbose('Used file once: ' + dirPath, {level: 1});
681
+ } catch (err) {
682
+
683
+ // Add the path to the file that failed to load,
684
+ // this can be used when it's a syntax error
685
+ // (It's hard to find the cause otherwise)
686
+ err.file_path = dirPath;
687
+
688
+ _duplicateCheck[dirPath] = false;
689
+
690
+ if (options.throwError !== false) {
691
+
692
+ if (options.throwError !== true) {
693
+ let yellow = __Janeway.esc('103;91');
694
+ alchemy.printLog('error', [yellow + '========================='], {err: err, level: -2});
695
+ alchemy.printLog('error', [yellow + ' Failed to load file: '], {err: err, level: -2});
696
+ alchemy.printLog('error', [yellow + ' »', dirPath.split(libpath.sep).last()], {err: err, level: -2});
697
+ alchemy.printLog('error', [yellow + ' In directory: '], {err: err, level: -2});
698
+ alchemy.printLog('error', [yellow + ' »', dirPath.split(libpath.sep).slice(0, -1).join(libpath.sep)], {err: err, level: -2});
699
+ alchemy.printLog('error', [yellow + ' With error: '], {err: err, level: -2});
700
+ alchemy.printLog('error', [yellow + ' »', err], {err: err, level: -2});
701
+ alchemy.printLog('error', [yellow + '========================='], {err: err, level: -2});
702
+ }
703
+
704
+ throw err;
705
+ }
706
+
707
+ if (!options.silent) {
708
+ // @todo: "Failed to use file once..." message doesn't get displayed
709
+ log.error('Failed to use file once: ' + dirPath, {level: 5, err: err, extra: true});
710
+ }
711
+ }
712
+ } else {
713
+ //log.verbose('File not loaded, already used once: ' + dirPath, {level: 1});
714
+ }
715
+ });