1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | 1x 1x 1x 30x 30x 3x 1x 2x 29x 137x 137x 117x 117x 35x 82x 117x 1x 116x 117x 468x 117x 114x 117x 19x 117x 117x 17x 17x 17x 117x 117x 2x 1x 116x 18x 1x 115x 72x 107x 72x 2x 113x 4x 4x 3x 110x 22x | // parse.js // Reads an schema and retrieves the proper options from it // Errors specifics to this submodule const OptionsError = require('./errors'); const path = require('path'); module.exports = async (schema, arg = {}, env= {}, parent = {}) => { const options = {}; if (typeof arg !== 'object') { if (!schema.__root) { throw new OptionsError('/server/options/notobject'); } arg = { [schema.__root]: arg }; } // Loop each of the defined variables for (let key in schema) { // RETRIEVAL // Make the definition local so it's easier to handle const def = schema[key]; let value; // Skip the control variables such as '__root' if (/^\_\_/.test(key)) continue; // Make sure we are dealing with a valid definition Iif (typeof def !== 'object') { throw new Error('Invalid option definition: ' + JSON.stringify(def)); } // Decide whether to use the argument or not if (def.arg === false) { // No argument expected but one was passed // Should this throw or not? Iif (arg[key]) { console.log((new OptionsError('/server/options/noarg')).message); // throw new OptionsError('/server/options/noarg', { key }); } } else { def.arg = def.arg === true ? key : def.arg || key; } // Decide whether to use the environment or not if (def.env === false) { // No argument expected but one was passed Iif (env[key.toUpperCase()]) { console.log((new OptionsError('/server/options/noenv')).message); // throw new OptionsError('/server/options/noenv', { key }); } } else { def.env = (def.env === true ? key : def.env || key).toUpperCase(); } // List of possibilities, from HIGHER preference to LOWER preference const possible = [ env[def.env], arg[def.arg], parent[def.inherit], def.default ].filter(value => typeof value !== 'undefined'); if (possible.length) { value = possible[0]; } if (def.find) { value = await def.find(value, { arg, env, parent, schema }); } // Extend the base object or user object with new values if these are not set Iif (def.extend && (typeof value === 'undefined' || typeof value === 'object')) { if (typeof value === 'undefined') { value = {}; } Object.assign(value, def.default, value); } // Normalize the "public" pub if (def.file && typeof value === 'string') { Eif (!path.isAbsolute(value)) { value = path.join(process.cwd(), value); } value = path.normalize(value); } Iif (def.clean) { value = def.clean(value, { arg, env, parent, schema }); } // VALIDATION // Validate that it is set if (def.required) { if (typeof value === 'undefined') { throw new OptionsError('/server/options/required', { key }); } // TODO: check that the file and folder exist } if (def.enum) { if (!def.enum.includes(value)) { throw new OptionsError('/server/options/enum', { key, value, possible: def.enum }); } } // Validate the type (only if there's a value) if (def.type && value) { // Parse valid types into a simple array of strings: ['string', 'number'] def.type = (def.type instanceof Array ? def.type : [def.type]) // pulls up the name for primitives such as String, Number, etc .map(one => (one.name ? one.name : one).toLowerCase()); // Make sure it is one of the valid types if (!def.type.includes(typeof value)) { throw new OptionsError('/server/options/type', { key, expected: def.type, received: typeof value, value }); } } if (def.validate) { let ret = def.validate(value, def, options); if (ret instanceof Error) throw ret; if (!ret) throw new OptionsError('/server/options/validate', { key, value }); } options[key] = value; } return options; }; |