From 65a64e5db50d873a4ab496343da9b92c0524a68e Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Mon, 29 Jul 2019 14:42:34 -0700 Subject: [PATCH] feat: allow custom dictionary patterns; Closes #7 --- readme.md | 36 +++++++++++++++++++++++++++++++----- src/index.js | 9 +++++---- test/index.js | 26 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/readme.md b/readme.md index efcec8d..e3a4d13 100644 --- a/readme.md +++ b/readme.md @@ -34,20 +34,46 @@ stamp(); ## API -### tinydate(pattern)(date) +### tinydate(pattern, dict?)(date?) +Returns: `Function` -#### pattern +Returns a rendering function that will optionally accept a [`date`](#date) value as its only argument. -Type: `string` +#### pattern +Type: `String`
+Required: `true` The template pattern to be parsed. -#### date +#### dict +Type: `Object`
+Required: `false` + +A custom dictionary of template patterns. You may override [existing patterns](#patterns) or declare new ones. + +> **Important:** All dictionary items **must be a function** and must control its own formatting.
For example, when defining your own `{ss}` template, `tinydate` **will not** pad its value to two digits. +```js +const today = new Date('2019-07-04, 5:30:00 PM'); + +// Example custom dictionary: +// - Adds {MMMM} +// - Overrides {DD} +const stamp = tinydate('Today is: {MMMM} {DD}, {YYYY}', { + MMMM: d => d.toLocaleString('default', { month: 'long' }), + DD: d => d.getDate() +}); + +stamp(today); +//=> 'Today is: July 4, 2019' +``` + +#### date Type: `Date`
Default: `new Date()` -The date from which to retrieve values. Defaults to current datetime. +The date from which to retrieve values. Defaults to current datetime if no value is provided. + ## Patterns diff --git a/src/index.js b/src/index.js index af24e37..3ea001b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ var RGX = /([^{]*?)\w(?=\})/g; -var dict = { +var MAP = { YYYY: 'getFullYear', YY: 'getYear', MM: function (d) { @@ -13,15 +13,16 @@ var dict = { fff: 'getMilliseconds' }; -export default function (str) { +export default function (str, custom) { var parts=[], offset=0; + str.replace(RGX, function (key, _, idx) { // save preceding string parts.push(str.substring(offset, idx - 1)); offset = idx += key.length + 1; // save function - parts.push(function (d) { - return ('00' + (typeof dict[key]==='string' ? d[dict[key]]() : dict[key](d))).slice(-key.length); + parts.push(custom && custom[key] || function (d) { + return ('00' + (typeof MAP[key] === 'string' ? d[MAP[key]]() : MAP[key](d))).slice(-key.length); }); }); diff --git a/test/index.js b/test/index.js index 2ba6e2e..ca06f3e 100644 --- a/test/index.js +++ b/test/index.js @@ -35,3 +35,29 @@ test('rendering', t => { t.end(); }); + +test('customize', t => { + t.plan(5); + + const tmpl = '{MMMM} {DD}, {YYYY}'; + + const stamp = fn(tmpl, { + // new key + MMMM: d => d.toLocaleString('default', { month: 'long' }), + // override key + DD: d => d.getDate() + }); + + t.is(typeof stamp, 'function', 'returns a function'); + t.is(stamp(foo), 'May 1, 2017', 'returns formatted string w/ customized keys'); + + // instance w/o customized dictionary (DD should be 01 not 1) + t.is(render('{DD}'), '01', '~> does not leak {DD} customization into other instances'); + + try { + render(tmpl); + } catch (err) { + t.true(err instanceof TypeError, '~> throws TypeError w/ undefined {MMMM} format'); + t.is(err.message, 'MAP[key] is not a function', '~> says {MMMM} is not a function'); + } +});