Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/modules / ___dist / router / src / OneRouterToRuleThemAll.js
Size: Mime:
"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;