Repository URL to install this package:
|
Version:
1.2.18 ▾
|
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "qs", {
enumerable: true,
get: function get() {
return _queryString.default;
}
});
Object.defineProperty(exports, "makeHistory", {
enumerable: true,
get: function get() {
return _makeHistory.default;
}
});
exports.default = exports.history = exports.oneRouter = exports.OneRouter = exports.withOneRouter = exports.provideOneRouter = exports.OneRouterContainer = void 0;
var _react = _interopRequireDefault(require("react"));
var _propTypes = require("prop-types");
var _queryString = _interopRequireDefault(require("query-string"));
var _mobx = require("xmobx/mobx");
var _chainAbleBoost = require("chain-able-boost");
var _matchPath = _interopRequireDefault(require("react-router-dom/matchPath"));
var _exotic = require("exotic");
var _makeHistory = _interopRequireDefault(require("./makeHistory"));
var _deps = require("./deps");
var _config = require("./config");
var _jsxFileName = "router/src/OneRouterToRuleThemAll.js";
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const history = (0, _makeHistory.default)();
exports.history = history;
const IS_BROWSER = typeof window === 'object'; // --- above all routers ---
// @todo optimize
// @decorate({
// // urlFromExpress
// gotoNext: action,
// full: computed,
// origin: computed,
// pathname: computed,
// searchParams: computed,
// history: observable.ref,
// getSearchParams: computed,
// parse: computed,
// update: action,
// delete: action,
// replace: action,
// clearHistory: action,
// set: action,
// toObj: computed(requiresReaction: false),
// entries: computed(requiresReaction: true),
// get: computed(requiresReaction: true),
// has: computed(requiresReaction: true),
// })
/**
* @todo subscribe, observe, unsubscribe
*
* @note modules/ does not hot reload
* extends Container
*/
class OneRouterToRuleThemAll {
// History | HistoryType
// hard to import the typing that itself imports this file...
// history: History
// observersList: Array<Function>
constructor() {
// this.observersList = new WeakMap()
this.observersList = []; // super()
// @todo can use this to avoid making all history and router observable
this.store = _mobx.observable.map(); // for @overriding parent entries
this.entries = this.toObj; // allow using .get but on our .entries
this.get = key => {
const entries = this.entries();
return entries[key];
};
this.set = (key, value) => {
this.store.set(key, value);
return this;
};
this.from = obj => {
const keys = Object.keys(obj);
let didChange = false; // const original = stringify(this.entries())
// const changed = stringify(obj)
// if (original === changed) {
// return
// }
// // go through keys
keys.forEach(key => {
const value = obj[key]; // update if it changed
if (this.store.get(key) !== value) {
this.store.set(key, value);
didChange = true;
}
}); // // could also set the .diff
// if (didChange === true) {
// this.observersList.forEach(subscriber => subscriber(this))
// }
}; // for backend routing
// this.urlFromExpress = EMPTY_STRING
/**
* @note I'm not actually sure if `extendObservable`
* is any different than just doing this.property = observable({})
* but I think it makes the instance have a $mobx property too
* to help tracks
* @todo @james ask Michel from mobx
*/
// setup observables
// this.history = observable(history)
// this.router = observable({})
// const types = {
// history,
// router: {},
// }
// extendObservable(this, types)
// this.history = history
this.router = {
location: {//
},
history: {
location: {}
}
};
}
get urlFromExpress() {
return (0, _exotic.isObj)(this.router) && (0, _exotic.isObj)(this.router.location) ? this.router.location.full || '' : '';
}
set urlFromExpress(oneUrl) {
// this.oneUrl = oneUrl
this.oneUrl = oneUrl;
this.router.location = oneUrl || {};
this.router.history.location = oneUrl || this.router.location;
}
toString(pretty = true) {
const entries = this.entries();
return pretty === true ? (0, _chainAbleBoost.stringify)(entries, null, 2) : (0, _chainAbleBoost.stringify)(entries);
}
has(data) {
return this.toString(false).includes(data);
}
/**
* @access private
* @param {*} msg
* @param {*} data
*/
log(msg, data) {} // if (this.getDebug()) {
// if (data === undefined && typeof msg === 'string') {
// console.log(msg)
// } else {
// console.log(stringify({ [msg]: data }, null, 2))
// }
// }
/**
* @see prevnextcontainer
* @see history/goBack
* @description common naming
* @alias goBack
* @alias gotoBack
*/
gotoPrev() {
/**
* @todo - should unify this history usage stuff & simplify v3
*/
this.router.history.goBack();
}
gotoNext() {
this.router.history.goForward();
}
/**
* @access public
* @example https://uxui.com/route?search=eh#hash
*
* @alias fullyQualifiedWebAddress
* @alias absolute
* @alias absoluteWebAddress
* @name full
*/
get full() {
return IS_BROWSER ? window.location.href : this.router.history.location.href || this.router.history.location.origin + '/' + this.router.history.location.pathname;
} // also available in entries
get origin() {
return this.router.history.location.origin;
}
/**
* @access private
* (pathname.match(/\w+/gim) || []).join('')
*/
get pathname() {
if (IS_BROWSER) {
return window.location.pathname || '@@empty-browser';
}
if (this.oneUrl !== undefined) {
return this.oneUrl.pathname;
}
if (typeof global === 'object' && global.oneUrl !== undefined) {
return global.oneUrl.pathname;
}
if (this.urlFromExpress !== _exotic.EMPTY_STRING) {
return this.urlFromExpress;
}
if ((0, _exotic.isObj)(this.router) && (0, _exotic.isObj)(this.router.history) && // @todo - this was the issue
(0, _exotic.isObj)(this.router.history.location) && (0, _exotic.isString)(this.router.history.location.pathname) && this.router.history.location.pathname !== '/') {
return this.router.history.location; // return this.history.pathname
}
if (this.history.pathname) {
return this.history.pathname;
}
if (this.history.location.pathname) {
return this.history.location.pathname;
}
const unknown = new Error('could not find pathname');
console.error(unknown);
console.log(this);
return '@@unknown';
}
/**
* @private
* @example
* localhost/eh/10/
* /eh/:categoryId
* match.params.categoryId
* => 10
*
* @type {computed}
* @return {string | number | boolean | object | undefined}
*/
get matched() {
const matched = {};
const pathname = this.pathname;
const routePathsList = _config.config.get('routePathsList'); // go through our routes
// use the sealed object for perf
// match params (which has a cache)
// merge with matched object
// return matched
for (let index = 0; index < routePathsList.length; index++) {
const matchablePath = routePathsList[index];
_deps.routePathCurrent.path = matchablePath;
const matchedFromPath = (0, _matchPath.default)(pathname, _deps.routePathCurrent); // !!!!!!!!! IMPORTANT
// console.log('____MATCHED___', index, pathname, matchedFromPath, matchablePath)
if ((0, _exotic.isObj)(matchedFromPath)) {
// this.log(pathname, { routePathCurrent, matchedFromPath, pathname })
// const params = { ...matchedFromPath, ...matchedFromPath.params }
const params = _objectSpread({}, matchedFromPath.params);
Object.assign(matched, params); // access to which route had the data
// Object.defineProperty(matched, '@@matches', {
// writable: false,
// configurable: false,
// enumerable: false,
// value: matchedFromPath,
// })
}
} // react router always counts this as root
// if (this.props && isObj(this.props.match)) {
// // matched = {
// // ...matched,
// // ...this.props.match,
// // ...this.props.match.params,
// // }
// Object.assign(matched, this.props.match.params)
// }
// else {
// matched.match = matched
// }
return matched;
}
/**
* @private
* @see this.getSearchParams
* @return {String}
*/
get searchParams() {
return this.getSearchParams();
}
/**
* @public
* @description this.router this.router.history history
*
* @todo regression when doing babel
*
* @type {mobx.Computed}
* @return {ReactRouter.history | History}
*/
get history() {
return ((0, _exotic.isObj)(this.router) && (0, _exotic.isObj)(this.router.history) ? this.router.history : history) || {};
}
/**
* @see http://unixpapa.com/js/querystring.html
* @api https://github.com/sindresorhus/query-string#nesting
*
* @todo these entries could be observable...
*
* @param {string} [fallback='']
* @return {object | string | array}
* @type {computed}
*/
getSearchParams(fallback = '') {
const parsed = this.history.location.search ? _queryString.default.parse(this.history.location.search) : fallback;
if (!parsed) {
return parsed;
} // could optimize this
const searchParams = {};
Object.assign(searchParams, parsed);
/**
* it does not support doing things like
* @example &eh=1&eh=10
*/
if (IS_BROWSER) {
const params = new URLSearchParams(window.location.search); // issue exporting fromIteratorToArray
const entries = Array.from(params);
const obj = (0, _exotic.fromPairsToObj)(entries); // searchParams = { ...parsed, ...obj }
Object.assign(searchParams, obj);
}
const final = this.parse(searchParams);
return final;
}
/**
* @param {Object<string, JSON>} parsed
* @return {Object<string, Object>} destringified
*/
parse(parsed) {
if ((0, _exotic.isObj)(parsed)) {
Object.keys(parsed).forEach(key => {
// remove undefined
if ((0, _exotic.isNil)(parsed[key]) === true) {
delete parsed[key];
}
if ((0, _exotic.isString)(parsed[key]) === false) {
return;
}
if ((0, _deps.isURIEncoded)(parsed[key]) === true) {
parsed[key] = decodeURIComponent(parsed[key]);
}
if ((0, _exotic.isValidJSON)(parsed[key]) === true) {
parsed[key] = (0, _deps.parseJSON)(parsed[key]);
if ((0, _exotic.isObj)(parsed[key])) {
const toJSON = () => (0, _chainAbleBoost.stringify)(parsed[key]);
(0, _chainAbleBoost.defineFinal)(parsed[key], 'toString', toJSON);
(0, _chainAbleBoost.defineFinal)(parsed[key], Symbol.toPrimitive, toJSON);
}
}
});
}
return parsed;
}
/**
* @todo return better intelisense here
* @alias entries
* @return {object}
*/
toObj() {
const pathname = this.pathname; // const pathstring = stripNonAlphaNumeric(pathname)
const browserLocation = IS_BROWSER ? window.location : {};
const params = this.getSearchParams({});
const matched = this.matched;
const flattenedActions = _objectSpread({}, browserLocation, this.history, this.history.location, this.router); // autofixSafe
const entries = _objectSpread({
pathname
}, params, matched); // @todo @james
// runInAction(() => {
// const { hash, key, search, state } = this.history.location
// const length = flattenedActions.length
// this.from({
// pathname,
// params,
// length,
// search,
// state,
// key,
// hash,
// matched,
// // entries,
// })
// // this
// // .set('obj')
// // .set('pathname', pathname)
// // .set('params', params)
// // .set('matched', matched)
// // .set('entries', entries)
// // .set('key', key)
// // .set('state', state)
// // .set('hash', hash)
// // .set('search', search)
// // .set('length', length)
// })
const mergedEntries = _objectSpread({}, flattenedActions, entries); // this.log('___entries___', entries)
return mergedEntries;
}
/**
* @tutorial https://github.com/ReactTraining/history/blob/master/modules/LocationUtils.js
* @alias push
* @alias merge
* @alias updateQueryParams
*
* @fires observable update
* @description any kind of url update can be done here
*
* @param {Object | String | Array} to
* @return {OneRouterToRuleThemAll} @chainable
*
* @todo should chain it :(
*
* @example
* [state, pathname, hash]
* toUrlOrPathNameOrParams, optionalParamAsDataOrPath
* let to = toUrlOrPathNameOrParams
* let state = optionalParamAsDataOrPath
* let hash
* if (arguments.length === 2)
*/
update(to, options = {
shouldStringify: true,
shouldMerge: true,
shouldUseNative: false
}) {
this.log('oneRouterUpdate', {
to
});
/**
* @static @todo @demo @fixme
*/
// application.showLoadingGauge(true, 1000)
if ((0, _exotic.isObj)(to)) {
this.log('oneRouterUpdate_isObj'); // if (to.search) {
// to = to.search
// }
// if (to.hash) {
// return this.update('#' + to.hash)
// }
if (to.pathname) {
throw new Error(`avoid stringifying history-like properties: ` + to);
} // Commented the below line for filter to work in plp
const searchParams = {}; // const searchParams = this.getSearchParams(false)
this.log({
to
});
if (searchParams && options.shouldMerge !== false) {
to = (0, _chainAbleBoost.merge)(searchParams, to);
}
this.log({
to
}); // const params = options.shouldStringify ? qs.stringify(to) : to
const params = (0, _deps.goodLookingStringify)(to);
if (options.shouldUseNative === true) {
// to, document.title, url
window.history.pushState(to, '', this.pathname + '?' + params);
return this;
} else {
this.history.push({
pathname: this.pathname,
search: params
});
}
return this;
} // @note this works
// but we don't want to use this how it's been used in domain currently
// if (isHash(to)) {
// this.history.push({ pathname: this.pathname, hash: to })
// return this
// }
if ((0, _deps.isStringifiedParams)(to)) {
this.log('isStringifiedParams');
return this.update(_queryString.default.parse(to)); // this.history.push({ search: to })
// return this
}
if ((0, _deps.isFullyQualifiedWebAddress)(to)) {
this.log('isFullyQualifiedWebAddress'); // check our local routes
// otherwise goto
// routePathsList.includes(to)
// return this.router.goto(to)
this.history.push({
pathname: to
});
return this;
}
if ((0, _deps.isRelativeWebAddress)(to)) {
this.log('isFullyQualifiedWebAddress'); // check our local routes
// return this.router.replace()
this.history.replace(to);
return this;
}
return this;
}
/**
* @param {Object | String} param query param to delete
* @return {OneRouter} @chainable
*/
delete(param) {
if ((0, _exotic.isString)(param)) {
const searchParamsRaw = this.getSearchParams(false);
const searchParams = this.parse(searchParamsRaw);
if (searchParams === false) {
return this;
} else {
delete searchParams[param];
return this.update(searchParams);
}
}
return this;
}
/**
* @alias goto
* @inheritdoc
* @see this.update
* @description same as update, but goto this url instead of merging
* @type {Action}
*
* @param {String} path
* @return {OneRouter} @chainable
*/
replace(path) {
this.router.history.replace(path);
return this;
}
/**
* @type {Action}
* @description reset url, goto root
* @return {OneRouter} @chainable
*/
clearHistory() {
this.history.replace({
pathname: this.location.pathname,
search: ``
});
return this;
}
/**
* @description
* @fires onChange
* @event onChange
*
* @todo add these aliases
* @alias observe
* @alias subscribe
*
* @param {Function} subscriber called when observable changes
* @return {OneRouter} @chainable
*/
onChange(subscriber) {
/**
* @api https://mobx.js.org/refguide/observe
*/
// observe(this, subscriber)
// observe(this.store, subscriber)
// if (this.observersList.includes(subscriber) === false) {
// this.observersList.set(subscriber, true)
// this.observersList.push(subscriber)
// const subscribeToRouteChange = this.get('subscribe')
const subscribeToRouteChange = this.router && this.router.history && this.router.history.listen;
if ((0, _exotic.isFunction)(subscribeToRouteChange)) {
subscribeToRouteChange(subscriber);
} else {
console.assert(typeof subscribeToRouteChange === 'function', 'ON_CHANGE_ONE_ROUTER_NOT_FUNCTION');
} // }
/**
* @deprecated
*
* @api https://mobx.js.org/refguide/reaction.html
* @todo could use ^ probably better perf, easy to swap out later
*
* // this was not clear, did not work
* @api https://mobx.js.org/refguide/autorun.html
* Just like the @observer decorator/function,
* autorun will only observe data
* that is used during the execution of the provided function.
*
* @note we could used boxed values but the docs say
* > since MobX tracks changes to boxes automatically,
* in most cases it is better to use a reaction
* like mobx.autorun instead.
*/
// autorun(subscriber, this)
return this;
}
/**
* window.location.reload()
*/
reload() {
window.location.reload();
}
get isRestricted() {
const restrictedRoutes = this.get('restrictedRoutes') || []; // @todo use chain-able/matcher
// ^ regexp, function, or string, or string wildcard/route-params
// isMatch(restrictedRoutes, this.pathname)
const isRestricted = restrictedRoutes.includes(this.pathname); // return as bool
if (isRestricted) {
return true;
} else {
return false;
}
}
}
const oneRouter = new OneRouterToRuleThemAll();
exports.oneRouter = oneRouter;
if (IS_BROWSER) {
window.oneRouter = oneRouter;
} // ========= @todo split =========
// ^^^^^^^^ if variable declaration was another file, single responsibility, would work ^^^^^
// provide it all the way down through context
// @withRouter
class OneRouter extends _react.default.Component {
getChildContext() {
return {
oneRouter,
router: _objectSpread({}, this.context.router, {
history: this.props.history,
route: {
location: this.props.history.location,
match: this.state.match
}
})
};
}
}
exports.OneRouter = OneRouter;
Object.defineProperty(OneRouter, "childContextTypes", {
configurable: true,
enumerable: true,
writable: true,
value: {
router: _propTypes.object.isRequired,
oneRouter: _propTypes.object
}
});
const provideOneRouter = _props => {
// PureComponent
// @withRouter
class OneRouterWrap extends _react.default.Component {
componentWillMount() {
const props = this.props; // console.log('PROPS_WILLMOUNT__', JSON.stringify(props, null, 2))
oneRouter.props = props;
oneRouter.router = this.context.router || props.router; // oneRouter.history = oneRouter.history.router || oneRouter.history
// const { history, isSSR } = this.props
// if (!isSSR) this.unsubscribeFromHistory = history.listen(this.handleLocationChange)
// this.handleLocationChange(history.location)
} // componentWillUnmount() {
// if (this.unsubscribeFromHistory) {
// this.unsubscribeFromHistory()
// }
// }
// handleLocationChange = location => {
// this.store.notifyRouteChange(location)
// }
// ===
// componentWillReceiveProps(props) {
// // console.log('___PROPS___', JSON.stringify(props, null, 2))
// props = props || this.props
// oneRouter.props = clone(props)
// oneRouter.router = this.context.router || props.router
// oneRouter.history = oneRouter.history.router || oneRouter.history
// oneRouter.entries()
// }
render() {
return null;
}
}
Object.defineProperty(OneRouterWrap, "contextTypes", {
configurable: true,
enumerable: true,
writable: true,
value: {
router: _propTypes.object // can trigger oneRouter here too
// @michael @bhargavi
// // @todo !!!
// oneRouter.onChange(change => {
// // import {sessionContainer} from 'state/container'
// // const { isRegisteredUser, isGuestUser } = sessionContainer
// // () => isRegisteredUser
// // const restrictedRoutes = ['/']
// // oneRouter.set('restrictedRoutes', restrictedRoutes)
// // if (oneRouter.isRestricted === true) {
// // oneRouter.update('/login')
// // }
// })
}
});
return _react.default.createElement(OneRouterWrap, _props);
}; // not used?
exports.provideOneRouter = provideOneRouter;
const withOneRouter = Target => {
return class OneRouterWrapper extends _react.default.Component {
render() {
const attributes = _objectSpread({}, oneRouter, this.props);
return _react.default.createElement(Target, _extends({}, attributes, {
__source: {
fileName: _jsxFileName,
lineNumber: 836
},
__self: this
}));
}
};
};
exports.withOneRouter = withOneRouter;
oneRouter.OneRouter = OneRouter;
oneRouter.connectToRouter = withOneRouter;
oneRouter.withOneRouter = withOneRouter; // oneRouter.history = history
oneRouter.provide = provideOneRouter;
const OneRouterContainer = provideOneRouter;
exports.OneRouterContainer = OneRouterContainer;
var _default = oneRouter;
exports.default = _default;