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("fluid-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 */
44kettle.config.getConfigPath = function (outerDefault) {
45 var arg2 = process.argv[2];
46 if (arg2 === "-") {
47 arg2 = null;
48 }
49 var configPath = arg2 || outerDefault;
50 if (!configPath) {
51 fluid.fail("Config path must be specified as 1st command line argument");
52 }
53 return configPath;
54};
55
56kettle.config.initCLI = function (defaultConfigPath, defaultConfigName) {
57 return kettle.config.loadConfig({
58 configPath: kettle.config.getConfigPath(defaultConfigPath),
59 configName: kettle.config.getConfigName(defaultConfigName)
60 });
61};
62
63kettle.config.expectedTopLevel = fluid.arrayToHash(["type", "options", "loadConfigs", "mergeConfigs", "require"]);
64
65kettle.config.checkConfig = function (config, fullPath) {
66 fluid.each(config, function (value, key) {
67 if (!kettle.config.expectedTopLevel[key]) {
68 fluid.fail("Error in config file at path " + fullPath + " - key \"" + key +
69 "\" found, where the only legal options are " +
70 fluid.keys(kettle.config.expectedTopLevel).join(", "));
71 }
72 });
73};
74
75kettle.config.loadSubConfigs = function (prefix, fullPath, configPaths, gradeNames) {
76 configPaths = fluid.makeArray(configPaths);
77 fluid.each(configPaths, function loadConfigFromPath(configPath) {
78 var loadedDefaults;
79 try {
80 loadedDefaults = kettle.config.createDefaultsImpl(prefix, configPath);
81 } catch (e) {
82 e.message += " while loading included configs for config at path " + fullPath;
83 throw e;
84 }
85 if (gradeNames) {
86 gradeNames.push(loadedDefaults);
87 }
88 });
89};
90
91kettle.config.createDefaultsImpl = function (prefix, filePath) {
92 var fullPath;
93 if (filePath.charAt(0) === "%") {
94 fullPath = fluid.module.resolvePath(filePath);
95 prefix = path.dirname(fullPath);
96 } else {
97 var fileName = path.basename(filePath),
98 dirName = path.dirname(filePath);
99 prefix = path.resolve(prefix, dirName);
100 fullPath = path.resolve(prefix, fileName);
101 }
102 var configFile;
103 var testFiles = [fullPath, fullPath + ".json", fullPath + ".json5"];
104 var firstExisting = kettle.firstExistingFile(testFiles);
105 if (!firstExisting) {
106 fluid.fail("Could not find a config file at any of the paths ", testFiles.join(", "));
107 }
108 var parser = kettle.JSON.readFileSync(firstExisting, "reading config file at " + firstExisting);
109 parser.then(function (parsed) {
110 configFile = parsed;
111 }, function (rejection) {
112 fluid.fail(rejection.message);
113 });
114
115 kettle.config.checkConfig(configFile);
116 var gradeNames = ["kettle.config"];
117
118 kettle.config.loadSubConfigs(prefix, firstExisting, configFile.mergeConfigs, gradeNames);
119 kettle.config.loadSubConfigs(prefix, firstExisting, configFile.loadConfigs);
120
121 var requires = fluid.makeArray(configFile.require);
122 fluid.each(requires, function loadRequire(requireId) {
123 if (requireId.charAt(0) === "%") {
124 requireId = fluid.module.resolvePath(requireId);
125 }
126 try {
127 var resolved = resolve.sync(requireId, {
128 basedir: prefix
129 });
130 require(resolved);
131 } catch (e) {
132 e.message += " while trying to resolve require directive for " + requireId + " in config at path " + firstExisting;
133 throw (e);
134 }
135 });
136 configFile.type = configFile.type || "kettle.config." + fluid.allocateGuid();
137 configFile.options = configFile.options || {};
138 configFile.options.gradeNames = gradeNames.concat(fluid.makeArray(
139 configFile.options.gradeNames));
140 fluid.defaults(configFile.type, configFile.options);
141 fluid.log(fluid.logLevel.TRACE, "Created defaults for config type " + configFile.type + ": " + fluid.prettyPrintJSON(configFile.options));
142 return configFile.type;
143};
144
145/** Convert a Kettle "config" as specified by a base path and configName value into a set of component defaults
146 * constituting a runnable Kettle application. This will recursively load as grades any "loadConfigs" and "mergeConfigs" specified in the root
147 * config file, for mergeConfigs, merge their configuration into the root config.
148 * @param {Object} options - Contains fields:
149 * configName {String} a value that will be looked up to a "config" file in the supplied
150 * directory by appending the <code>.json</code> suffix
151 * configPath {String} A fully qualified directory name holding config files, or one prefixed with a %module base path reference
152 * @return {String} The global name of a Fluid component which can be invoked to instantiate the Kettle application
153 */
154kettle.config.createDefaults = function (options) {
155 return kettle.config.createDefaultsImpl(fluid.module.resolvePath(options.configPath), options.configName);
156};
157
158/* Arguments as for <code>kettle.config.createDefaults</code> only the corresponding Kettle application will be run immediately
159 */
160kettle.config.loadConfig = function (options) {
161 var componentName = kettle.config.createDefaults(options);
162 return fluid.invokeGlobalFunction(componentName);
163};