moy-router
Version:
Give a solution for moy-dom router management.
218 lines (192 loc) • 6.6 kB
JavaScript
/**
* moy-router v1.0.6
* (jp) 2018 murakami
* @license MIT
*/
import { compose, prepend, join, map, entries, reduce, split, ifElse, includes, prop, concat, type, Maybe, and, or, assoc, __, path, curry, tap, always, length, dissoc } from 'moy-fp';
import pathToRegExp from 'path-to-regexp';
import { Element } from 'moy-dom';
var slicedToArray = function () {
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
var stringifyQuery = compose(prepend('?'), join('&'), map(compose(join('='), map(encodeURIComponent))), entries);
var parseQuery = compose(reduce(function (query, params) {
var _compose = compose(map(decodeURIComponent), split('='))(params),
_compose2 = slicedToArray(_compose, 2),
key = _compose2[0],
value = _compose2[1];
query[key] = value;
return query;
}, {}), split('&'));
var pushQueryAndHash = ifElse(function (queryAndHash, route) {
var _queryAndHash$split = queryAndHash.split('#'),
_queryAndHash$split2 = slicedToArray(_queryAndHash$split, 2),
query = _queryAndHash$split2[0],
hash = _queryAndHash$split2[1];
route.query = query ? parseQuery(query) : undefined;
route.hash = hash;
})(function (queryAndHash, route) {
return route.query = parseQuery(queryAndHash);
})(includes('#'));
var handleStringPathname = function handleStringPathname(pathname) {
var route = {};
route.pathname = pathname;
var queryAndHash = compose(prop(1), split('?'))(pathname);
!queryAndHash && pathname.includes('#') && (queryAndHash = compose(prepend('#'), prop(1), split('#'))(pathname));
queryAndHash && pushQueryAndHash(queryAndHash, route);
return route;
};
var handleObjectPathname = function handleObjectPathname(route) {
if (route.params) {
route.pathname = pathToRegExp.compile(route.pathname)(route.params);
}
if (route.query) {
route.pathname += stringifyQuery(route.query);
}
if (route.hash) {
route.pathname += concat(route.hash)('#');
}
return route;
};
var parseRoute = ifElse(handleStringPathname)(handleObjectPathname)(type('String'));
var windowMonad = Maybe.of(window);
var createHistory = (function (current) {
return and(windowMonad.map(function (window) {
return window.addEventListener('popstate', compose(assoc('route', __, current), path(['state', 'route'])));
}))({
back: function back() {
windowMonad.map(function (window) {
return window.history.back();
});
},
go: function go(index) {
windowMonad.map(function (window) {
return window.history.go(index);
});
},
forward: function forward() {
windowMonad.map(function (window) {
return window.history.forward();
});
},
replace: function replace(routeConfig) {
var route = parseRoute(routeConfig);
windowMonad.map(function (window) {
return or(window.history.replaceState(route, '', route.pathname))(assoc('route', route, current));
});
},
push: function push(routeConfig) {
var route = parseRoute(routeConfig);
windowMonad.map(function (window) {
return or(window.history.replaceState(route, '', route.pathname))(assoc('route', route, current));
});
}
});
});
var RouterView = curry(function (current, routes) {
return function (props) {
var currentPathname = current.route.pathname.split('?').map(split('#'))[0][0];
var maybeRouteNode = compose(map(function (route) {
return route.component(Object.assign({}, props, {
route: {
pathname: current.route.pathname,
params: current.route.params,
query: current.route.query,
hash: current.route.hash,
state: current.route.state
}
}));
}), map(tap(ifElse(function (currentRoute) {
var matched = currentPathname.match(currentRoute.pathnameRegExp);
current.route.params = {};
currentRoute.paramKeys.forEach(function (key, index) {
current.route.params[key.name] = decodeURIComponent(matched[index + 1]);
});
})(always(undefined))(compose(and(!current.route.params), length, prop('paramKeys'))))), Maybe.of)(routes.find(function (route) {
return route.pathnameRegExp.test(currentPathname);
}));
return maybeRouteNode.isJust ? maybeRouteNode.join() : '';
};
});
var RouterLink = curry(function (push, _ref) {
var to = _ref.to,
innerNode = _ref.innerNode;
return Element.of('a', {
href: type('String', to) ? to : to.pathname,
onclick: function onclick(e) {
e.preventDefault();
push(to);
}
}, innerNode);
});
var getLocation = (function () {
return Maybe.of(window).map(function (window) {
return window.location.pathname + window.location.search + window.location.hash;
});
});
var createRouter = (function (routes, subscription) {
var routesRegExpPathname = map(compose(dissoc('pathname'), function (route) {
return assoc('pathnameRegExp')(pathToRegExp(prop('pathname', route), prop('paramKeys', route), {
sensitive: true
}))(route);
}, tap(function (route) {
return route.paramKeys = [];
})))(routes);
var currentRoute = getLocation().chain(parseRoute);
var current = new Proxy({
route: currentRoute
}, {
set: function set(target, prop$$1, value) {
Reflect.set(target, prop$$1, value);
prop$$1 === 'route' && subscription();
return true;
}
});
var _createHistory = createHistory(current),
back = _createHistory.back,
go = _createHistory.go,
forward = _createHistory.forward,
replace = _createHistory.replace,
push = _createHistory.push;
var router = {
back: back,
go: go,
forward: forward,
replace: replace,
push: push,
RouterView: RouterView(current, routesRegExpPathname),
RouterLink: RouterLink(push)
};
return router;
});
export { createRouter };