string-format
Version:
String formatting inspired by Python's str.format()
97 lines (85 loc) • 3.08 kB
JavaScript
void function(global) {
'use strict';
// ValueError :: String -> Error
function ValueError(message) {
var err = new Error(message);
err.name = 'ValueError';
return err;
}
// create :: Object -> String,*... -> String
function create(transformers) {
return function(template) {
var args = Array.prototype.slice.call(arguments, 1);
var idx = 0;
var state = 'UNDEFINED';
return template.replace(
/([{}])\1|[{](.*?)(?:!(.+?))?[}]/g,
function(match, literal, _key, xf) {
if (literal != null) {
return literal;
}
var key = _key;
if (key.length > 0) {
if (state === 'IMPLICIT') {
throw ValueError('cannot switch from ' +
'implicit to explicit numbering');
}
state = 'EXPLICIT';
} else {
if (state === 'EXPLICIT') {
throw ValueError('cannot switch from ' +
'explicit to implicit numbering');
}
state = 'IMPLICIT';
key = String(idx);
idx += 1;
}
// 1. Split the key into a lookup path.
// 2. If the first path component is not an index, prepend '0'.
// 3. Reduce the lookup path to a single result. If the lookup
// succeeds the result is a singleton array containing the
// value at the lookup path; otherwise the result is [].
// 4. Unwrap the result by reducing with '' as the default value.
var path = key.split('.');
var value = (/^\d+$/.test(path[0]) ? path : ['0'].concat(path))
.reduce(function(maybe, key) {
return maybe.reduce(function(_, x) {
return x != null && key in Object(x) ?
[typeof x[key] === 'function' ? x[key]() : x[key]] :
[];
}, []);
}, [args])
.reduce(function(_, x) { return x; }, '');
if (xf == null) {
return value;
} else if (Object.prototype.hasOwnProperty.call(transformers, xf)) {
return transformers[xf](value);
} else {
throw ValueError('no transformer named "' + xf + '"');
}
}
);
};
}
// format :: String,*... -> String
var format = create({});
// format.create :: Object -> String,*... -> String
format.create = create;
// format.extend :: Object,Object -> ()
format.extend = function(prototype, transformers) {
var $format = create(transformers);
prototype.format = function() {
var args = Array.prototype.slice.call(arguments);
args.unshift(this);
return $format.apply(global, args);
};
};
/* istanbul ignore else */
if (typeof module !== 'undefined') {
module.exports = format;
} else if (typeof define === 'function' && define.amd) {
define(function() { return format; });
} else {
global.format = format;
}
}.call(this, this);