From 305278c259f4dcdcc2cd7820ea85020bbc9053dc Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 2 Jul 2018 15:06:52 +0200 Subject: [PATCH] feat: support literal decorator --- README.md | 47 ++++++++++++++++++++++++++++----------------- index.js | 53 ++++++++++++++++++++++++++++++--------------------- test/index.js | 5 +++++ 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 38dc60b..8031d81 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ ## sprintf parser and basic formatter -* Full [printf format parser](#parser) -* Basic (ECMAScript level) modifier [resolvers](#preconfigured-modifiers) -* Format function [generator](#format-function-generator). +- Full [printf format parser](#parser) +- Basic (ECMAScript level) modifier [resolvers](#preconfigured-modifiers) +- Format function [generator](#format-function-generator). ### Installation @@ -42,17 +42,17 @@ const data = parse("Some %s with %d count"); `data` spec: -* `literals` - Surrounding string literals -* `placeholders` - Meta data of parsed placholders. +- `literals` - Surrounding string literals +- `placeholders` - Meta data of parsed placholders. Placeholder properties map (refer to [spec](https://en.wikipedia.org/wiki/Printf_format_string) for explanation of each property) - * `parameter` - (optional) parameter setting (e.g. `1`) - * `flags` - (optional) array of flags (e.g. `["0", "-"]`) - * `width` - (optional) width (e.g. `4` or `"*"` if dynamic) - * `precision` - (optional) precision (e.g. `4` or `"*"` if dynamic) - * `length` - (optional) length (e.g. `"z"`) - * `type` - Modifier type (e.g. `"s"` or `"d"`) - * `content` - Full string representation of placeholder (e.g. `"%s"`) -* `isParameterIndexingValid` - Whether parameter indexing is valid across all placeholders. + - `parameter` - (optional) parameter setting (e.g. `1`) + - `flags` - (optional) array of flags (e.g. `["0", "-"]`) + - `width` - (optional) width (e.g. `4` or `"*"` if dynamic) + - `precision` - (optional) precision (e.g. `4` or `"*"` if dynamic) + - `length` - (optional) length (e.g. `"z"`) + - `type` - Modifier type (e.g. `"s"` or `"d"`) + - `content` - Full string representation of placeholder (e.g. `"%s"`) +- `isParameterIndexingValid` - Whether parameter indexing is valid across all placeholders. e.g. if no placeholders come with parameters it'll be true. If some but not all of them will come with parameters, it'll be false (if used, then all placeholders should use them). #### Format function generator @@ -75,6 +75,17 @@ format = require("sprintf-kit")({ }); format("Some %s with %d count", "foo", 12, "rest", "args"); // Some foo with 12 count rest args + +// Message string literals (all but placeholders text) can be additionally decorated +// Useful when we want to apply some specific color to message without affecting format of special arguments + +const clc = require("cli-color"); + +format = require("sprintf-kit")({ + d: require("sprintf-kit/modifiers/d"), + s: require("sprintf-kit/modifiers/s"), + literal: literal => clc.red(literal) +}); ``` #### Preconfigured modifiers @@ -85,11 +96,11 @@ Modifiers can be found at `sprintf-kit/modifiers` folder. Preconfigured modifiers -* `d` - Number -* `f` - Floating point value -* `i` - Integer -* `j` - JSON -* `s` - String +- `d` - Number +- `f` - Floating point value +- `i` - Integer +- `j` - JSON +- `s` - String Every modifier is exception safe, in case of approaching invalid value, adequate error message token is displayed in place of placeholder diff --git a/index.js b/index.js index a8d507e..e5be762 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ "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") @@ -10,7 +11,7 @@ var aFrom = require("es5-ext/array/from") var slice = Array.prototype.slice; -var customTypes = primitiveSet("rest"); +var customTypes = primitiveSet("literal", "rest"); module.exports = function (modifiers) { objForEach(ensureObject(modifiers), function (value, type) { @@ -30,29 +31,37 @@ module.exports = function (modifiers) { var args = slice.call(arguments, 1); var skippedArgsLength = 0; - var resolvedString = data.literals.reduce(function (result, literal, index) { - var placeholder = placeholders[index - 1]; - var resolvedPlaceholder; - if (placeholder.width === "*") ++skippedArgsLength; - if (placeholder.precision === "*") ++skippedArgsLength; - if (!isParameterIndexingValid) { - resolvedPlaceholder = "[invalid placeholder parameters]"; - } else if (modifiers[placeholder.type]) { - if (placeholder.parameter) index = placeholder.parameter; - if (index > args.length) { - resolvedPlaceholder = placeholder.content; + var resolvedString; + if (data.literals.length > 1) { + resolvedString = data.literals.reduce(function (result, literal, index) { + if (index === 1 && modifiers.literal) result = modifiers.literal(result, args); + var placeholder = placeholders[index - 1]; + var resolvedPlaceholder; + if (placeholder.width === "*") ++skippedArgsLength; + if (placeholder.precision === "*") ++skippedArgsLength; + 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 += skippedArgsLength - 1; + var arg = args[index]; + resolvedPlaceholder = modifiers[placeholder.type]( + arg, placeholder, index, args + ); + } } else { - index += skippedArgsLength - 1; - var arg = args[index]; - resolvedPlaceholder = modifiers[placeholder.type]( - arg, placeholder, index, args - ); + resolvedPlaceholder = placeholder.content; } - } else { - resolvedPlaceholder = placeholder.content; - } - return result + resolvedPlaceholder + literal; - }); + return ( + result + resolvedPlaceholder + (modifiers.literal || identity)(literal, args) + ); + }); + } else { + resolvedString = (modifiers.literal || identity)(data.literals[0]); + } if (!modifiers.rest || args.length <= placeholders.length + skippedArgsLength) { return resolvedString; } diff --git a/test/index.js b/test/index.js index 13ad543..bece17f 100644 --- a/test/index.js +++ b/test/index.js @@ -9,6 +9,7 @@ test("Should", function (t) { t.test("Resolve", function (t) { // eslint-disable-next-line id-length var resolve = getResolver({ d: modifierD, s: modifierS }); + t.equal(resolve("foo raz", "marko"), "foo raz", "No placeholders"); t.equal(resolve("foo %s", "marko"), "foo marko", "Single placeholder"); t.equal(resolve("foo %s %d", "marko", 12), "foo marko 12", "Two placeholders"); t.equal( @@ -47,6 +48,10 @@ test("Should", function (t) { t.equal(resolve(12, 13), "12-13", "Non-string first argument with rest"); t.end(); }); + var resolve = getResolver({ d: modifierD, literal: function (str) { return str + "foo"; } }); + t.equal(resolve("mar %d ko", 12), "mar foo12 kofoo", "Support 'literal' modifier"); + t.equal(resolve("marlo"), "marlofoo", "Support 'literal' modifier with no placeholder"); + t.throws( function () { getResolver({ foo: modifierD }); }, TypeError, "Reject invalid modifiers map" );