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.
78 lines
2.4 KiB
78 lines
2.4 KiB
'use strict'; |
|
|
|
var getFieldAsFn = require('./get-field-as-fn'); |
|
|
|
/** |
|
* Create a decoder for input sources using the given codec hash |
|
* @this {object} A loader or compilation |
|
* @param {Array.<object>} codecs A list of codecs, each with a `decode` function |
|
* @param {boolean} mustDecode Return an error for a source that is not decoded |
|
* @returns {function(string):string|Error} A decode function that returns an absolute path or else an Error |
|
*/ |
|
function decodeSourcesWith(codecs, mustDecode) { |
|
/* jshint validthis:true */ |
|
var context = this; |
|
|
|
// get a list of valid decoders |
|
var candidates = [].concat(codecs) |
|
.reduce(reduceValidDecoder.bind(null, codecs), []); |
|
|
|
/** |
|
* Attempt to decode the given source path using the previously supplied codecs |
|
* @param {string} inputSource A source path from a source map |
|
* @returns {Error|string|undefined} An absolute path if decoded else an error if encountered else undefined |
|
*/ |
|
return function decode(inputSource) { |
|
|
|
// attempt all candidates until a match |
|
for (var i = 0, decoded = null; i < candidates.length && !decoded; i++) { |
|
|
|
// call the decoder |
|
try { |
|
decoded = candidates[i].decode.call(context, inputSource); |
|
} |
|
catch (exception) { |
|
return getNamedError(exception); |
|
} |
|
|
|
// match implies a return value |
|
if (decoded) { |
|
|
|
// abstract sources cannot be decoded, only validated |
|
if (candidates[i].abstract) { |
|
return undefined; |
|
} |
|
// non-string implies error |
|
if (typeof decoded !== 'string') { |
|
return getNamedError('Decoder returned a truthy value but it is not a string:\n' + decoded); |
|
} |
|
// otherwise success |
|
else { |
|
return decoded; |
|
} |
|
} |
|
} |
|
|
|
// default is undefined or error |
|
return mustDecode ? new Error('No viable decoder for source: ' + inputSource) : undefined; |
|
|
|
function getNamedError(details) { |
|
var name = candidates[i].name || '(unnamed)', |
|
message = [ |
|
'Decoding with codec: ' + name, |
|
'Incoming source: ' + inputSource, |
|
details && (details.stack ? details.stack : details) |
|
] |
|
.filter(Boolean) |
|
.join('\n'); |
|
return new Error(message); |
|
} |
|
}; |
|
} |
|
|
|
module.exports = decodeSourcesWith; |
|
|
|
function reduceValidDecoder(reduced, codec) { |
|
var decoder = getFieldAsFn('decode')(codec); |
|
return decoder ? reduced.concat(codec) : reduced; |
|
} |