Skip to content

Commit

Permalink
feat: allow custom dictionary patterns;
Browse files Browse the repository at this point in the history
Closes #7
  • Loading branch information
lukeed committed Jul 29, 2019
1 parent 3517322 commit 65a64e5
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 9 deletions.
36 changes: 31 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`<br>
Required: `true`

The template pattern to be parsed.

#### date
#### dict
Type: `Object`<br>
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.<br>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`<br>
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

Expand Down
9 changes: 5 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
var RGX = /([^{]*?)\w(?=\})/g;

var dict = {
var MAP = {
YYYY: 'getFullYear',
YY: 'getYear',
MM: function (d) {
Expand All @@ -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);
});
});

Expand Down
26 changes: 26 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
});

0 comments on commit 65a64e5

Please sign in to comment.