├── .nvmrc
├── .idea
├── scopes
│ ├── index.xml
│ └── source.xml
├── encodings.xml
├── codeStyles
│ └── codeStyleConfig.xml
├── misc.xml
├── vcs.xml
├── jsLibraryMappings.xml
├── modules.xml
├── codeStyleSettings.xml
├── mobx-router5.iml
├── watcherTasks.xml
└── inspectionProfiles
│ └── Project_Default.xml
├── src
├── index.js
├── modules
│ ├── mobxPlugin.js
│ └── RouterStore.js
└── index.d.ts
├── .gitignore
├── tools
├── .eslintrc
└── build.js
├── __tests__
├── .eslintrc
└── main.js
├── .travis.yml
├── .editorconfig
├── .gitattributes
├── LICENSE.txt
├── .eslintrc
├── package.json
├── CONTRIBUTING.md
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | v8.11.1
2 |
--------------------------------------------------------------------------------
/.idea/scopes/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/scopes/source.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import mobxPlugin from './modules/mobxPlugin';
2 | import RouterStore from './modules/RouterStore';
3 |
4 | export {
5 | mobxPlugin,
6 | RouterStore,
7 | };
8 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Include your project-specific ignores in this file
2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
3 |
4 | coverage
5 | dist
6 | docs/dist
7 | node_modules
8 | npm-debug.log
9 |
--------------------------------------------------------------------------------
/.idea/jsLibraryMappings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/tools/.eslintrc:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "env": {
4 | "node": true,
5 | "browser": false
6 | },
7 | "globals": {"Promise": true},
8 | "plugins": ["import"],
9 | "rules": {
10 | "import/no-extraneous-dependencies": [
11 | "error",
12 | {
13 | "optionalDependencies": false,
14 | "peerDependencies": false
15 | }
16 | ],
17 | "strict": "off"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/__tests__/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | },
5 | "plugins": ["import"],
6 | "rules": {
7 | "import/no-extraneous-dependencies": [
8 | "error",
9 | {
10 | "optionalDependencies": false,
11 | "peerDependencies": true,
12 | "devDependencies": true
13 | }
14 | ],
15 | "no-unused-expressions": "off",
16 | "padded-blocks": "off"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | branches:
7 | only:
8 | - master
9 | notifications:
10 | email: false
11 | node_js:
12 | - '8'
13 | before_script:
14 | - npm prune
15 | script:
16 | - npm run lint
17 | - npm run test:cover
18 | - npm run build
19 | after_success:
20 | - npm run coveralls
21 | - npx travis-deploy-once "npx semantic-release"
22 |
23 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | # Change these settings to your own preference
10 | indent_style = space
11 | indent_size = 2
12 |
13 | # We recommend you to keep these unchanged
14 | charset = utf-8
15 | end_of_line = lf
16 | insert_final_newline = true
17 | trim_trailing_whitespace = true
18 |
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Automatically normalize line endings for all text-based files
2 | # http://git-scm.com/docs/gitattributes#_end_of_line_conversion
3 | * text=auto
4 |
5 | # For the following file types, normalize line endings to LF on
6 | # checkin and prevent conversion to CRLF when they are checked out
7 | # (this is required in order to prevent newline related issues like,
8 | # for example, after the build script is run)
9 | .* text eol=lf
10 | *.css text eol=lf
11 | *.ejs text eol=lf
12 | *.js text eol=lf
13 | *.md text eol=lf
14 | *.txt text eol=lf
15 | *.json text eol=lf
16 |
--------------------------------------------------------------------------------
/.idea/mobx-router5.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/watcherTasks.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/modules/mobxPlugin.js:
--------------------------------------------------------------------------------
1 |
2 | // TODO
3 | const defaultOptions = {};
4 |
5 | function mobxPluginFactory(routerStore, options = defaultOptions) {
6 | function mobxPlugin(router, dependencies) {
7 | // NOTE: cross-referencing objects
8 | router.setDependency('routerStore', routerStore);
9 | routerStore.setRouter(router);
10 |
11 | // Implemented methods
12 | return {
13 | onTransitionStart(toState, fromState) {
14 | routerStore.onTransitionStart(toState, fromState);
15 | },
16 | onTransitionSuccess(toState, fromState, opts) {
17 | routerStore.onTransitionSuccess(toState, fromState, opts);
18 | },
19 | onTransitionCancel(toState, fromState) {
20 | routerStore.onTransitionCancel(toState, fromState);
21 | },
22 | onTransitionError(toState, fromState, err) {
23 | routerStore.onTransitionError(toState, fromState, err);
24 | },
25 | };
26 | }
27 |
28 | mobxPlugin.pluginName = 'MOBX_PLUGIN';
29 |
30 | return mobxPlugin;
31 | }
32 |
33 | export default mobxPluginFactory;
34 |
35 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [2017] [Leonardo Gentile]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import {State} from "router5";
2 | import {IComputedValue} from "mobx/lib/core/computedvalue";
3 | import {Route, Router} from "router5/create-router";
4 | import {Options} from "router5/core/navigation";
5 | import {PluginFactory} from "router5/core/plugins";
6 | import {Params} from "router5";
7 |
8 | declare module "mobx-router5" {
9 | export class RouterStore {
10 | public router: Router;
11 | public route: Route;
12 | public previousRoute: Route;
13 | public transitionRoute: Route;
14 | public transitionError: any;
15 | public intersectionNode: string;
16 | // public toActivate: IObservableArray;
17 | // public toDeactivate: IObservableArray;
18 |
19 | public setRouter: (router: Router) => void;
20 | public updateRoute: (routeType: string, route: Route) => void;
21 | public resetRoute: (routeType: string) => void;
22 | public onTransitionStart: (route: Route, previousRoute: Route) => void;
23 | public onTransitionSuccess: (route: Route, previousRoute: Route, opts: Options) => void;
24 | public onTransitionCancel: (route: Route, previousRoute: Route) => void;
25 | public onTransitionError: (route: Route, previousRoute: Route, transitionError: any) => void;
26 | public clearErrors: () => void;
27 | public navigate: (toRoute: string, params?: Params) => void;
28 | public shouldUpdateNodeFactory: (nodeName: string) => IComputedValue<(toState: State, fromState?: State) => Boolean>;
29 | }
30 |
31 | export function mobxPlugin(routerStore: RouterStore): PluginFactory;
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "browser": true
5 | },
6 | // Parser
7 | "parser": "babel-eslint",
8 | "parserOptions": {
9 | // ECMA Features
10 | "ecmaFeatures": {
11 | "arrowFunctions": true,
12 | "blockBindings": true,
13 | "classes": true,
14 | "defaultParams": true,
15 | "destructuring": true,
16 | "modules": true,
17 | "objectLiteralComputedProperties": true,
18 | "templateStrings": true,
19 | "jsx": true
20 | }
21 | },
22 | "extends": [
23 | "eslint:recommended",
24 | "plugin:import/errors",
25 | "plugin:import/warnings"
26 | ],
27 | "rules": {
28 | // Possible Errors
29 | "no-dupe-args": 2,
30 | "no-dupe-keys": 2,
31 | "no-empty": 2,
32 | "no-func-assign": 2,
33 | "no-inner-declarations": 2,
34 | "no-unreachable": 2,
35 | "no-unexpected-multiline": 2,
36 | // Best practices
37 | "consistent-return": 0,
38 | "curly": [2, "multi-line"],
39 | "eqeqeq": 2,
40 | "no-else-return": 2,
41 | "no-multi-spaces": 0,
42 | // Strict mode
43 | "strict": 0,
44 | // Variables
45 | "no-shadow": 0,
46 | "no-unused-vars": [2, { "args": "none" }],
47 | "no-use-before-define": 0,
48 | // Style
49 | "brace-style": [1, "stroustrup"],
50 | "comma-spacing": [2, {"before": false, "after": true}],
51 | "comma-style": [2, "last"],
52 | "consistent-this": [2, "that"],
53 | "lines-around-comment": [2, {"allowBlockStart": true}],
54 | "key-spacing": 0,
55 | "new-parens": 0,
56 | "quotes": [2, "single", "avoid-escape"],
57 | "no-underscore-dangle": 0,
58 | "no-unneeded-ternary": 2,
59 | "semi": 2,
60 | // ES6
61 | "no-var": 2,
62 | "no-this-before-super": 2,
63 | "object-shorthand": 2
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/tools/build.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Babel Starter Kit (https://www.kriasoft.com/babel-starter-kit)
3 | *
4 | * Copyright © 2015-2016 Kriasoft, LLC. All rights reserved.
5 | *
6 | * This source code is licensed under the MIT license found in the
7 | * LICENSE.txt file in the root directory of this source tree.
8 | */
9 |
10 | 'use strict';
11 |
12 | const fs = require('fs');
13 | const path = require('path');
14 | const del = require('del');
15 | const rollup = require('rollup');
16 | const babel = require('rollup-plugin-babel');
17 | const pkg = require('../package.json');
18 |
19 | let promise = Promise.resolve();
20 |
21 |
22 | // Clean up the output directory
23 | promise = promise.then(() => del(['dist/*']));
24 |
25 | // Compile source code into a distributable format with Babel
26 | ['es', 'cjs', 'umd'].forEach((format) => {
27 | promise = promise.then(() => rollup.rollup({
28 | entry: 'src/index.js',
29 | external: Object.keys(pkg.dependencies).concat(Object.keys(pkg.peerDependencies)),
30 | plugins: [babel(Object.assign(pkg.babel, {
31 | babelrc: false,
32 | exclude: 'node_modules/**',
33 | runtimeHelpers: true, // because we use transform-runtime plugin (avoid repetition)
34 | presets: pkg.babel.presets.map(x => (x === 'latest' ? ['latest', { es2015: { modules: false } }] : x)),
35 | }))],
36 | })
37 | .then(bundle => bundle.write({
38 | dest: `dist/${format === 'cjs' ? 'index' : `index.${format}`}.js`,
39 | format,
40 | sourceMap: true,
41 | moduleName: format === 'umd' ? pkg.name : undefined,
42 | })));
43 | });
44 |
45 | // Copy typings file to dist.
46 | promise = promise.then(() => {
47 | const typingsFile = fs.readFileSync(path.resolve(__dirname, '..', 'src', 'index.d.ts'), { encoding: 'utf-8' });
48 | fs.writeFileSync(path.resolve(__dirname, '..', 'dist', 'index.d.ts'), typingsFile, 'utf-8');
49 | });
50 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/modules/RouterStore.js:
--------------------------------------------------------------------------------
1 | import {observable, action, computed} from 'mobx';
2 | import transitionPath, {shouldUpdateNode} from 'router5-transition-path';
3 |
4 | class RouterStore {
5 |
6 | @observable.ref route = null;
7 | @observable.ref previousRoute = null;
8 | @observable.ref transitionRoute = null;
9 | @observable.ref transitionError = null;
10 | @observable.ref intersectionNode = '';
11 | @observable.ref toActivate = [];
12 | @observable.ref toDeactivate = [];
13 | // @observable currentView;
14 |
15 | router = null;
16 |
17 | constructor() {
18 | this.navigate = this.navigate.bind(this);
19 | this.shouldUpdateNodeFactory = this.shouldUpdateNodeFactory.bind(this);
20 | }
21 |
22 | setRouter(router) {
23 | this.router = router;
24 | }
25 |
26 | updateRoute(routeType, route) {
27 | this[routeType] = route;
28 | }
29 |
30 | resetRoute(routeType) {
31 | this[routeType] = null;
32 | }
33 |
34 | // ===========
35 | // = ACTIONS =
36 | // ===========
37 | // These are called by the plugin
38 | @action onTransitionStart = (route, previousRoute) => {
39 | this.updateRoute('transitionRoute', route);
40 | this.transitionError = null;
41 | };
42 |
43 | @action onTransitionSuccess = (route, previousRoute, opts) => {
44 | this.updateRoute('route', route);
45 | this.updateRoute('previousRoute', previousRoute);
46 | if (route) {
47 | const {intersection, toActivate, toDeactivate} = transitionPath(route, previousRoute);
48 | this.intersectionNode = opts.reload ? '' : intersection;
49 | this.toActivate = toActivate;
50 | this.toDeactivate = toDeactivate;
51 | }
52 | this.clearErrors();
53 | };
54 |
55 | @action onTransitionCancel = (route, previousRoute) => {
56 | this.resetRoute('transitionRoute');
57 | };
58 |
59 | @action onTransitionError = (route, previousRoute, transitionError) => {
60 | this.updateRoute('transitionRoute', route);
61 | this.updateRoute('previousRoute', previousRoute);
62 | this.transitionError = transitionError;
63 | };
64 |
65 | // These can be called manually
66 | @action clearErrors = () => {
67 | this.resetRoute('transitionRoute');
68 | this.transitionError = null;
69 | };
70 |
71 |
72 | // Public API, we can manually call these router methods
73 | // Note: These are not actions because they don't directly modify the state
74 |
75 | // Just an alias
76 | navigate = (name, params, opts) => {
77 | this.router.navigate(name, params, opts);
78 | };
79 |
80 | // Utility to calculate which react routeNode should update
81 | shouldUpdateNodeFactory = (nodeName) => {
82 | return computed(() => {
83 | return shouldUpdateNode(nodeName)(this.route, this.previousRoute);
84 | });
85 | };
86 |
87 |
88 | }
89 |
90 | export default RouterStore;
91 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobx-router5",
3 | "version": "0.0.0-development",
4 | "description": "Router5 integration with mobX",
5 | "homepage": "https://github.com/LeonardoGentile/mobx-router5",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/LeonardoGentile/mobx-router5.git"
9 | },
10 | "author": "Leonardo Gentile",
11 | "contributors": [],
12 | "license": "MIT",
13 | "keywords": [
14 | "router",
15 | "mobx",
16 | "router5",
17 | "functional",
18 | "routing",
19 | "observable",
20 | "reactive",
21 | "observer"
22 | ],
23 | "files": [
24 | "dist",
25 | "src",
26 | "README.md",
27 | "CHANGELOG.md",
28 | "CONTRIBUTING.md",
29 | "LICENSE.txt"
30 | ],
31 | "bugs": {
32 | "url": "https://github.com/LeonardoGentile/mobx-router5/issues"
33 | },
34 | "main": "dist/index.js",
35 | "module": "dist/index.es.js",
36 | "babel": {
37 | "comments": false,
38 | "presets": [
39 | "latest",
40 | "stage-0"
41 | ],
42 | "plugins": [
43 | "transform-runtime",
44 | "transform-decorators-legacy"
45 | ]
46 | },
47 | "dependencies": {
48 | "babel-runtime": "^6.11.6",
49 | "router5-transition-path": "5.3.0"
50 | },
51 | "devDependencies": {
52 | "babel-cli": "^6.16.0",
53 | "babel-core": "^6.17.0",
54 | "babel-eslint": "^7.0.0",
55 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
56 | "babel-plugin-transform-runtime": "^6.15.0",
57 | "babel-preset-latest": "^6.16.0",
58 | "babel-preset-stage-0": "^6.16.0",
59 | "babel-register": "^6.16.3",
60 | "commitizen": "2.9.6",
61 | "coveralls": "^2.11.14",
62 | "cz-conventional-changelog": "2.1.0",
63 | "del": "^2.2.2",
64 | "eslint": "^3.19.0",
65 | "eslint-plugin-import": "^2.2.0",
66 | "jest": "23.6.0",
67 | "mobx": "^4.2.0",
68 | "rollup": "0.41.6",
69 | "rollup-plugin-babel": "^2.7.1",
70 | "router5": "^6.1.2",
71 | "semantic-release": "^15.1.7",
72 | "semantic-release-conventional-commits": "^2.0.0",
73 | "travis-deploy-once": "4.4.1"
74 | },
75 | "peerDependencies": {
76 | "router5": "^6.1.2",
77 | "mobx": "^5.0.0"
78 | },
79 | "scripts": {
80 | "commit": "git-cz",
81 | "lint": "eslint src test tools",
82 | "test": "jest",
83 | "test:watch": "jest --watch",
84 | "test:cover": "jest --coverage",
85 | "coveralls": "cat ./coverage/lcov.info | coveralls",
86 | "build": "node tools/build",
87 | "publish:docs": "easystatic deploy docs --repo LeonardoGentile/mobx-router5",
88 | "start": "easystatic start docs",
89 | "semantic-release": "semantic-release",
90 | "travis-deploy-once": "travis-deploy-once"
91 | },
92 | "config": {
93 | "commitizen": {
94 | "path": "./node_modules/cz-conventional-changelog"
95 | }
96 | },
97 | "release": {
98 | "analyzeCommits": {
99 | "path": "semantic-release-conventional-commits",
100 | "minorTypes": [
101 | "feat",
102 | "minor"
103 | ],
104 | "patchTypes": [
105 | "fix",
106 | "patch",
107 | "docs",
108 | "refactor",
109 | "style",
110 | "perf"
111 | ]
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/__tests__/main.js:
--------------------------------------------------------------------------------
1 | import { createRouter } from 'router5';
2 | import { autorun } from 'mobx';
3 | import RouterStore from '../src/modules/RouterStore';
4 | import mobxPlugin from '../src/modules/mobxPlugin';
5 |
6 |
7 |
8 | const routes = [
9 | { name: 'a', path: '/a' },
10 | { name: 'b', path: '/b?param1¶m2' },
11 | { name: 'c', path: '/c', children: [
12 | { name: 'd', path: '/d?param1¶m2', },
13 | { name: 'e', path: '/e', },
14 | { name: 'f', path: '/f', },
15 | { name: 'g', path: '/g', children: [
16 | { name: 'h', path: '/h' },
17 | { name: 'i', path: '/i' },
18 | ]}
19 | ]}
20 | ];
21 |
22 |
23 | function createTestRouter(options) {
24 | const router = createRouter(routes, {...options});
25 | return router;
26 | }
27 |
28 |
29 | describe('mobxPlugin', () => {
30 | let router;
31 | let routerStore;
32 |
33 | function navigateTo(previousRoute, nextRouteName, routeParams, routeOptions, done) {
34 | router.navigate(nextRouteName, routeParams, routeOptions, function () {
35 | const nextRoute = router.getState();
36 | done(previousRoute, nextRoute);
37 | });
38 | }
39 |
40 | beforeEach(() => {
41 | routerStore = new RouterStore();
42 | router = createTestRouter({defaultRoute: 'a'});
43 | router.usePlugin(mobxPlugin(routerStore));
44 | });
45 |
46 | afterEach(() => {
47 | router.stop();
48 | });
49 |
50 | describe('router', () => {
51 | test('should be registered', () => {
52 | expect(router.hasPlugin('MOBX_PLUGIN')).toBe(true);
53 | });
54 | });
55 |
56 | describe('routerStore', () => {
57 |
58 | test('should add the router instance into the routerStore ', () => {
59 | expect(routerStore.router).toBe(router);
60 | });
61 |
62 | test(
63 | 'should have observable properties `route` and `previousRoute` reflecting the navigation',
64 | () => {
65 |
66 | router.start('a', function () {
67 | const previousRoute = router.getState();
68 | const nextRouteName = 'c.g.i';
69 | navigateTo(previousRoute, nextRouteName, {}, {}, assertFn);
70 | });
71 |
72 | function assertFn(previousRoute, nextRoute) {
73 | expect(routerStore.previousRoute.name).toBe('a');
74 | expect(routerStore.previousRoute.name).toBe(previousRoute.name);
75 | expect(routerStore.previousRoute.path).toBe('/a');
76 | expect(routerStore.previousRoute.path).toBe(previousRoute.path);
77 |
78 | expect(routerStore.route.name).toBe('c.g.i');
79 | expect(routerStore.route.name).toBe(nextRoute.name);
80 | expect(routerStore.route.path).toBe('/c/g/i');
81 | expect(routerStore.route.path).toBe(nextRoute.path);
82 | }
83 |
84 | }
85 | );
86 |
87 |
88 | test(
89 | 'should have observable properties `params` reflecting the navigation',
90 | (done) => {
91 |
92 | let count = 0;
93 | // assert inside autorun
94 | let disposer = autorun(function () {
95 | let route = routerStore.route;
96 | if (route) {
97 | let params = routerStore.route.params;
98 | if (count === 0) {
99 | count++;
100 | }
101 | else if (count === 1) {
102 | expect(params.param1).toBe('hello');
103 | expect(params.param2).toBe('there');
104 | count++;
105 | }
106 | else if (count === 2) {
107 | expect(params.param1).toBe('ok');
108 | expect(params.param2).toBe('yeah');
109 | count++;
110 | }
111 | else {
112 | expect(params.param1).toBe('good');
113 | expect(params.param2).toBe('bye');
114 |
115 | disposer();
116 |
117 | done(); // Tell Jest my test is done
118 | }
119 | }
120 | });
121 |
122 | router.start('a', function () {
123 | const previousRoute = router.getState();
124 | const nextRoute = 'b';
125 | navigateTo(previousRoute, nextRoute, {param1: 'hello', param2: 'there'}, {}, gotoC);
126 | });
127 |
128 | function gotoC(previousRoute, nextRoute) {
129 | const oldRoute = router.getState();
130 | navigateTo(oldRoute, 'c.d', {param1: 'ok', param2: 'yeah'}, {}, gotoCWithNewParams);
131 | }
132 |
133 | function gotoCWithNewParams(previousRoute, nextRoute) {
134 | const oldRoute = router.getState();
135 | navigateTo(oldRoute, 'c.d', {param1: 'good', param2: 'bye'}, {}, assertFn);
136 | }
137 |
138 | function assertFn(previousRoute, nextRoute) {
139 | expect(routerStore.previousRoute.params.param1).toBe('ok');
140 | expect(routerStore.previousRoute.params.param2).toBe('yeah');
141 |
142 | expect(routerStore.route.params.param1).toBe('good');
143 | expect(routerStore.route.params.param2).toBe('bye');
144 |
145 | }
146 | }
147 | );
148 |
149 |
150 | test(
151 | 'should have the correct intersection node for navigation: c.f -> c.g',
152 | () => {
153 | routerStore = new RouterStore();
154 | router = createTestRouter({defaultRoute: 'c.f'});
155 | router.usePlugin(mobxPlugin(routerStore));
156 |
157 | router.start('c.f', function () {
158 | const previousRoute = router.getState();
159 | const nextRouteName = 'c.g';
160 | navigateTo(previousRoute, nextRouteName, {}, {}, assertFn);
161 | });
162 |
163 | function assertFn(previousRoute, nextRoute) {
164 | expect(routerStore.intersectionNode).toBe('c');
165 | }
166 |
167 | }
168 | );
169 |
170 | test(
171 | 'should have the correct intersection node for navigation: b -> c.g.h',
172 | () => {
173 | routerStore = new RouterStore();
174 | router = createTestRouter({defaultRoute: 'b'});
175 | router.usePlugin(mobxPlugin(routerStore));
176 |
177 | router.start('b', function () {
178 | const previousRoute = router.getState();
179 | const nextRouteName = 'c.g.h';
180 | navigateTo(previousRoute, nextRouteName, {}, {}, assertFn);
181 | });
182 |
183 | function assertFn(previousRoute, nextRoute) {
184 | expect(routerStore.intersectionNode).toBe('');
185 | }
186 | }
187 | );
188 |
189 | });
190 |
191 | });
192 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing to mobx-router5
2 |
3 | Please take a moment to review this document in order to make the contribution process easy and
4 | effective for everyone involved.
5 |
6 | Following these guidelines helps to communicate that you respect the time of the developers
7 | managing and developing this open source project. In return, they should reciprocate that respect in
8 | addressing your issue or assessing patches and features.
9 |
10 |
11 | ### Using the issue tracker
12 |
13 | The [issue tracker](https://github.com/LeonardoGentile/mobx-router5/issues) is the preferred channel
14 | for [bug reports](#bugs), [features requests](#features) and [submitting pull requests](#pull-requests),
15 | but please respect the following restrictions:
16 |
17 | * Please **do not** use the issue tracker for personal support requests.
18 | [Stack Overflow](https://stackoverflow.com/) is a better places to get help.
19 |
20 | * Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of
21 | others.
22 |
23 |
24 |
25 | ### Bug reports
26 |
27 | A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are
28 | extremely helpful - thank you!
29 |
30 | Guidelines for bug reports:
31 |
32 | 1. **Use the GitHub issue search** — check if the issue has already been reported.
33 |
34 | 2. **Check if the issue has been fixed** — try to reproduce it using the latest `master` or
35 | development branch in the repository.
36 |
37 | 3. **Isolate the problem** — ideally create a reduced test case and a live example.
38 |
39 | A good bug report shouldn't leave others needing to chase you up for more information. Please try to
40 | be as detailed as possible in your report. What is your environment? What steps will reproduce the
41 | issue? What browser(s) and OS experience the problem? What would you expect to be the outcome? All
42 | these details will help people to fix any potential bugs.
43 |
44 | Example:
45 |
46 | > Short and descriptive example bug report title
47 | >
48 | > A summary of the issue and the environment (browser/OS, npm, node, rollup, webpack...with relative versions) in which it occurs. If suitable, include the
49 | > steps required to reproduce the bug.
50 | >
51 | > 1. This is the first step
52 | > 2. This is the second step
53 | > 3. Further steps, etc.
54 | >
55 | > `` - a link to the reduced test case
56 | >
57 | > Any other information you want to share that is relevant to the issue being reported. This might
58 | > include the lines of code that you have identified as causing the bug, and potential solutions
59 | > (and your opinions on their merits).
60 |
61 |
62 |
63 | ## Feature requests
64 |
65 | Feature requests are welcome. But take a moment to find out whether your idea fits with the scope
66 | and aims of the project. It's up to *you* to make a strong case to convince the project's developers
67 | of the merits of this feature. Please provide as much detail and context as possible.
68 |
69 |
70 |
71 | ## Pull requests
72 |
73 | Good pull requests - patches, improvements, new features - are a fantastic help. They should remain
74 | focused in scope and avoid containing unrelated commits.
75 |
76 | **Please ask first** before embarking on any significant pull request (e.g. implementing features,
77 | refactoring code, porting to a different language), otherwise you risk spending a lot of time
78 | working on something that the project's developers might not want to merge into the project.
79 |
80 | Please adhere to the coding conventions used throughout a project (indentation, accurate comments,
81 | etc.) and any other requirements (such as test coverage).
82 |
83 | Adhering to the following process is the best way to get your work included in the project:
84 |
85 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork, and configure
86 | the remotes:
87 |
88 | ```bash
89 | # Clone your fork of the repo into the current directory
90 | git clone https://github.com//mobx-router5.git
91 | # Navigate to the newly cloned directory
92 | cd mobx-router5
93 | # Assign the original repo to a remote called "upstream"
94 | git remote add upstream https://github.com/LeonardoGentile/mobx-router5.git
95 | # install deps
96 | npm install
97 | ```
98 |
99 | 2. If you cloned a while ago, get the latest changes from upstream:
100 |
101 | ```bash
102 | git checkout master
103 | git pull upstream master
104 | ```
105 |
106 | 3. Create a new topic branch (off the main project development branch) to contain your feature,
107 | change, or fix:
108 |
109 | ```bash
110 | git checkout -b
111 | ```
112 |
113 | 4. Commit your changes in logical chunks. Please adhere to these [git commit message guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#-git-commit-guidelines)
114 | or your code is unlikely be merged into the main project.
115 |
116 | This repo uses [semantic-release](https://github.com/semantic-release/semantic-release) to automatically
117 | bump npm version based on standard commit messages, so it is very important to follow the guidelines.
118 | To help writing standard commit messages this repo uses [commitizen](http://commitizen.github.io/cz-cli/).
119 | In order to make this work please __do not use__ `git commit` for committing your changes but:
120 |
121 | ```bash
122 | git add .
123 | npm run commit
124 | ```
125 | This will run the npm script `commit` from `packages.json` and will prompt you questions about your
126 | changes, please follow the prompted instructions.
127 |
128 | If you don' want to run the npm script you can install _commitizen_ globally with `npm install -g commitizen`
129 | and the equivalent command `git cz`.
130 |
131 | Use Git's [interactive rebase](https://help.github.com/articles/about-git-rebase/)
132 | feature to tidy up your commits before making them public.
133 |
134 | 5. Locally merge (or rebase) the upstream development branch into your topic branch:
135 |
136 | ```bash
137 | git pull [--rebase] upstream master
138 | ```
139 |
140 | 6. Push your topic branch up to your fork:
141 |
142 | ```bash
143 | git push origin
144 | ```
145 |
146 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title
147 | and description.
148 |
149 | **IMPORTANT**: By submitting a patch, you agree to allow the project owners to license your work
150 | under the terms of the [MIT License](LICENSE.txt).
151 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/LeonardoGentile/mobx-router5)
2 | [](https://coveralls.io/github/LeonardoGentile/mobx-router5?branch=master)
3 | [](https://github.com/LeonardoGentile/mobx-router5/blob/master/LICENSE.txt)
4 | [](https://www.npmjs.com/package/mobx-router5)
5 |
6 |
7 | # mobx-router5
8 |
9 | > [Router5](https://router5.js.org/) integration with [mobx](https://mobx.js.org/). If you develop with React, use this package with __[react-mobx-router5](https://github.com/LeonardoGentile/react-mobx-router5)__. This plugin represents the source of truth for the @observer components exposed by react-mobx-router5.
10 | This plugin can also be used standalone together with mobx.
11 |
12 | ## Requirements
13 |
14 | - __router5 >= 6.1.2__
15 | - __mobx >= 5.0.0__ If you use Mobx < 5 install "^4.0.0" version
16 |
17 |
18 | Notice Mobx@5 introduced breaking changes, please follow the [migration guide](https://github.com/mobxjs/mobx/blob/e17c47833d1812eee6d77914be890aa41e4b7908/CHANGELOG.md#500)
19 |
20 |
21 | These are considered `peerDependencies` that means they should exist in your installation, you should install them yourself to make this plugin work. The package won't install them as dependencies.
22 |
23 | ## Install
24 |
25 | ```bash
26 | npm install mobx-router5
27 | ```
28 |
29 |
30 | ## How it works
31 | Before using this plugin it is necessary that you understand how [router5 works](http://router5.github.io/docs/understanding-router5.html).
32 |
33 | Whenever you performs a router5's transition from one state to another and that transition is *started*, *canceled*, it's *successful* or it has a transition *error*
34 | this plugin exposes all this info as [mobx observables references](https://mobx.js.org/refguide/observable.html) properties of the `RouterStore` class.
35 | You can then use the mobx API to **observe** and react to these **observables**:
36 |
37 | ```ecmascript 6
38 | @observable.ref route; // Current Route - Object
39 | @observable.ref previousRoute; // Object
40 | @observable.ref transitionRoute; // Object
41 | @observable.ref transitionError; // Object
42 | @observable.ref intersectionNode; // String
43 | @observable.ref canActivate; // Array
44 | @observable.ref canDeactivate; // Array
45 |
46 | ```
47 |
48 |
49 | ## How to use
50 |
51 | - Create a router **store** instance from the `RouterStore` class
52 | - Create and configure a **router** instance
53 | - Add the **plugin** to the router instance, **passing the store to the plugin**
54 | - Use the store methods to perform routing or use your router instance directly
55 | - The only (non-action) method provided is `navigate` that is just an alias for router5 `navigate`
56 | - **Observe** the observable properties exposed by the store
57 |
58 | ```javascript
59 | import {createRouter} from 'router5';
60 | import loggerPlugin from 'router5/plugins/logger';
61 | import browserPlugin from 'router5/plugins/browser';
62 | import routes from './routes';
63 | import {mobxPlugin, RouterStore} from 'mobx-router5';
64 |
65 | // Instantiate it directly or extend the class as you wish before invoking new
66 | const routerStore = new RouterStore();
67 |
68 | export default function configureRouter(useLoggerPlugin = false) {
69 | const router = createRouter(routes, {defaultRoute: 'home'})
70 | .usePlugin(mobxPlugin(routerStore)) // Important: pass the store to the plugin!
71 | .usePlugin(browserPlugin({useHash: true}));
72 |
73 | if (useLoggerPlugin) {
74 | router.usePlugin(loggerPlugin) ;
75 | }
76 |
77 | return router;
78 | }
79 | ```
80 |
81 | ## RouterStore
82 |
83 | The core idea of this little plugin is the router store.
84 | The plugin will automatically call the actions (in fact mobx `@action`s) exposed by the store.
85 | By default you can just import the class `RouterStore`, create a new instance, pass it to the plugin and just use it.
86 |
87 |
88 | ### Actions
89 | On route transition Start/Success/Cancel/Error the *mobxPlugin* invokes automatically these mobx actions exposed by the `RouterStore`:
90 |
91 | - `onTransitionStart(toState, fromState)`
92 | - set the `transitionRoute`
93 | - clear the `transitionError`
94 | - `onTransitionSuccess(toState, fromState, opts)`
95 | - set the `route`, `previousRoute`, `canActivate`, `canDeactivate` and `interserctionNode`
96 | - also calls the `clearErrors()` action
97 | - `onTransitionCancel(toState, fromState)`
98 | - reset the `transitionRoute`
99 | - `onTransitionError(toState, fromState, err)`
100 | - set the `transitionRoute`, `previousRoute` and `transitionError`
101 |
102 |
103 | Normally it's **not necessary** to *manually* call these actions, the plugin will do it for us.
104 |
105 | The only one probably worth calling manually (only when necessary) is `clearErrors()`: it resets the `transitionRoute` and `transitionError`.
106 |
107 | ### Router instance reference inside the store
108 |
109 | When you add the plugin to the router with
110 |
111 | ```
112 | router.usePlugin(mobxPlugin(routerStore))
113 | ```
114 |
115 | the router reference is added to the store, so that you can call all the router's methods from the store itself, for example:
116 |
117 | ```
118 | routerStore.router.navigate('home', {}, {})
119 | ```
120 | and all other router5's API [listed here](http://router5.github.io/docs/api-reference.html).
121 |
122 | ### Store reference inside the router's dependencies
123 | The plugin also adds the `routerStore` (the instance) to the router dependencies with
124 |
125 | ```
126 | router.setDependency('routerStore', routerStore);
127 | ```
128 | That means that you can access your store from router5's lifecycle methods (canActivate, canDeactivate), middleware or plugins, see [router5 docs](http://router5.github.io/docs/injectables.html) on this topic.
129 |
130 | This creates indeed a cross referece, that is, the store has a reference of the router and the router has a reference of the store. In most cases this should not create any trouble but be aware of this for cases I haven't foreseen.
131 |
132 | ### Your own store
133 | If you know what you are doing you can subclass or create yor own store, making sure you implement at least the actions listed above and a `setRouter(router)` method.
134 |
135 | ### Contributing
136 | Please refer to the CONTRIBUTING.md file.
137 | Please notice that this would require node >=8 as some dev packages require it (for example semantic-release)
138 |
139 | ## Acknowledgments
140 |
141 | - The structure and build process of this repo are based on [babel-starter-kit](https://github.com/kriasoft/babel-starter-kit)
142 | - I've taken the [redux-router5](https://github.com/router5/redux-router5) plugin as example for developing this one
143 | - Thanks to egghead.io for the nice tips about open source development on their [free course](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
144 | - Special thanks to [Thomas Roch](https://github.com/troch) for the awesome [router5](https://github.com/router5/router5) ecosystem
145 |
146 |
147 |
--------------------------------------------------------------------------------