# @motorcycle/router

> Standard Router Driver for Motorcycle.js

A driver built on top of [@motorcycle/history](https://github.com/motorcyclejs/history)
and [switch-path](https://github.com/staltz/switch-path) to ease the pain of routing.
Works server-side and in browsers!

## Let me have it!
```sh
npm install --save @motorcycle/router
```

## Basic Usage

```typescript
import { run } from '@motorcycle/run';
import { makeDomComponent, div, h1 } from '@motorcycle/dom';
import { History } from '@motorcycle/history';
import { Router } from '@motorcycle/router';
import { of } from 'most';

function Main(sources) {
  const match$ = sources.router.define({
    '/': HomeComponent,
    '/other': OtherComponent
  });

  const page$ = match$.map(({path, value}) => {
    return value({...sources, router: sources.router.path(path)});
  });

  return {
    dom: page$.map(c => c.dom).switch(),
    router: page$.map(c => c.route$).switch().startWith('/'),
  };
}

const Dom = makeDomComponent(document.querySelector('#app')));

function Effects(sinks) {
  const { dom } = Dom(sinks);
  const { router } = Router(History(sinks));
}

run(main, Effects)

function HomeComponent() {
  const route$ = ... // left as a user exercise
  return {
    dom: of(div([h1('home')])),
    route$,
  }
}

function OtherComponent() {
  const route$ = ... // left as a user exercise
  return {
    dom: of(div([h1('other')])),
    route$
  }
}
```

## API

For all types not defined here, please refer to `@motorcycle/history`'s type
documentation [here](https://github.com/motorcyclejs/history#types)

#### `Router(sinks: HistorySources): RouterSources`

This is the main API of this driver. This function simply wraps `@motorcycle/history`
and returns a source object containing methods instead of a stream.

#### `RouterComponent: RouterHOC`

A convenience function, to more declaratively define your routes to
*Components*. It returns a stream of your currently matched Component.
When using the router driver directly there is more flexibility. With
the Router function, you must use routes to match to Components.

```typescript
function main(sources: Sources): Sinks {
  const sinks$: Stream<Sinks> =
    Router({
      '/': HomeComponent, // HomeComponent :: (sources: Sources) => Sinks;
      '/profile: {
        '/': ProfileComponent,
        '/:id': (id: number) => (sources: Sources) => ProfileId({...sources, id}),
      }
    }, sources);

  return {
    DOM: sinks$.map(sinks => sinks.dom).switch(),
    router: sinks$.map(sinks => sinks.route$).switch()
  };
}
```

## Types

#### `RouterSource`

This is a type representation of the object passed into your main function.

```typescript
export interface RouterSource {
  history(): Stream<Location>;
  path(pathname: Pathname): RouterSource;
  define(routes: RouteDefinitions): Stream<DefineReturn>;
  createHref(path: Pathname): Pathname;
}
```

`history(): Stream<Location>` - This method allows you to reach the underlying
stream provided by `@motorcycle/history`.

`path(pathname: Pathname): RouterSource` - This method allows you to created
nested router instances, very much like `DOM.select()` creates a new place in the
DOM tree to look for elements and events, this allows dynamically created routes
that can be matched that are decoupled from any parent routes.

`define(routes: RouteDefinitions): Stream<DefineReturn>` - This method takes
an object (anything supported by switch-path) of keys that represent your routes
and returns a stream with an object that repesents any matches.

`createHref(path: Pathname): Pathname` - This method allows you to create Hrefs
that are namespaced at any RouterSource instance.

```typescript
const nestedRouter = sources.router.path('/some').path('/path')

const href = nestedRouter.createHref('/unaware/of/nesting')

console.log(href) '/some/path/unaware/of/nesting')
```

#### `DefineReturn`

```typescript
export interface DefineReturn {
  location: Location;
  createHref: (path: Pathname) => Pathname;
  path: string | null;
  value: any | null;
}
```

#### `RouteDefinitions`
```typescript
export interface RouteDefinitions {
  [key: Pathname]: any;
}
```

#### `RouterHOC`
```typescript
export interface RouterHOC {
  <Sources, Sinks>(definitions: RouterDefinitions<Sources, Sinks>,
    sources: RouterSources<Sources>): Stream<Sinks>;

  <Sources, Sinks>(definitions: RouterDefinitions<Sources, Sinks>):
    (sources: RouterSources<Sources>) => Stream<Sinks>;
}
```