You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
2.2 KiB
111 lines
2.2 KiB
'use strict'; |
|
|
|
var normalize = require('value-or-function'); |
|
|
|
var slice = Array.prototype.slice; |
|
|
|
function createResolver(config, options) { |
|
// TODO: should the config object be validated? |
|
config = config || {}; |
|
options = options || {}; |
|
|
|
var resolver = { |
|
resolve: resolve, |
|
}; |
|
|
|
|
|
// Keep constants separately |
|
var constants = {}; |
|
|
|
function resolveConstant(key) { |
|
if (constants.hasOwnProperty(key)) { |
|
return constants[key]; |
|
} |
|
|
|
var definition = config[key]; |
|
// Ignore options that are not defined |
|
if (!definition) { |
|
return; |
|
} |
|
|
|
var option = options[key]; |
|
|
|
if (option != null) { |
|
if (typeof option === 'function') { |
|
return; |
|
} |
|
option = normalize.call(resolver, definition.type, option); |
|
if (option != null) { |
|
constants[key] = option; |
|
return option; |
|
} |
|
} |
|
|
|
var fallback = definition.default; |
|
if (option == null && typeof fallback !== 'function') { |
|
constants[key] = fallback; |
|
return fallback; |
|
} |
|
} |
|
|
|
|
|
// Keep requested keys to detect (and disallow) recursive resolution |
|
var stack = []; |
|
|
|
function resolve(key) { |
|
var option = resolveConstant(key); |
|
if (option != null) { |
|
return option; |
|
} |
|
|
|
var definition = config[key]; |
|
// Ignore options that are not defined |
|
if (!definition) { |
|
return; |
|
} |
|
|
|
if (stack.indexOf(key) >= 0) { |
|
throw new Error('Recursive resolution denied.'); |
|
} |
|
|
|
option = options[key]; |
|
var fallback = definition.default; |
|
var appliedArgs = slice.call(arguments, 1); |
|
var args = [definition.type, option].concat(appliedArgs); |
|
|
|
function toResolve() { |
|
stack.push(key); |
|
var option = normalize.apply(resolver, args); |
|
|
|
if (option == null) { |
|
option = fallback; |
|
if (typeof option === 'function') { |
|
option = option.apply(resolver, appliedArgs); |
|
} |
|
} |
|
|
|
return option; |
|
} |
|
|
|
function onResolve() { |
|
stack.pop(); |
|
} |
|
|
|
return tryResolve(toResolve, onResolve); |
|
} |
|
|
|
|
|
return resolver; |
|
} |
|
|
|
|
|
function tryResolve(toResolve, onResolve) { |
|
try { |
|
return toResolve(); |
|
} finally { |
|
onResolve(); |
|
} |
|
} |
|
|
|
|
|
module.exports = createResolver;
|
|
|