Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Returns resolved data in form of separate tokens
- Loading branch information
Showing
4 changed files
with
191 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
"use strict"; | ||
|
||
var ensureObject = require("es5-ext/object/valid-object") | ||
, objForEach = require("es5-ext/object/for-each") | ||
, ensurePlainFunction = require("es5-ext/object/ensure-plain-function") | ||
, primitiveSet = require("es5-ext/object/primitive-set") | ||
, typeChars = require("./lib/type-chars") | ||
, parse = require("./parse"); | ||
|
||
var slice = Array.prototype.slice; | ||
|
||
var customTypes = primitiveSet("literal", "rest"); | ||
|
||
var validateModifiers = function (modifiers) { | ||
objForEach(ensureObject(modifiers), function (value, type) { | ||
if (!hasOwnProperty.call(typeChars, type) && !customTypes[type]) { | ||
throw new TypeError("Invalid modifier type: " + type); | ||
} | ||
ensurePlainFunction(value); | ||
}); | ||
}; | ||
|
||
var resolveRest = function (modifier, data, args, placeholderArgsLength) { | ||
if (!modifier || args.length <= placeholderArgsLength) return null; | ||
return modifier(slice.call(args, placeholderArgsLength), data); | ||
}; | ||
|
||
var resolveResultWithPlaceholders = function (modifiers, data, args, result) { | ||
var extraPlaceholderArgsLength = 0; | ||
var isParameterIndexingValid = data.isParameterIndexingValid; | ||
result.substitutions = data.placeholders.map(function (placeholder, index) { | ||
var parameter = index + 1; | ||
if (placeholder.width === "*") ++extraPlaceholderArgsLength; | ||
if (placeholder.precision === "*") ++extraPlaceholderArgsLength; | ||
if (!isParameterIndexingValid) return "[invalid placeholder parameters]"; | ||
if (!modifiers[placeholder.type]) return placeholder.content; | ||
if (placeholder.parameter) parameter = placeholder.parameter; | ||
if (parameter > args.length) return placeholder.content; | ||
var placeholderIndex = parameter + extraPlaceholderArgsLength - 1; | ||
var arg = args[placeholderIndex]; | ||
return modifiers[placeholder.type](arg, placeholder, placeholderIndex, args); | ||
}); | ||
result.rest = resolveRest( | ||
modifiers.rest, data, args, data.placeholders.length + extraPlaceholderArgsLength | ||
); | ||
return result; | ||
}; | ||
|
||
module.exports = function (modifiers) { | ||
validateModifiers(modifiers); | ||
|
||
return function (format/*, ...params*/) { | ||
var result; | ||
if (typeof format !== "string") { | ||
return { | ||
literals: [], | ||
substitutions: [], | ||
rest: resolveRest(modifiers.rest, null, arguments, 0) | ||
}; | ||
} | ||
var data = parse(format); | ||
var args = slice.call(arguments, 1); | ||
|
||
result = { literals: data.literals }; | ||
if (data.literals.length <= 1) { | ||
result.substitutions = []; | ||
result.rest = resolveRest(modifiers.rest, data, args, 0); | ||
return result; | ||
} | ||
return resolveResultWithPlaceholders(modifiers, data, args, result); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,23 @@ | ||
"use strict"; | ||
|
||
var aFrom = require("es5-ext/array/from") | ||
, identity = require("es5-ext/function/identity") | ||
, ensureObject = require("es5-ext/object/valid-object") | ||
, objForEach = require("es5-ext/object/for-each") | ||
, ensurePlainFunction = require("es5-ext/object/ensure-plain-function") | ||
, primitiveSet = require("es5-ext/object/primitive-set") | ||
, typeChars = require("./lib/type-chars") | ||
, parse = require("./parse"); | ||
|
||
var slice = Array.prototype.slice; | ||
|
||
var customTypes = primitiveSet("literal", "rest"); | ||
|
||
var validateModifiers = function (modifiers) { | ||
objForEach(ensureObject(modifiers), function (value, type) { | ||
if (!hasOwnProperty.call(typeChars, type) && !customTypes[type]) { | ||
throw new TypeError("Invalid modifier type: " + type); | ||
} | ||
ensurePlainFunction(value); | ||
}); | ||
}; | ||
|
||
var resolveRest = function (modifier, data, args, placeholderArgsLength) { | ||
if (!modifier || args.length <= placeholderArgsLength) return ""; | ||
return modifier(args.slice(placeholderArgsLength), data); | ||
}; | ||
|
||
var resolveStringWithPlaceholders = function (modifiers, data, args) { | ||
var extraPlaceholderArgsLength = 0; | ||
var isParameterIndexingValid = data.isParameterIndexingValid; | ||
var resolvedString = data.literals.reduce(function (result, literal, index) { | ||
if (index === 1 && modifiers.literal) result = modifiers.literal(result, args); | ||
var placeholder = data.placeholders[index - 1]; | ||
var resolvedPlaceholder; | ||
if (placeholder.width === "*") ++extraPlaceholderArgsLength; | ||
if (placeholder.precision === "*") ++extraPlaceholderArgsLength; | ||
if (!isParameterIndexingValid) { | ||
resolvedPlaceholder = "[invalid placeholder parameters]"; | ||
} else if (modifiers[placeholder.type]) { | ||
if (placeholder.parameter) index = placeholder.parameter; | ||
if (index > args.length) { | ||
resolvedPlaceholder = placeholder.content; | ||
} else { | ||
index += extraPlaceholderArgsLength - 1; | ||
var arg = args[index]; | ||
resolvedPlaceholder = modifiers[placeholder.type](arg, placeholder, index, args); | ||
} | ||
} else { | ||
resolvedPlaceholder = placeholder.content; | ||
} | ||
return result + resolvedPlaceholder + (modifiers.literal || identity)(literal, args); | ||
}); | ||
return ( | ||
resolvedString + | ||
resolveRest( | ||
modifiers.rest, data, args, data.placeholders.length + extraPlaceholderArgsLength | ||
) | ||
); | ||
}; | ||
|
||
var resolveStringWithoutPlaceholders = function (modifiers, data, args) { | ||
var resolvedString = data.literals[0]; | ||
if (modifiers.literal) resolvedString = modifiers.literal(resolvedString); | ||
return resolvedString + resolveRest(modifiers.rest, data, args, data.placeholders.length); | ||
}; | ||
var getResolver = require("./get-resolver"); | ||
|
||
module.exports = function (modifiers) { | ||
validateModifiers(modifiers); | ||
|
||
return function (format/*, ...params*/) { | ||
if (typeof format !== "string") { | ||
return modifiers.rest ? modifiers.rest(aFrom(arguments)) : ""; | ||
var resolve = getResolver(modifiers); | ||
|
||
return function (formatIgnored/*, ...params*/) { | ||
var data = resolve.apply(null, arguments); | ||
var literals = data.literals; | ||
var substitutions = data.substitutions; | ||
if (modifiers.literal) { | ||
literals = literals.map(function (literal) { return modifiers.literal(literal); }); | ||
} | ||
var data = parse(format); | ||
var args = slice.call(arguments, 1); | ||
|
||
if (data.literals.length > 1) return resolveStringWithPlaceholders(modifiers, data, args); | ||
return resolveStringWithoutPlaceholders(modifiers, data, args); | ||
var resolvedString = literals.length | ||
? literals.reduce(function (resolved, literal, index) { | ||
return resolved + substitutions[index - 1] + literal; | ||
}) | ||
: ""; | ||
if (data.rest) resolvedString += data.rest; | ||
return resolvedString; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
"use strict"; | ||
|
||
var test = require("tape") | ||
, modifierD = require("../modifiers/d") | ||
, modifierS = require("../modifiers/s") | ||
, getResolver = require("../get-resolver"); | ||
|
||
test("getResolver", function (t) { | ||
t.test("Should resolve", function (t) { | ||
// eslint-disable-next-line id-length | ||
var resolve = getResolver({ d: modifierD, s: modifierS }); | ||
t.deepEqual( | ||
resolve("foo raz", "marko"), { literals: ["foo raz"], substitutions: [], rest: null }, | ||
"No placeholders" | ||
); | ||
t.deepEqual( | ||
resolve("foo %s", "marko"), | ||
{ literals: ["foo ", ""], substitutions: ["marko"], rest: null }, "Single placeholder" | ||
); | ||
t.deepEqual( | ||
resolve("foo %s %d", "marko", 12), | ||
{ literals: ["foo ", " ", ""], substitutions: ["marko", "12"], rest: null }, | ||
"Two placeholders" | ||
); | ||
t.deepEqual( | ||
resolve("foo %s %d", "marko", 12, "elo"), | ||
{ literals: ["foo ", " ", ""], substitutions: ["marko", "12"], rest: null }, | ||
"Two placeholders with arguments overflow and no rest handling defined" | ||
); | ||
t.deepEqual( | ||
resolve("foo %s %d", "marko"), | ||
{ literals: ["foo ", " ", ""], substitutions: ["marko", "%d"], rest: null }, | ||
"foo marko %d", "Two placeholders with argument missing" | ||
); | ||
t.deepEqual( | ||
resolve("foo %2$s %1$d", 12, "bar"), | ||
{ literals: ["foo ", " ", ""], substitutions: ["bar", "12"], rest: null }, | ||
"Parameters swap" | ||
); | ||
t.deepEqual( | ||
resolve("foo %*d", 10, 12), | ||
{ literals: ["foo ", ""], substitutions: ["12"], rest: null }, "Dynamic width" | ||
); | ||
t.deepEqual( | ||
resolve("foo %.*d", 10, 12), | ||
{ literals: ["foo ", ""], substitutions: ["12"], rest: null }, "Dynamic precision" | ||
); | ||
t.deepEqual( | ||
resolve("foo %2$s %2$d", 12, "bar"), | ||
{ | ||
literals: ["foo ", " ", ""], | ||
substitutions: [ | ||
"[invalid placeholder parameters]", "[invalid placeholder parameters]" | ||
], | ||
rest: null | ||
}, | ||
|
||
"Invalid parameters setup" | ||
); | ||
t.deepEqual( | ||
resolve(12, 13), { literals: [], substitutions: [], rest: null }, | ||
"Non-string first argument without rest" | ||
); | ||
|
||
resolve = getResolver({ | ||
d: modifierD, | ||
// eslint-disable-next-line id-length | ||
s: modifierS, | ||
rest: function (args, data) { return (data ? " " : "") + args.join("-"); } | ||
}); | ||
|
||
t.deepEqual( | ||
resolve("foo %s", "marko", 12, "elo"), | ||
{ literals: ["foo ", ""], substitutions: ["marko"], rest: " 12-elo" }, | ||
"Arguments overflow with rest handling" | ||
); | ||
t.deepEqual( | ||
resolve("foo %*s", 10, "marko", 12, "elo"), | ||
{ literals: ["foo ", ""], substitutions: ["marko"], rest: " 12-elo" }, | ||
"Arguments overflow with rest handling and width shift" | ||
); | ||
t.deepEqual( | ||
resolve("foo %x", "elo"), { literals: ["foo ", ""], substitutions: ["%x"], rest: null }, | ||
"Placeholder content on unknown type" | ||
); | ||
t.deepEqual( | ||
resolve(12, 13), { literals: [], substitutions: [], rest: "12-13" }, | ||
"Non-string first argument with rest" | ||
); | ||
t.end(); | ||
}); | ||
|
||
t.throws( | ||
function () { getResolver({ foo: modifierD }); }, TypeError, "Reject invalid modifiers map" | ||
); | ||
|
||
t.end(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters