Skip to content
This repository was archived by the owner on Dec 15, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@
extends:
- "defaults/configurations/walmart/es6-react"

parser: "babel-eslint"

plugins:
- "filenames"
- "babel"
- "flowtype"

rules:
filenames/filenames: [2, "^[a-z\\-\\.]+$"] # dash-cased filenames.
quotes: [2, "single"]
spaced-comment: [2, "always"]
indent: [2, 2, {"SwitchCase": 1}]
jsx-quotes: [2, "prefer-single"]
arrow-parens: [1, "as-needed"]
arrow-parens: 0
no-fallthrough: 2
no-arrow-condition: 0
no-confusing-arrow: [2, {"allowParens": false}]
flowtype/define-flow-type: warn

env:
es6: true
Expand Down
20 changes: 20 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[ignore]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have to specify all these ignores if you're only including src?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, yeah :/ This is how Flow knows which code is yours and which code is library stuff.

dist/
lib/
.*/node_modules/fbjs/.*
.*/__tests__/.*
.*/__mocks__/.*
.*/node_modules/flow-typed/src/.*

[include]
src/

[libs]
interfaces/
node_modules/flow-typed/definitions

[options]
module.name_mapper='history\(.*\)' -> 'history'
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
suppress_comment= \\(.\\|\n\\)*\\$FlowIssue]
55 changes: 55 additions & 0 deletions interfaces/history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
declare module 'history' {
declare type HistoryOptions = Object;

declare type Action = 'PUSH' | 'REPLACE' | 'POP';
declare type Hash = string;
declare type Href = string;
declare type Path = string;
declare type Pathname = string;
declare type Query = Object;
declare type Search = string;

declare type LocationKey = string;
declare type LocationState = ?Object;

declare type Location = {
pathname: Pathname;
search: Search;
query: Query;
state: LocationState;
action: Action;
key: LocationKey;
};

declare type LocationDescriptorObject = {
pathname: Pathname;
search?: Search;
query?: Query;
state?: LocationState;
};

declare type LocationDescriptor = LocationDescriptorObject | Path;

declare type LocationListener = (location: Location) => void;

declare type TransitionHook = (location: Location, callback: ?Function) => any;

declare type History = {
listenBefore: (hook: TransitionHook) => Function,
listen: (listener: LocationListener) => Function,
transitionTo: (location: Location) => void,
push: (location: LocationDescriptor) => void,
replace: (location: LocationDescriptor) => void,
go: (n: number) => void,
goBack: () => void,
goForward: () => void,
createKey: () => LocationKey,
createPath: (location: LocationDescriptor) => Path,
createHref: (location: LocationDescriptor) => Href,
createLocation: (location: LocationDescriptor, action: ?Action, key: ?LocationKey) => Location
};

declare type BeforeUnloadHook = () => ?string;
declare type CreateHistory = (options: ?HistoryOptions) => History;
declare type CreateHistoryEnhancer = (createHistory: CreateHistory) => CreateHistory;
}
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"watch-lib": "watch 'npm run build-lib' src/ -d",
"clean": "npm run clean-lib && npm run clean-dist",
"build": "npm run build-lib && npm run build-dist",
"lint": "eslint --color --ext .js,.jsx src test",
"lint": "concurrently 'eslint --color --ext .js,.jsx src test' 'flow check'",
"test": "mocha test/.setup.js test/**/*.spec.js",
"test-cov": "BABEL_ENV=coverage nyc mocha test/.setup.js 'test/**/*.spec.js'",
"check": "npm run lint && npm run test",
Expand Down Expand Up @@ -61,8 +61,12 @@
"enzyme": "^2.4.1",
"eslint": "^3.0.1",
"eslint-config-defaults": "^10.0.0-alpha.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 I'll PR that in!

"eslint-plugin-babel": "^3.3.0",
"eslint-plugin-filenames": "0.2.0",
"eslint-plugin-flowtype": "^2.6.3",
"eslint-plugin-react": "^5.2.2",
"flow-bin": "^0.30.0",
"flow-typed": "^2.0.0-beta.6",
"jsdom": "^9.4.1",
"lodash": "^4.13.1",
"mocha": "^2.5.3",
Expand Down
1 change: 1 addition & 0 deletions src/action-types.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does flow do anything for files that don't have any explicit types defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It at least infers their types, although it might be that consumers of this file that have the // @flow declaration do this anyway.

export const LOCATION_CHANGED = 'ROUTER_LOCATION_CHANGED';
export const PUSH = 'ROUTER_PUSH';
export const REPLACE = 'ROUTER_REPLACE';
Expand Down
29 changes: 14 additions & 15 deletions src/create-matcher.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
// @flow
import UrlPattern from 'url-pattern';

export default routes => {
const routeDictionary = Object.keys(routes).map(route => ({
export default (routes: Object) => {
const routeList = Object.keys(routes).map(route => ({
route,
pattern: new UrlPattern(route),
result: routes[route]
}));

return incomingUrl => {
return (incomingUrl: string) => {
// Discard query strings
const route = incomingUrl.split('?')[0]; // eslint-disable-line no-magic-numbers

// Find the route that matches the URL
for (const key in routeDictionary) {
if (routeDictionary.hasOwnProperty(key)) {
const storedRoute = routeDictionary[key];
const match = storedRoute.pattern.match(route);
for (let i = 0; i < routeList.length; i++) {
const storedRoute = routeList[i];
const match = storedRoute.pattern.match(route);

if (match) {
// Return the matched params and user-defined result object
return {
route: storedRoute.route,
params: match,
result: storedRoute.result
};
}
if (match) {
// Return the matched params and user-defined result object
return {
route: storedRoute.route,
params: match,
result: storedRoute.result
};
}
}

Expand Down
25 changes: 17 additions & 8 deletions src/fragment.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
// @flow
import type { Location } from 'history';
import type { RouterContext } from './provider';

import React, { PropTypes } from 'react';

const Fragment = (props, context) => {
type Props = {
forRoute: string,
forRoutes: [string],
withConditions: (location: Location) => bool,
children: ReactPropTypes.node
};

const Fragment = (
props: Props,
context: {
router: RouterContext
}
) => {
const { forRoute, forRoutes, withConditions, children } = props;
const { store } = context.router;
const { matchRoute } = store;
Expand Down Expand Up @@ -30,13 +46,6 @@ const Fragment = (props, context) => {
return <div>{children}</div>;
};

Fragment.propTypes = {
forRoute: PropTypes.string,
forRoutes: PropTypes.arrayOf(PropTypes.string),
withConditions: PropTypes.func,
children: PropTypes.node
};

Fragment.contextTypes = {
router: PropTypes.object
};
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow
import createStoreWithRouter, {
locationDidChange,
initializeCurrentLocation
Expand Down
15 changes: 14 additions & 1 deletion src/initial-router-state.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
// @flow
import type {
Query,
History
} from 'history';

import createMatcher from './create-matcher';

type Args = {
pathname: string,
query: Query,
routes: {[key: string]: Object},
history: History
};

export default ({
pathname = '/',
query = {},
routes,
history
}) => ({
}: Args) => ({
...history.createLocation({
pathname,
query
Expand Down
30 changes: 19 additions & 11 deletions src/link.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
// @flow
import type { LocationDescriptor } from 'history';
import type { RouterContext } from './provider';
import React, { Component, PropTypes } from 'react';

import { PUSH, REPLACE } from './action-types';

type Props = {
href: string | LocationDescriptor,
replaceState: bool,
persistQuery: bool,
target: string,
className: string,
style: Object,
children: ReactPropTypes.node
};

const LEFT_MOUSE_BUTTON = 0;

const normalizeHref = location =>
Expand Down Expand Up @@ -61,7 +74,12 @@ const onClick = ({e, target, location, replaceState, router}) => {
}
};

const Link = (props, context) => {
const Link = (
props: Props,
context: {
router: RouterContext
}
) => {
const {
href,
target,
Expand Down Expand Up @@ -100,16 +118,6 @@ const Link = (props, context) => {
);
};

Link.propTypes = {
href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
replaceState: PropTypes.bool,
persistQuery: PropTypes.bool,
target: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
children: PropTypes.node
};

Link.contextTypes = {
router: PropTypes.object
};
Expand Down
28 changes: 20 additions & 8 deletions src/provider.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
// @flow
import type { Store } from 'redux';

import React, { Component, PropTypes } from 'react';

export type RouterContext = { store: Store };

type Props = {
store: Object,
children: ReactPropTypes.node
};

export class RouterProvider extends Component {
constructor(props) {
router: { store: Store };

constructor(props: Props) {
super(props);
this.router = {
store: props.store
Expand All @@ -23,12 +35,12 @@ RouterProvider.childContextTypes = {
router: PropTypes.object
};

RouterProvider.propTypes = {
children: PropTypes.node,
store: PropTypes.object
type ProvideRouterArgs = {
store: Object
};

export default ({ store }) => ComposedComponent => props =>
<RouterProvider store={store}>
<ComposedComponent {...props} />
</RouterProvider>;
export default ({ store }: ProvideRouterArgs) =>
(ComposedComponent: ReactClass<*>) => (props: Object) =>
<RouterProvider store={store}>
<ComposedComponent {...props} />
</RouterProvider>;
7 changes: 5 additions & 2 deletions src/reducer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// @flow
import type { Action } from 'redux';
import type { Location } from 'history';
import { LOCATION_CHANGED } from './action-types';

export default (state = {}, action) => {
export default (state: ?Location | Object = {}, action: Action) => {
if (action.type === LOCATION_CHANGED) {
// No-op the initial route action
if (state && state.pathname === action.payload.pathname) {
Expand All @@ -9,7 +12,7 @@ export default (state = {}, action) => {

return {
...action.payload,
previous: state.current
previous: state && state.current
};
}
return state;
Expand Down
Loading