UNPKG

6.8 kBJavaScriptView Raw
1/*
2Kettle Config Loader
3
4Copyright 2012-2013 OCAD University
5Copyright 2015 Raising the Floor - International
6
7Licensed under the New BSD license. You may not use this file except in
8compliance with this License.
9
10You may obtain a copy of the License at
11https://github.com/fluid-project/kettle/blob/master/LICENSE.txt
12*/
13
14"use strict";
15
16var fluid = require("infusion"),
17 path = require("path"),
18 resolve = require("resolve"),
19 kettle = fluid.registerNamespace("kettle");
20
21fluid.defaults("kettle.config", {
22 gradeNames: ["fluid.component"]
23});
24
25/** Returns a suitable environment value {String} by considering, in the following order
26 * i) the 3rd command line argument, ii) The environment variable
27 * NODE_ENV, iii) the supplied argument {String} if any, This value
28 * is suitable for appearing as the configName field in the options to <code>kettle.config.createDefaults</code> etc.
29 */
30kettle.config.getConfigName = function (outerDefault) {
31 var nodeEnv = process.argv[3] || process.env.NODE_ENV || outerDefault;
32 if (nodeEnv) {
33 fluid.log("Loader running configuration name " + nodeEnv);
34 } else {
35 fluid.fail("No configuration specified in either 1st command line argument, NODE_ENV or internal default");
36 }
37 return nodeEnv;
38};
39
40/** Returns a suitable config base path {String} by considering, in the following order
41 * i) the 2nd command line argument (if it is nonempty and not the string "-") , ii) the supplied argument {String} if any. This value
42 * is suitable for appearing as the configPath field in the options to <code>kettle.config.createDefaults</code> etc.
43 */
44
45kettle.config.getConfigPath = function (outerDefault) {
46 var arg2 = process.argv[2];
47 if (arg2 === "-") {
48 arg2 = null;
49 }
50 var configPath = arg2 || outerDefault;
51 if (!configPath) {
52 fluid.fail("Config path must be specified as 1st command line argument");
53 }
54 return configPath;
55};
56
57kettle.config.initCLI = function (defaultConfigPath, defaultConfigName) {
58 return kettle.config.loadConfig({
59 configPath: kettle.config.getConfigPath(defaultConfigPath),
60 configName: kettle.config.getConfigName(defaultConfigName)
61 });
62};
63
64kettle.config.expectedTopLevel = fluid.arrayToHash(["type", "options", "loadConfigs", "mergeConfigs", "require"]);
65
66kettle.config.checkConfig = function (config, fullPath) {
67 fluid.each(config, function (value, key) {
68 if (!kettle.config.expectedTopLevel[key]) {
69 fluid.fail("Error in config file at path " + fullPath + " - key \"" + key +
70 "\" found, where the only legal options are " +
71 fluid.keys(kettle.config.expectedTopLevel).join(", "));
72 }
73 });
74};
75
76kettle.config.loadSubConfigs = function (prefix, fullPath, configPaths, gradeNames) {
77 configPaths = fluid.makeArray(configPaths);
78 fluid.each(configPaths, function loadConfigFromPath(configPath) {
79 var loadedDefaults;
80 try {
81 loadedDefaults = kettle.config.createDefaultsImpl(prefix, configPath);
82 } catch (e) {
83 e.message += " while loading included configs for config at path " + fullPath;
84 throw e;
85 }
86 if (gradeNames) {
87 gradeNames.push(loadedDefaults);
88 }
89 });
90};
91
92kettle.config.createDefaultsImpl = function (prefix, filePath) {
93 var fullPath;
94 if (filePath.charAt(0) === "%") {
95 fullPath = fluid.module.resolvePath(filePath);
96 prefix = path.dirname(fullPath);
97 } else {
98 var fileName = path.basename(filePath),
99 dirName = path.dirname(filePath);
100 prefix = path.resolve(prefix, dirName);
101 fullPath = path.resolve(prefix, fileName);
102 }
103 var configFile;
104 var testFiles = [fullPath, fullPath + ".json", fullPath + ".json5"];
105 var firstExisting = kettle.firstExistingFile(testFiles);
106 if (!firstExisting) {
107 fluid.fail("Could not find a config file at any of the paths ", testFiles.join(", "));
108 }
109 var parser = kettle.JSON.readFileSync(firstExisting, "reading config file at " + firstExisting);
110 parser.then(function (parsed) {
111 configFile = parsed;
112 }, function (rejection) {
113 fluid.fail(rejection.message);
114 });
115
116 kettle.config.checkConfig(configFile);
117 var gradeNames = ["kettle.config"];
118
119 kettle.config.loadSubConfigs(prefix, firstExisting, configFile.mergeConfigs, gradeNames);
120 kettle.config.loadSubConfigs(prefix, firstExisting, configFile.loadConfigs);
121
122 var requires = fluid.makeArray(configFile.require);
123 fluid.each(requires, function loadRequire(requireId) {
124 if (requireId.charAt(0) === "%") {
125 requireId = fluid.module.resolvePath(requireId);
126 }
127 try {
128 var resolved = resolve.sync(requireId, {
129 basedir: prefix
130 });
131 require(resolved);
132 } catch (e) {
133 e.message += " while trying to resolve require directive for " + requireId + " in config at path " + firstExisting;
134 throw (e);
135 }
136 });
137 configFile.type = configFile.type || "kettle.config." + fluid.allocateGuid();
138 configFile.options = configFile.options || {};
139 configFile.options.gradeNames = gradeNames.concat(fluid.makeArray(
140 configFile.options.gradeNames));
141 fluid.defaults(configFile.type, configFile.options);
142 fluid.log(fluid.logLevel.TRACE, "Created defaults for config type " + configFile.type + ": " + fluid.prettyPrintJSON(configFile.options));
143 return configFile.type;
144};
145
146/** Convert a Kettle "config" as specified by a base path and configName value into a set of component defaults
147 * constituting a runnable Kettle application. This will recursively load as grades any "loadConfigs" and "mergeConfigs" specified in the root
148 * config file, for mergeConfigs, merge their configuration into the root config.
149 * @param options {Object} Contains fields:
150 * configName {String} a value that will be looked up to a "config" file in the supplied
151 * directory by appending the <code>.json</code> suffix
152 * configPath {String} A fully qualified directory name holding config files, or one prefixed with a %module base path reference
153 * @return {String} the global name of a Fluid component which can be invoked to instantiate the Kettle application
154 */
155
156kettle.config.createDefaults = function (options) {
157 return kettle.config.createDefaultsImpl(fluid.module.resolvePath(options.configPath), options.configName);
158};
159
160/** Arguments as for <code>kettle.config.createDefaults</code> only the corresponding Kettle application will be run immediately
161 */
162kettle.config.loadConfig = function (options) {
163 var componentName = kettle.config.createDefaults(options);
164 return fluid.invokeGlobalFunction(componentName);
165};
166