Skip to content

Commit

Permalink
Add FP function generation (closes #253) (#393)
Browse files Browse the repository at this point in the history
* Add FP function generation

* Modify build_index to build FP index

* Add FP typings generator

* Regenerate docs, indices, typings and FP

* Add FP doc

* Add FP CHANGELOG.md entry

* Add lodash-fp example

* Add FP examples

* Remove unused phantomjs-prebuilt

* Add FP tests

* Fix style

* Add ECMAScript Modules documentation article
  • Loading branch information
leshakoss committed May 20, 2017
1 parent 4bb1311 commit 024f429
Show file tree
Hide file tree
Showing 579 changed files with 30,917 additions and 1,524 deletions.
86 changes: 60 additions & 26 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,66 @@ This change log follows the format documented in [Keep a CHANGELOG].

## [Unreleased]

### Added

- FP functions like those in [lodash](https://github.com/lodash/lodash/wiki/FP-Guide),
that support [currying](https://en.wikipedia.org/wiki/Currying), and, as a consequence,
functional-style [function composing](https://medium.com/making-internets/why-using-chain-is-a-mistake-9bc1f80d51ba)).

Each non-FP function has two FP counterparts: one that has [Options](docs/Options) object as its first argument
and one that hasn't. The name of the former has `WithOptions` added to the end of its name.

In FP functions, the order of arguments is reversed.

See [FP Guide](docs/fp) for more information.

```javascript
import addYears from 'date-fns/fp/addYears'
import formatWithOptions from 'date-fns/fp/formatWithOptions'
import eoLocale from 'date-fns/locale/eo'

// If FP function has not recieved enough arguments, it returns another function
const addFiveYears = addYears(5)

// Several arguments can be curried at once
const dateToString = formatWithOptions({locale: eoLocale}, 'D MMMM YYYY')

const dates = [
new Date(2017, 0 /* Jan */, 1),
new Date(2017, 1 /* Feb */, 11),
new Date(2017, 6 /* Jul */, 2)
]

const formattedDates = dates.map((date) => dateToString(addFiveYears(date)))
//=> ['1 januaro 2022', '11 februaro 2022', '2 julio 2022']
```

- Added support for [ECMAScript Modules](http://www.ecma-international.org/ecma-262/6.0/#sec-modules)
via `'date-fns/esm'` subpackage.

It allows usage with bundlers that support tree-shaking,
like [rollup.js](http://rollupjs.org) and [webpack](https://webpack.js.org):

```javascript
// Without tree-shaking:
import format from 'date-fns/format'
import parse from 'date-fns/parse'

// With tree-shaking:
import {format, parse} from 'date-fns/esm'
```

Also, as `'date-fns/esm'` function submodules provide default export,
they can be used with TypeScript to import functions in more idiomatic way:

```typescript
// In TypeScript,
import * as format from 'date-fns/format'

// is same as:
import format from 'date-fns/esm/format'
```

### Changed

- **BREAKING**: min and max functions now accept an array of dates
Expand Down Expand Up @@ -219,32 +279,6 @@ rather than spread arguments.
for consistency and future features.
See [docs/Options.js](https://github.com/date-fns/date-fns/blob/master/docs/Options.js)

- Added support for [ECMAScript Modules](http://www.ecma-international.org/ecma-262/6.0/#sec-modules)
via `'date-fns/esm'` subpackage.

It allows usage with bundlers that support tree-shaking,
like [rollup.js](http://rollupjs.org) and [webpack](https://webpack.js.org):

```javascript
// Without tree-shaking:
import format from 'date-fns/format'
import parse from 'date-fns/parse'

// With tree-shaking:
import {format, parse} from 'date-fns/esm'
```

Also, as `'date-fns/esm'` function submodules provide default export,
they can be used with TypeScript to import functions in more idiomatic way:

```typescript
// In TypeScript,
import * as format from 'date-fns/format'

// is same as:
import format from 'date-fns/esm/format'
```

## [1.28.5] - 2017-05-19

### Fixed
Expand Down
42 changes: 42 additions & 0 deletions docs/esm.md
@@ -0,0 +1,42 @@
# ECMAScript Modules


**date-fns** v2.x provides support for
[ECMAScript Modules](http://www.ecma-international.org/ecma-262/6.0/#sec-modules)
via `'date-fns/esm'` subpackage.

## Table of Contents

- [Tree-shaking](#tree-shaking)

- [Usage With TypeScript](#usage-with-typescript)

## Tree-shaking

ECMAScript Modules allow usage with bundlers that support tree-shaking,
like [rollup.js](http://rollupjs.org) and [webpack](https://webpack.js.org):

```javascript
// Without tree-shaking:
import format from 'date-fns/format'
import parse from 'date-fns/parse'

// With tree-shaking:
import {format, parse} from 'date-fns/esm'
```

**Important**: as at webpack 2.2.0, tree-shaking is not removing all unused imports.
See [webpack issue #2867](https://github.com/webpack/webpack/issues/2867)

## Usage With TypeScript

Also, as `'date-fns/esm'` function submodules provide default export,
they can be used with TypeScript to import functions in more idiomatic way:

```typescript
// In TypeScript, instead of importing regular function submodules:
import * as format from 'date-fns/format'

// you can use 'date-fns/esm' function submodules as a shortcut:
import format from 'date-fns/esm/format'
```
84 changes: 84 additions & 0 deletions docs/fp.md
@@ -0,0 +1,84 @@
# FP Guide

**date-fns** v2.x provides [functional programming](https://en.wikipedia.org/wiki/Functional_programming) (FP)
friendly functions, like those in [lodash](https://github.com/lodash/lodash/wiki/FP-Guide),
that support [currying](https://en.wikipedia.org/wiki/Currying).

## Table of Contents

- [Usage](#usage)

- [Using Function Composition](#using-function-composition)

- [ECMAScript Modules](#ecmascript-modules)

## Usage

FP functions are provided via `'date-fns/fp'` submodule.

Each non-FP function has two FP counterparts: one that has [Options](docs/Options) object as its first argument
and one that hasn't. The name of the former has `WithOptions` added to the end of its name.

In **date-fns'** FP functions, the order of arguments is reversed.

```javascript
import addYears from 'date-fns/fp/addYears'
import formatWithOptions from 'date-fns/fp/formatWithOptions'
import eoLocale from 'date-fns/locale/eo'
import toUpper from 'lodash/fp/toUpper' // 'date-fns/fp' is compatible with 'lodash/fp'!

// If FP function has not recieved enough arguments, it returns another function
const addFiveYears = addYears(5)

// Several arguments can be curried at once
const dateToString = formatWithOptions({locale: eoLocale}, 'D MMMM YYYY')

const dates = [
new Date(2017, 0 /* Jan */, 1),
new Date(2017, 1 /* Feb */, 11),
new Date(2017, 6 /* Jul */, 2)
]

const formattedDates = dates.map(addFiveYears).map(dateToString).map(toUpper)
//=> ['1 JANUARO 2022', '11 FEBRUARO 2022', '2 JULIO 2022']
```

## Using Function Composition

The main advantage of FP functions is support of functional-style
[function composing](https://medium.com/making-internets/why-using-chain-is-a-mistake-9bc1f80d51ba).

In the example above, you can compose `addFiveYears`, `dateToString` and `toUpper` into a single function:

```javascript
const formattedDates = dates.map((date) => toUpper(dateToString(addFiveYears(date))))
```

Or you can use `compose` function provided by [lodash](https://lodash.com) to do the same in more idiomatic way:

```javascript
import compose from 'lodash/fp/compose'

const formattedDates = dates.map(compose(toUpper, dateToString, addFiveYears))
```

Or if you prefer natural direction of composing (as opposed to the computationally correct order),
you can use lodash' `flow` instead:

```javascript
import flow from 'lodash/fp/flow'

const formattedDates = dates.map(flow(addFiveYears, dateToString, toUpper))
```

## ECMAScript Modules

FP functions are also can be used with bundlers that support tree-shaking,
like [rollup.js](http://rollupjs.org) and [webpack](https://webpack.js.org)
via `'date-fns/esm/fp'` submodule:

```javascript
import {addYears, formatWithOptions} from 'date-fns/esm/fp'
```

Read more in [ECMAScript Modules](docs/esm) article in the documentation.
16 changes: 16 additions & 0 deletions docs/index.js
Expand Up @@ -54,6 +54,22 @@ export default {
description: 'Internationalization',
path: path.join(__dirname, 'i18n.md')
},
{
type: 'markdown',
urlId: 'ECMAScript-Modules',
category: 'General',
title: 'ECMAScript Modules',
description: 'Tree-shaking guide',
path: path.join(__dirname, 'esm.md')
},
{
type: 'markdown',
urlId: 'FP-Guide',
category: 'General',
title: 'FP Guide',
description: 'Curried functions',
path: path.join(__dirname, 'fp.md')
},
{
type: 'markdown',
urlId: 'License',
Expand Down
2 changes: 1 addition & 1 deletion examples/browserify/README.md
@@ -1,6 +1,6 @@
# Usage With Browserify

See [example.js](./example.js) and [misc.js](./misc.js) for source code examples.
See [example.js](./example.js), [fp.js](./fp.js) and [misc.js](./misc.js) for source code examples.

See [package.json scripts](./package.json) for CLI usage.

Expand Down
19 changes: 19 additions & 0 deletions examples/browserify/fp.js
@@ -0,0 +1,19 @@
var addYears = require('date-fns/fp/addYears')
var dateFns = require('date-fns/fp')
var formatWithOptions = dateFns.formatWithOptions
var eoLocale = require('date-fns/locale/eo')

var addFiveYears = addYears(5)
var dateToString = formatWithOptions({locale: eoLocale}, 'D MMMM YYYY')

var dates = [
new Date(2017, 0 /* Jan */, 1),
new Date(2017, 1 /* Feb */, 11),
new Date(2017, 6 /* Jul */, 2)
]

var formattedDates = dates
.map((date) => dateToString(addFiveYears(date)))
.join(', ')

console.log(formattedDates === '1 januaro 2022, 11 februaro 2022, 2 julio 2022')
9 changes: 6 additions & 3 deletions examples/browserify/package.json
Expand Up @@ -14,15 +14,18 @@
"scripts": {
"build": "yarn run build-date-fns && yarn run build-browserify && yarn run build-babili",
"build-date-fns": "env PACKAGE_OUTPUT_PATH=\"$(pwd)/node_modules/date-fns\" ../../scripts/build_package.sh",
"build-browserify": "yarn run build-browserify-example && yarn run build-browserify-misc",
"build-browserify": "yarn run build-browserify-example && yarn run build-browserify-fp && yarn run build-browserify-misc",
"build-browserify-example": "mkdir -p dist && browserify example.js -o dist/example.js",
"build-browserify-fp": "mkdir -p dist && browserify fp.js -o dist/fp.js",
"build-browserify-misc": "mkdir -p dist && browserify misc.js -o dist/misc.js",
"build-babili": "yarn run build-babili-example && yarn run build-babili-misc && yarn run stats-size",
"build-babili": "yarn run build-babili-example && yarn run build-babili-fp && yarn run build-babili-misc && yarn run stats-size",
"build-babili-example": "babili dist/example.js --out-file dist/example.min.js --minified --no-comments",
"build-babili-fp": "babili dist/fp.js --out-file dist/fp.min.js --minified --no-comments",
"build-babili-misc": "babili dist/misc.js --out-file dist/misc.min.js --minified --no-comments",
"stats-size": "gzip-size dist/example.min.js | pretty-bytes",
"test": "yarn run test-example && yarn run test-misc",
"test": "yarn run test-example && yarn run test-fp && yarn run test-misc",
"test-example": "test $(env TZ=UTC node ./dist/example.min.js) = true",
"test-fp": "test $(env TZ=UTC node ./dist/fp.min.js) = true",
"test-misc": "test $(env TZ=UTC node ./dist/misc.min.js) = true"
}
}
2 changes: 1 addition & 1 deletion examples/flow/README.md
@@ -1,6 +1,6 @@
# Usage With webpack 1.x

See [example.js.flow](./example.js.flow) and [misc.js.flow](./misc.js.flow) for source code examples.
See [example.js.flow](./example.js.flow), [fp.js.flow](./fp.js.flow) and [misc.js.flow](./misc.js.flow) for source code examples.

See [package.json scripts](./package.json) for CLI usage.

Expand Down
19 changes: 19 additions & 0 deletions examples/flow/fp.js.flow
@@ -0,0 +1,19 @@
var addYears = require('date-fns/fp/addYears')
var dateFns = require('date-fns/fp')
var formatWithOptions = dateFns.formatWithOptions
var eoLocale = require('date-fns/locale/eo')

const addFiveYears = addYears(5)
const dateToString = formatWithOptions({locale: eoLocale}, 'D MMMM YYYY')

const dates : Date[] = [
new Date(2017, 0 /* Jan */, 1),
new Date(2017, 1 /* Feb */, 11),
new Date(2017, 6 /* Jul */, 2)
]

const formattedDates : string = dates
.map((date) => dateToString(addFiveYears(date)))
.join(', ')

console.log(formattedDates === '1 januaro 2022, 11 februaro 2022, 2 julio 2022')
6 changes: 4 additions & 2 deletions examples/flow/package.json
Expand Up @@ -14,11 +14,13 @@
"build": "yarn run build-date-fns && yarn run flow-check && yarn run build-babel",
"flow-check": "flow check",
"build-date-fns": "env PACKAGE_OUTPUT_PATH=\"$(pwd)/node_modules/date-fns\" ../../scripts/build_package.sh",
"build-babel": "yarn run build-babel-example && yarn run build-babel-misc",
"build-babel": "yarn run build-babel-example && yarn run build-babel-fp && yarn run build-babel-misc",
"build-babel-example": "mkdir -p dist && babel example.js.flow --out-file dist/example.js",
"build-babel-fp": "mkdir -p dist && babel fp.js.flow --out-file dist/fp.js",
"build-babel-misc": "mkdir -p dist && babel misc.js.flow --out-file dist/misc.js",
"test": "yarn run test-example && yarn run test-misc",
"test": "yarn run test-example && yarn run test-fp && yarn run test-misc",
"test-example": "test $(env TZ=UTC node ./dist/example.js) = true",
"test-fp": "test $(env TZ=UTC node ./dist/fp.js) = true",
"test-misc": "test $(env TZ=UTC node ./dist/misc.js) = true"
}
}
3 changes: 3 additions & 0 deletions examples/lodash-fp/.babelrc
@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}
23 changes: 23 additions & 0 deletions examples/lodash-fp/README.md
@@ -0,0 +1,23 @@
# Usage With lodash FP

See [example.js](./example.js) for source code example.

See [package.json scripts](./package.json) for CLI usage.

## Build Example

```sh
yarn
yarn run build
```

See ./dist for output.

## Minimal Build Size

You can see the build size:

```sh
gzip-size dist/example.min.js | pretty-bytes
#=> 20.1 kB
```
16 changes: 16 additions & 0 deletions examples/lodash-fp/example.js
@@ -0,0 +1,16 @@
import addYears from 'date-fns/fp/addYears'
import formatWithOptions from 'date-fns/fp/formatWithOptions'
import eoLocale from 'date-fns/locale/eo'

import compose from 'lodash/fp/compose'
import toUpper from 'lodash/fp/toUpper'
import isEqual from 'lodash/isEqual'

const addFiveYears = addYears(5)
const dateToString = formatWithOptions({locale: eoLocale}, 'D MMMM YYYY')

const dates = ['2017-01-01', '2017-02-11', '2017-07-02']

const formattedDates = dates.map(compose(toUpper, dateToString, addFiveYears))

console.log(isEqual(formattedDates, ['1 JANUARO 2022', '11 FEBRUARO 2022', '2 JULIO 2022']))

0 comments on commit 024f429

Please sign in to comment.