Press n or j to go to the next uncovered block, b, p or k for the previous block.
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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | 27x 27x 27x 1212x 1212x 28x 1184x 10x 1x 9x 1183x 7833x 7833x 7171x 7171x 7171x 6921x 250x 1x 7170x 6955x 215x 7170x 6955x 7170x 28680x 7170x 2896x 7170x 106x 106x 7170x 356x 248x 356x 356x 123x 7170x 234x 7170x 2x 1x 7169x 232x 2x 7167x 1878x 2553x 1878x 2x 7165x 4x 4x 3x 7162x 2891x 1174x 7814x 7814x 2x 1174x | // 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'); // The main function. // - arg: user options from the argument in the main server() function // - env: the environment variables as read from env.js // - parent: if it's a submodule, the global configuration const parse = module.exports = async (schema, arg = {}, env= {}, parent = {}) => { // Fully parsed options will be stored here const options = {}; // For plugins, accept "false" as an option to nuke a full plugin if (arg === false && parent) { return false; } // Accepts a single option instead of an object and it will be mapped to its // root value. Example: server(2000) === server({ port: 2000 }) if (typeof arg !== 'object') { if (!schema.__root) { throw new OptionsError('notobject'); } arg = { [schema.__root]: arg }; } // Loop each of the defined options for (let name in schema) { // RETRIEVAL // Make the definition local so it's easier to handle const def = schema[name]; let value; // Skip the control variables such as '__root' if (/^__/.test(name)) continue; // Make sure we are dealing with a valid schema definition for this option Iif (typeof def !== 'object') { throw new Error('Invalid option definition: ' + JSON.stringify(def)); } // The user defined a function to find the actual value manually Iif (def.find) { value = await def.find({ arg, env, def, parent, schema }); } else { // Use the user-passed option unles explictly told not to if (def.arg !== false) { def.arg = def.arg === true ? name : def.arg || name; } else if (arg[name] && process.env.NODE_ENV === 'test') { throw new OptionsError('noarg', { name }); } // Use the environment variable unless explicitly told not to if (def.env !== false) { def.env = (def.env === true ? name : def.env || name).toUpperCase(); } else Iif (env[name.toUpperCase()] && process.env.NODE_ENV === 'test') { if (!/^win/.test(process.platform) || name !== 'public') { throw new OptionsError('noenv', { name }); } } // Make sure to use the name if we are inheriting with true if (def.env !== false) { def.inherit = (def.inherit === true ? name : def.inherit || name); } // List of possibilities, from HIGHER preference to LOWER preference // Removes the empty one and gets the first one as it has HIGHER 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]; } } // Extend the base object or user object with new values if these are not set if (def.extend && (typeof value === 'undefined' || typeof value === 'object')) { Iif (typeof value === 'undefined') { value = {}; } value = Object.assign({}, def.default, value); } // Normalize the "public" folder or file if ((def.file || def.folder) && typeof value === 'string') { if (!path.isAbsolute(value)) { value = path.join(process.cwd(), value); } value = path.normalize(value); if (def.folder && value[value.length - 1] !== path.sep) { value = value + path.sep; } } // A final hook for the schema to call up on the value if (def.clean) { value = await def.clean(value, { arg, env, parent, def, schema }); } // VALIDATION // Validate that it is set if (def.required) { if (typeof value === 'undefined') { throw new OptionsError('required', { name }); } // TODO: check that the file and folder exist } if (def.enum) { if (!def.enum.includes(value)) { throw new OptionsError('enum', { name, 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('type', { name, expected: def.type, value }); } } if (def.validate) { let ret = def.validate(value, def, options); if (ret instanceof Error) throw ret; if (!ret) throw new OptionsError('validate', { name, value }); } if (typeof value !== 'undefined') { options[name] = value; } } // If the property 'options' exists handle it recursively for (let name in schema) { const def = schema[name]; if (def.options) { options[name] = await parse(def.options, arg[name], env, options); } } return options; }; |