[6a3a178] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | var getFieldAsFn = require('./get-field-as-fn');
|
---|
| 4 |
|
---|
| 5 | /**
|
---|
| 6 | * Create a decoder for input sources using the given codec hash
|
---|
| 7 | * @this {object} A loader or compilation
|
---|
| 8 | * @param {Array.<object>} codecs A list of codecs, each with a `decode` function
|
---|
| 9 | * @param {boolean} mustDecode Return an error for a source that is not decoded
|
---|
| 10 | * @returns {function(string):string|Error} A decode function that returns an absolute path or else an Error
|
---|
| 11 | */
|
---|
| 12 | function decodeSourcesWith(codecs, mustDecode) {
|
---|
| 13 | /* jshint validthis:true */
|
---|
| 14 | var context = this;
|
---|
| 15 |
|
---|
| 16 | // get a list of valid decoders
|
---|
| 17 | var candidates = [].concat(codecs)
|
---|
| 18 | .reduce(reduceValidDecoder.bind(null, codecs), []);
|
---|
| 19 |
|
---|
| 20 | /**
|
---|
| 21 | * Attempt to decode the given source path using the previously supplied codecs
|
---|
| 22 | * @param {string} inputSource A source path from a source map
|
---|
| 23 | * @returns {Error|string|undefined} An absolute path if decoded else an error if encountered else undefined
|
---|
| 24 | */
|
---|
| 25 | return function decode(inputSource) {
|
---|
| 26 |
|
---|
| 27 | // attempt all candidates until a match
|
---|
| 28 | for (var i = 0, decoded = null; i < candidates.length && !decoded; i++) {
|
---|
| 29 |
|
---|
| 30 | // call the decoder
|
---|
| 31 | try {
|
---|
| 32 | decoded = candidates[i].decode.call(context, inputSource);
|
---|
| 33 | }
|
---|
| 34 | catch (exception) {
|
---|
| 35 | return getNamedError(exception);
|
---|
| 36 | }
|
---|
| 37 |
|
---|
| 38 | // match implies a return value
|
---|
| 39 | if (decoded) {
|
---|
| 40 |
|
---|
| 41 | // abstract sources cannot be decoded, only validated
|
---|
| 42 | if (candidates[i].abstract) {
|
---|
| 43 | return undefined;
|
---|
| 44 | }
|
---|
| 45 | // non-string implies error
|
---|
| 46 | if (typeof decoded !== 'string') {
|
---|
| 47 | return getNamedError('Decoder returned a truthy value but it is not a string:\n' + decoded);
|
---|
| 48 | }
|
---|
| 49 | // otherwise success
|
---|
| 50 | else {
|
---|
| 51 | return decoded;
|
---|
| 52 | }
|
---|
| 53 | }
|
---|
| 54 | }
|
---|
| 55 |
|
---|
| 56 | // default is undefined or error
|
---|
| 57 | return mustDecode ? new Error('No viable decoder for source: ' + inputSource) : undefined;
|
---|
| 58 |
|
---|
| 59 | function getNamedError(details) {
|
---|
| 60 | var name = candidates[i].name || '(unnamed)',
|
---|
| 61 | message = [
|
---|
| 62 | 'Decoding with codec: ' + name,
|
---|
| 63 | 'Incoming source: ' + inputSource,
|
---|
| 64 | details && (details.stack ? details.stack : details)
|
---|
| 65 | ]
|
---|
| 66 | .filter(Boolean)
|
---|
| 67 | .join('\n');
|
---|
| 68 | return new Error(message);
|
---|
| 69 | }
|
---|
| 70 | };
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | module.exports = decodeSourcesWith;
|
---|
| 74 |
|
---|
| 75 | function reduceValidDecoder(reduced, codec) {
|
---|
| 76 | var decoder = getFieldAsFn('decode')(codec);
|
---|
| 77 | return decoder ? reduced.concat(codec) : reduced;
|
---|
| 78 | } |
---|