├── .editorconfig
├── .github
└── workflows
│ ├── ci.yml
│ └── update_dependencies.yml
├── .gitignore
├── .mergify.yml
├── .npmignore
├── .prettierignore
├── .prettierrc.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __test__
├── plugin.spec.ts
└── rx-async-policy.spec.ts
├── artifacts.json
├── package.json
├── rollup.config.js
├── src
├── core.augment.ts
├── core.augment.typedoc.ts
├── index.ts
├── index.typedoc.ts
├── rx-async-policy.ts
└── ui-router-rx.ts
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: 'CI'
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | ci:
11 | needs: [test]
12 | runs-on: ubuntu-latest
13 | steps:
14 | - run: true
15 |
16 | test:
17 | name: yarn ${{ matrix.yarncmd }}
18 | runs-on: ubuntu-latest
19 | strategy:
20 | matrix:
21 | yarncmd: ['test']
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Configure
25 | run: |
26 | npm config set scripts-prepend-node-path auto
27 | git config --global user.email uirouter@github.actions
28 | git config --global user.name uirouter_github_actions
29 | - name: Install Dependencies
30 | run: yarn install --pure-lockfile
31 | - name: Check Peer Dependencies
32 | run: npx check-peer-dependencies
33 | - name: Run Tests
34 | run: yarn ${{ matrix.yarncmd }}
35 |
--------------------------------------------------------------------------------
/.github/workflows/update_dependencies.yml:
--------------------------------------------------------------------------------
1 | # This workflow requires a personal access token for uirouterbot
2 | name: Weekly Dependency Bumps
3 | on:
4 | repository_dispatch:
5 | types: [update_dependencies]
6 | schedule:
7 | - cron: '0 21 * * 0'
8 |
9 | jobs:
10 | upgrade:
11 | runs-on: ubuntu-latest
12 | name: 'Update ${{ matrix.deptype }} (latest: ${{ matrix.latest }})'
13 | strategy:
14 | matrix:
15 | excludes: ['']
16 | deptype: ['dependencies', 'devDependencies']
17 | latest: [false]
18 | steps:
19 | - uses: actions/checkout@v2
20 | - run: |
21 | git config user.name uirouterbot
22 | git config user.password ${{ secrets.UIROUTERBOT_PAT }}
23 | git remote set-url origin $(git remote get-url origin | sed -e 's/ui-router/uirouterbot/')
24 | git fetch --unshallow -p origin
25 | - name: Update dependencies
26 | id: upgrade
27 | uses: ui-router/publish-scripts/actions/upgrade@actions-upgrade-v1.0.3
28 | with:
29 | excludes: ${{ matrix.excludes }}
30 | deptype: ${{ matrix.deptype }}
31 | latest: ${{ matrix.latest }}
32 | - name: Create Pull Request
33 | id: cpr
34 | if: ${{ steps.upgrade.outputs.upgrades != '' }}
35 | # the following hash is from https://github.com/peter-evans/create-pull-request/releases/tag/v2.7.0
36 | uses: peter-evans/create-pull-request@340e629d2f63059fb3e3f15437e92cfbc7acd85b
37 | with:
38 | token: ${{ secrets.UIROUTERBOT_PAT }}
39 | request-to-parent: true
40 | branch-suffix: 'random'
41 | commit-message: 'chore(package): Update ${{ steps.upgrade.outputs.upgradecount }} ${{ matrix.deptype }} to ${{ steps.upgrade.outputs.upgradestrategy }}'
42 | title: 'chore(package): Update ${{ steps.upgrade.outputs.upgradecount }} ${{ matrix.deptype }} to ${{ steps.upgrade.outputs.upgradestrategy }}'
43 | body: |
44 | chore(package): Update ${{ steps.upgrade.outputs.upgradecount }} ${{ matrix.deptype }} to ${{ steps.upgrade.outputs.upgradestrategy }}
45 |
46 | ```
47 | ${{ steps.upgrade.outputs.upgrades }}
48 | ```
49 |
50 | Auto-generated by [create-pull-request][1]
51 |
52 | [1]: https://github.com/peter-evans/create-pull-request
53 | - name: Apply Merge Label
54 | if: ${{ steps.cpr.outputs.pr_number != '' }}
55 | uses: actions/github-script@0.9.0
56 | with:
57 | github-token: ${{ secrets.UIROUTERBOT_PAT }}
58 | script: |
59 | await github.issues.addLabels({
60 | owner: context.repo.owner,
61 | repo: context.repo.repo,
62 | issue_number: ${{ steps.cpr.outputs.pr_number }},
63 | labels: ['ready to squash and merge']
64 | });
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # common
2 | node_modules
3 | .DS_Store
4 | *~
5 | **/.*
6 | .*
7 | stats.html
8 |
9 | # webstorm files
10 | .idea
11 | idea-out
12 | *.iml
13 | *.ipr
14 | *.iws
15 |
16 | _doc
17 | _bundles
18 | lib
19 | lib-esm
20 | yarn-error.log
21 |
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | pull_request_rules:
2 | - name: Auto Squash and Merge
3 | conditions:
4 | - base=master
5 | - status-success=ci
6 | - 'label=ready to squash and merge'
7 | actions:
8 | delete_head_branch: {}
9 | merge:
10 | method: squash
11 | strict: smart
12 | - name: Auto Rebase and Merge
13 | conditions:
14 | - base=master
15 | - status-success=ci
16 | - 'label=ready to rebase and merge'
17 | actions:
18 | delete_head_branch: {}
19 | merge:
20 | method: rebase
21 | strict: smart
22 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Any hidden files
2 | **/.*
3 | .*
4 |
5 | src
6 | test
7 | scripts
8 | node_modules
9 |
10 | karma.**.js
11 | tslint.json
12 | tsconfig.json
13 | tsconfig.*.json
14 | yarn.lock
15 | stats.html
16 |
17 | *.iml
18 | *.ipr
19 | *.iws
20 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | CHANGELOG.md
3 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "es5",
4 | "printWidth": 120
5 | }
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 1.0.0 (2021-11-30)
2 | [Compare `@uirouter/rx` versions 0.6.5 and 1.0.0](https://github.com/ui-router/rx/compare/0.6.5...1.0.0)
3 |
4 | ### Features
5 |
6 | * **rxjs:** Add support for RxJS 7 (in addition to 6)
7 |
8 | ### BREAKING CHANGE
9 |
10 | - rxjs semver range changed to ^6.5.3 || ^7.4.0
11 |
12 |
13 |
14 | ## 0.6.5 (2020-01-13)
15 | [Compare `@uirouter/rx` versions 0.6.4 and 0.6.5](https://github.com/ui-router/rx/compare/0.6.4...0.6.5)
16 |
17 |
18 |
19 | ## 0.6.4 (2019-11-19)
20 | [Compare `@uirouter/rx` versions 0.6.0 and 0.6.4](https://github.com/ui-router/rx/compare/0.6.0...0.6.4)
21 |
22 | ### Bug Fixes
23 |
24 | * make RXWAIT custom async policy AOT compatible ([a091c48](https://github.com/ui-router/rx/commit/a091c48))
25 |
26 |
27 |
28 |
29 | # 0.6.0 (2019-10-01)
30 | [Compare `@uirouter/rx` versions 0.5.0 and 0.6.0](https://github.com/ui-router/rx/compare/0.5.0...0.6.0)
31 |
32 | ### Bug Fixes
33 |
34 | * **travis:** use service: xvfb instead of launching it manually. install libgconf debian package ([eace3a9](https://github.com/ui-router/rx/commit/eace3a9))
35 |
36 |
37 | ### Features
38 |
39 | * add rxwait custom async policy ([dca4929](https://github.com/ui-router/rx/commit/dca4929))
40 | * add rxwait custom async policy ([ab1aaa4](https://github.com/ui-router/rx/commit/ab1aaa4))
41 | * **package:** require uirouter/core >=6.0.0 via peerDependency ([9bacfa4](https://github.com/ui-router/rx/commit/9bacfa4))
42 |
43 |
44 | ### BREAKING CHANGES
45 |
46 | * **package:** this version of uirouter/rx depends on uirouter/core version 6 and greater
47 | Because this package now provides an async resolve policy for Observables, this package now has a peerDependency on uirouter/core version >=6.0.0
48 |
49 |
50 |
51 |
52 | # 0.5.0 (2018-05-08)
53 | [Compare `@uirouter/rx` versions 0.4.5 and 0.5.0](https://github.com/ui-router/rx/compare/0.4.5...0.5.0)
54 |
55 | ### Bug Fixes
56 |
57 | * **onError:** Handle transition error so rxjs doesn't log rejections ([84e6210](https://github.com/ui-router/rx/commit/84e6210))
58 |
59 |
60 | ### Features
61 |
62 | * upgrade to rxjs 6 ([46b8158](https://github.com/ui-router/rx/commit/46b8158))
63 |
64 |
65 | ### BREAKING CHANGES
66 |
67 | * rxjs 6.0.0 or higher is now required to use this module
68 |
69 |
70 |
71 |
72 | # 0.5.0-alpha.1 (2018-05-07)
73 | [Compare `@uirouter/rx` versions 0.4.5 and 0.5.0-alpha.1](https://github.com/ui-router/rx/compare/0.4.5...0.5.0-alpha.1)
74 |
75 | ### Features
76 |
77 | * upgrade to rxjs 6 ([46b8158](https://github.com/ui-router/rx/commit/46b8158))
78 |
79 |
80 | ### BREAKING CHANGES
81 |
82 | * rxjs 6.0.0 or higher is now required to use this module
83 |
84 |
85 |
86 |
87 | ## 0.4.5 (2017-10-17)
88 | [Compare `@uirouter/rx` versions 0.4.4 and 0.4.5](https://github.com/ui-router/rx/compare/0.4.4...0.4.5)
89 |
90 |
91 |
92 | ## 0.4.4 (2017-10-12)
93 | [Compare `@uirouter/rx` versions 0.4.2 and 0.4.4](https://github.com/ui-router/rx/compare/0.4.2...0.4.4)
94 |
95 |
96 | ## 0.4.2 (2017-10-12)
97 | [Compare `@uirouter/rx` versions 0.4.1 and 0.4.2](https://github.com/ui-router/rx/compare/0.4.1...0.4.2)
98 |
99 |
100 |
101 | # 0.4.0 (2017-05-08)
102 |
103 | * feat(build): Publish UMD bundles ([fd97a1d](https://github.com/ui-router/rx/commit/fd97a1d))
104 | * feat(plugin): Re-export UIRouterRx as UIRouterRxPlugin ([51a31e2](https://github.com/ui-router/rx/commit/51a31e2))
105 |
106 |
107 | ### BREAKING CHANGE
108 |
109 | * rename plugin.name from `ui-router-rx` to `@uirouter/rx`
110 |
111 |
112 |
113 | ## 0.3.2 (2017-05-06)
114 |
115 | * chore(*): added .editorconfig file ([8101b6b](https://github.com/ui-router/rx/commit/8101b6b))
116 | * chore(build): widen dependency on @uirouter/core ([ef3ac74](https://github.com/ui-router/rx/commit/ef3ac74))
117 | * chore(gitignore): re-ignore hidden files ([e30ae6e](https://github.com/ui-router/rx/commit/e30ae6e))
118 |
119 |
120 |
121 |
122 | ## 0.3.1 (2017-04-22)
123 |
124 | * chore(*): Fix botched npm release
125 |
126 |
127 | ## 0.3.0 (2017-04-22)
128 |
129 | * chore(*): Rename npm package from `ui-router-rx` to `@uirouter/rx`
130 | * chore(*): Switch dependency from `ui-router-core` to `@uirouter/core`
131 |
132 |
133 | ## 0.2.1 (2017-01-22)
134 |
135 | * fix(*): fix peer dependency for ui-router-core (from `^3.1.1` to `>=3.1.1`) ([4a29191](https://github.com/ui-router/rx/commit/4a29191))
136 |
137 |
138 |
139 |
140 | # 0.2.0 (2017-01-20)
141 |
142 | * feat(*): Replace with (more up to date) code from https://github.com/ui-router/ng2/blob/45c73 ([f98574e](https://github.com/ui-router/rx/commit/f98574e))
143 |
144 |
145 |
146 | # 0.1.0
147 |
148 | * Add package.json ([e4bd097](https://github.com/ui-router/rx/commit/e4bd097))
149 | * Create README.md ([c5305d6](https://github.com/ui-router/rx/commit/c5305d6))
150 | * Create ui-router-react.ts ([a1c5346](https://github.com/ui-router/rx/commit/a1c5346))
151 | * Initial commit ([b5e9dd2](https://github.com/ui-router/rx/commit/b5e9dd2))
152 | * Update readme ([5ec502f](https://github.com/ui-router/rx/commit/5ec502f))
153 | * Update README.md ([2b616a4](https://github.com/ui-router/rx/commit/2b616a4))
154 |
155 |
156 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 UI-Router
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @uirouter/rx
2 | Reactive Extensions (RxJS) for UI-Router
3 |
4 | ### What
5 |
6 | This UI-Router plugin exposes various events in UI-Router
7 | as [RxJS](https://github.com/ReactiveX/rxjs) Observables.
8 |
9 | - Transitions (successfull, or any)
10 | - Parameter values
11 | - State registration/deregistrations
12 |
13 | This helps you to use UI-Router in a reactive mode.
14 |
15 | This plugin works with UI-Router Core 2.0 and above (angular-ui-router 1.0.0-rc.1+, ui-router-ng2 1.0.0-beta.4+, ui-router-react 0.4.0+).
16 |
17 |
18 | ### Getting
19 |
20 | ```
21 | npm install @uirouter/rx
22 | ```
23 |
24 | ### Enabling
25 |
26 | This is a UI-Router Plugin.
27 | Add the `UIRouterRx` plugin to your app's `UIRouter` instance.
28 |
29 | ```js
30 | import { UIRouterRx } from "@uirouter/rx";
31 |
32 | // ... after UI-Router bootstrap, get a reference to the `UIRouter` instance
33 | // ... call `.plugin()` to register the ui-router-rx plugin
34 | uiRouter.plugin(UIRouterRx);
35 | ```
36 |
37 | ### Using
38 |
39 | In a state definition,
40 |
41 | ```js
42 | const foo$ = (uiRouter) =>
43 | uiRouter.globals.params$.map(params => params.fooId)
44 | .distinctUntilChanged()
45 | .map(fooId => fetch('/foo/' + fooId).then(resp => resp.json()))
46 |
47 | var fooState = {
48 | name: 'foo',
49 | url: '/foo/{fooId}',
50 | component: FooComponent,
51 | resolve: [
52 | { token: 'foo$', deps: [ UIRouter ], resolveFn: foo$ }
53 | ]
54 | })
55 | ```
56 |
57 | In the component, access the `foo$` resolve value (it will be an Observable). Subscribe to it and do something with it when it emits a new value.
58 |
59 | ```js
60 | var subscription = foo$.subscribe(foo => this.foo = foo);
61 | ```
62 |
63 | Don't forget to unsubscribe when the component is destroyed.
64 |
65 | ```js
66 | subscription.unsubscribe();
67 | ```
68 |
69 |
70 |
--------------------------------------------------------------------------------
/__test__/plugin.spec.ts:
--------------------------------------------------------------------------------
1 | import { UIRouter, servicesPlugin, memoryLocationPlugin } from '@uirouter/core';
2 | import { UIRouterRxPlugin } from '../src';
3 |
4 | describe('Globals Augmentation', () => {
5 | let router;
6 | beforeEach(() => {
7 | router = new UIRouter();
8 | router.plugin(servicesPlugin);
9 | router.plugin(memoryLocationPlugin);
10 | });
11 |
12 | it('should augument router.globals', () => {
13 | expect(router.globals.success$).not.toBeDefined();
14 | expect(router.globals.params$).not.toBeDefined();
15 |
16 | router.plugin(UIRouterRxPlugin);
17 | expect(router.globals.success$).toBeDefined();
18 | expect(router.globals.params$).toBeDefined();
19 | });
20 | });
21 |
22 | const tick = () => new Promise(resolve => setTimeout(resolve));
23 |
24 | describe('State Changes', () => {
25 | let router, successes;
26 |
27 | beforeEach(() => {
28 | successes = [];
29 | router = new UIRouter();
30 | router.plugin(servicesPlugin);
31 | router.plugin(memoryLocationPlugin);
32 | router.plugin(UIRouterRxPlugin);
33 |
34 | router.stateRegistry.register({ name: 'foo' });
35 | router.stateRegistry.register({ name: 'bar' });
36 |
37 | router.globals.success$.subscribe(trans => {
38 | successes.push(trans.to().name);
39 | });
40 | });
41 |
42 | it('(successful) should emit transitions from router.globals.success$', async function(done) {
43 | await router.stateService.go('foo').then(tick, tick);
44 | expect(router.globals.current.name).toEqual('foo');
45 | expect(successes).toEqual(['foo']);
46 |
47 | await router.stateService.go('bar').then(tick, tick);
48 | expect(router.globals.current.name).toEqual('bar');
49 | expect(successes).toEqual(['foo', 'bar']);
50 |
51 | done();
52 | });
53 |
54 | it('(unsuccessful) should not emit transitions from router.globals.success$', async function(done) {
55 | const failresolve = () => Promise.reject('the transition should fail');
56 | router.stateRegistry.register({ name: 'fail', resolve: { failresolve } });
57 | router.stateService.defaultErrorHandler(() => null);
58 |
59 | await router.stateService.go('foo').then(tick, tick);
60 | expect(successes).toEqual(['foo']);
61 |
62 | try {
63 | await router.stateService.go('fail');
64 | } catch (ignored) {}
65 |
66 | await tick();
67 |
68 | expect(router.globals.current.name).toBe('foo');
69 | expect(successes).toEqual(['foo']);
70 |
71 | done();
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/__test__/rx-async-policy.spec.ts:
--------------------------------------------------------------------------------
1 | import { RXWAIT } from '../src';
2 | import { Observable, of, Subject } from 'rxjs';
3 |
4 | describe('RXWAIT', () => {
5 | let resolveSuccessSpy;
6 |
7 | beforeEach(() => {
8 | resolveSuccessSpy = jasmine.createSpy('resolveSuccess');
9 | });
10 |
11 | describe('when provided with something other than an observable', () => {
12 | let resolve;
13 |
14 | beforeEach(() => {
15 | resolve = RXWAIT('5');
16 |
17 | return resolve.then(resolveSuccessSpy);
18 | });
19 |
20 | it('should resolve with an observable', () => {
21 | return resolve.then(observable => {
22 | observable.subscribe(value => {
23 | expect(value).toBe('5');
24 | });
25 | });
26 | });
27 | });
28 |
29 | describe('when provided with an observable that already emitted', () => {
30 | let resolve;
31 |
32 | beforeEach(() => {
33 | resolve = RXWAIT(of('5'));
34 | });
35 |
36 | it('should resolve straight away with an observable', () => {
37 | return resolve.then(observable => {
38 | observable.subscribe(value => {
39 | expect(value).toBe('5');
40 | });
41 | });
42 | });
43 | });
44 |
45 | describe('when provided with an observable that has not emitted yet', () => {
46 | let resolve, resolveRejectSpy, resolveSubject;
47 |
48 | beforeEach(() => {
49 | resolveSubject = new Subject();
50 |
51 | resolveRejectSpy = jasmine.createSpy('resolveReject');
52 |
53 | resolve = RXWAIT(resolveSubject.asObservable());
54 |
55 | resolve.then(resolveSuccessSpy, resolveRejectSpy);
56 | });
57 |
58 | it('should not resolve the promise', () => {
59 | expect(resolveSuccessSpy).not.toHaveBeenCalled();
60 | });
61 |
62 | describe('when the observable emits one value', () => {
63 | beforeEach(() => {
64 | resolveSubject.next('5');
65 |
66 | return resolve;
67 | });
68 |
69 | it('should resolve the promise with an observable', () => {
70 | expect(resolveSuccessSpy).toHaveBeenCalledWith(jasmine.any(Observable));
71 | });
72 |
73 | describe('when the observable emits again', () => {
74 | let value;
75 |
76 | beforeEach(() => {
77 | value = null;
78 |
79 | resolveSuccessSpy.calls.mostRecent().args[0].subscribe(result => {
80 | value = result;
81 | });
82 |
83 | resolveSubject.next('6');
84 | });
85 |
86 | it('should emite the new value', () => {
87 | expect(value).toBe('6');
88 | });
89 | });
90 | });
91 |
92 | describe('when the observable error', () => {
93 | beforeEach(() => {
94 | resolveSubject.error('Something went wrong');
95 |
96 | return resolve.catch(() => null);
97 | });
98 |
99 | it('should reject the promise with the error', () => {
100 | expect(resolveRejectSpy).toHaveBeenCalledWith('Something went wrong');
101 | });
102 | });
103 | });
104 | });
105 |
--------------------------------------------------------------------------------
/artifacts.json:
--------------------------------------------------------------------------------
1 | {
2 | "ARTIFACTS": [
3 | "lib",
4 | "lib-esm",
5 | "_bundles",
6 | "package.json"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uirouter/rx",
3 | "version": "1.0.0",
4 | "description": "Reactive extensions for UI-Router",
5 | "scripts": {
6 | "clean": "shx rm -rf lib lib-esm _bundles yarn-error.log stats.html",
7 | "compile": "npm run clean && tsc && tsc -outDir lib-esm -m es6",
8 | "bundle": "rollup -c",
9 | "build": "run-s compile bundle fixmaps:*",
10 | "test": "jest",
11 | "test:debug": "node --inspect ./node_modules/.bin/jest --runInBand --watch",
12 | "fixmaps:lib": "tweak_sourcemap_paths -a --include 'lib/**/*.js.map' 'lib-esm/**/*.js.map'",
13 | "fixmaps:bundle": "tweak_sourcemap_paths -p ../src --include '_bundles/**/*.js.map'",
14 | "release": "release",
15 | "prepublishOnly": "npm run build",
16 | "precommit": "pretty-quick --staged"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/ui-router/rx.git"
21 | },
22 | "keywords": [
23 | "ui-router",
24 | "reactive",
25 | "rxjs"
26 | ],
27 | "main": "lib/index.js",
28 | "module": "lib-esm/index.js",
29 | "typings": "lib/index.d.ts",
30 | "author": "Chris Thielen",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/ui-router/rx/issues"
34 | },
35 | "homepage": "https://github.com/ui-router/rx#readme",
36 | "peerDependencies": {
37 | "@uirouter/core": ">=6.0.1",
38 | "rxjs": "^6.5.3 || ^7.4.0"
39 | },
40 | "devDependencies": {
41 | "@rollup/plugin-commonjs": "15.0.0",
42 | "@rollup/plugin-node-resolve": "9.0.0",
43 | "@types/jest": "26.0.13",
44 | "@uirouter/core": "^6.0.1",
45 | "@uirouter/publish-scripts": "2.5.5",
46 | "husky": "^4.2.5",
47 | "jasmine": "^3.1.0",
48 | "jest": "^26.0.1",
49 | "prettier": "^2.0.5",
50 | "pretty-quick": "^3.0.2",
51 | "rollup": "^2.10.9",
52 | "rollup-plugin-sourcemaps": "^0.6.2",
53 | "rollup-plugin-terser": "^7.0.2",
54 | "rollup-plugin-visualizer": "^4.0.4",
55 | "rxjs": "^6.5.3 || ^7.4.0",
56 | "ts-jest": "^26.3.0",
57 | "typescript": "^4.0.2"
58 | },
59 | "jest": {
60 | "transform": {
61 | ".(ts|tsx)": "ts-jest"
62 | },
63 | "preset": "ts-jest",
64 | "restoreMocks": true
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import nodeResolve from '@rollup/plugin-node-resolve';
2 | import commonjs from '@rollup/plugin-commonjs';
3 |
4 | let pkg = require('./package.json');
5 | let banner = `/**
6 | * ${pkg.description}
7 | * @version v${pkg.version}
8 | * @link ${pkg.homepage}
9 | * @license MIT License, http://www.opensource.org/licenses/MIT
10 | */`;
11 |
12 | let plugins = [nodeResolve(), commonjs()];
13 |
14 | // Suppress this error message... there are hundreds of them. Angular team says to ignore it.
15 | // https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined
16 | function onwarn(warning) {
17 | if (warning.code === 'THIS_IS_UNDEFINED') return;
18 | console.error(warning.message);
19 | }
20 |
21 | function isExternal(id) {
22 | // @uirouter/core and rxjs should be external
23 | let externals = [/^rxjs\/?/, /\/rxjs\//, /^@uirouter\/.*/];
24 | let isExternal = externals.map(regex => regex.exec(id)).reduce((acc, val) => acc || !!val, false);
25 | // console.log(id, isExternal);
26 | return isExternal;
27 | }
28 |
29 | const CONFIG = {
30 | input: 'lib-esm/index.js',
31 | output: {
32 | file: '_bundles/ui-router-rx.js',
33 | name: '@uirouter/rx',
34 | globals: {
35 | '@uirouter/core': '@uirouter/core',
36 | '@uirouter/rx': '@uirouter/rx',
37 | },
38 | sourcemap: true,
39 | banner: banner,
40 | format: 'umd',
41 | exports: 'named',
42 | },
43 |
44 | plugins: plugins,
45 | onwarn: onwarn,
46 | external: isExternal,
47 | };
48 |
49 | export default CONFIG;
50 |
--------------------------------------------------------------------------------
/src/core.augment.ts:
--------------------------------------------------------------------------------
1 | /** @packageDocumentation @module core */
2 | import { StatesChangedEvent } from './ui-router-rx';
3 | import { Transition } from '@uirouter/core';
4 | import { Observable } from 'rxjs';
5 |
6 | declare module '@uirouter/core/lib/globals' {
7 | interface UIRouterGlobals {
8 | states$?: Observable;
9 | start$?: Observable;
10 | success$?: Observable;
11 | params$?: Observable<{ [paramName: string]: any }>;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/core.augment.typedoc.ts:
--------------------------------------------------------------------------------
1 | /** @packageDocumentation @coreapi @module rx */
2 | import { StatesChangedEvent } from './ui-router-rx';
3 | import { Transition } from '@uirouter/core';
4 | import { Observable } from 'rxjs';
5 |
6 | /**
7 | * Augments the core [[core.UIRouterGlobals]] interface
8 | * @coreapi @module rx.augment
9 | */
10 | // @ts-ignore
11 | declare module '../../core/src/globals' {
12 | /**
13 | * The following observables are added to the core `UIRouterGlobals` object via the [[rx]] plugin.
14 | *
15 | * Access these observables from the router's globals object:
16 | *
17 | * ```js
18 | * router.globals.*
19 | * ```
20 | */
21 | interface UIRouterGlobals {
22 | /**
23 | * This observable emits whenever the list of registered states change.
24 | *
25 | * example:
26 | * ```js
27 | * router.globals.states$.subscribe(event => {
28 | * event.deregistered.forEach(state => console.log(`${state.name} has been deregistered`));
29 | * event.registered.forEach(state => console.log(`${state.name} has been registered`));
30 | * console.log(`A total of ${event.currentStates.length} states are now registered`);
31 | * });
32 | * ```
33 | */
34 | states$?: Observable;
35 | /**
36 | * This observable emits each Transition when it starts.
37 | *
38 | * example:
39 | * ```js
40 | * router.globals.start$.subscribe(transition => {
41 | * console.log(`About to transition to ${transition.to()}`);
42 | * });
43 | * ```
44 | */
45 | start$?: Observable;
46 | /**
47 | * This observable emits each successful Transition.
48 | *
49 | * example:
50 | * ```js
51 | * router.globals.success$.subscribe(transition => {
52 | * console.log(`Current state is now ${transition.to()});
53 | * console.log(`Current params are now ${JSON.stringify(transition.params())});
54 | * });
55 | * ```
56 | */
57 | success$?: Observable;
58 | /**
59 | * This observable emits the current parameter values whenever a transition succeeds.
60 | *
61 | * example:
62 | * ```js
63 | * router.globals.params$.subscribe(transition => {
64 | * console.log(`Current params are now ${JSON.stringify(transition.params())});
65 | * });
66 | * ```
67 | */
68 | params$?: Observable<{ [paramName: string]: any }>;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import './core.augment';
2 | export * from './ui-router-rx';
3 | export * from './rx-async-policy';
4 |
--------------------------------------------------------------------------------
/src/index.typedoc.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Reactive ([RxJS](https://rxjs.dev/)) extensions for UI-Router.
3 | *
4 | * From: https://github.com/ui-router/rx
5 | *
6 | * This plugin adds two major features to UI-Router:
7 | *
8 | * - A new [[ResolvePolicy]] called [[RXWAIT]]
9 | * - Observables for state change events
10 | * [[globals]]
11 | * [[UIRouterGlobals]]
12 | * [[core/UIRouterGlobals]]
13 | * [[core.UIRouterGlobals]]
14 | * [[core.uirouterglobals]]
15 | * [[../../core/src/globals]]
16 | * [[_______core_src_globals_]]
17 | * [[core._______core_src_globals_.uirouterglobals]]
18 | * [[_______core_src_globals_.uirouterglobals]]
19 | *
20 | * @packageDocumentation @preferred @coreapi @module rx
21 | */
22 |
23 | import './core.augment.typedoc';
24 | export * from './ui-router-rx';
25 | export * from './rx-async-policy';
26 |
--------------------------------------------------------------------------------
/src/rx-async-policy.ts:
--------------------------------------------------------------------------------
1 | /** @packageDocumentation @publicapi @module rx */
2 |
3 | import { Observable, of } from 'rxjs';
4 | import { first, shareReplay } from 'rxjs/operators';
5 |
6 | /**
7 | * Determines the unwrapping behavior of asynchronous resolve values.
8 | *
9 | * - When an Observable is returned from the resolveFn, wait until the Observable emits at least one item.
10 | * If any other value will be converted to an Observable that emits such value.
11 | * - The Observable item will not be unwrapped.
12 | * - The Observable stream itself will be provided when the resolve is injected or bound elsewhere.
13 | *
14 | * #### Example:
15 | *
16 | * The `Transition` will wait for the `main.home` resolve observables to emit their first value.
17 | * Promises will be unwrapped and returned as observables before being provided to components.
18 | * ```js
19 | * var mainState = {
20 | * name: 'main',
21 | * resolve: mainResolves, // defined elsewhere
22 | * resolvePolicy: { async: RXWAIT },
23 | * }
24 | * ```
25 | */
26 | export function RXWAIT(resolveFnValue: Observable | any): Promise> {
27 | if (!(resolveFnValue instanceof Observable)) {
28 | resolveFnValue = of(resolveFnValue);
29 | }
30 |
31 | const data$: Observable = resolveFnValue.pipe(shareReplay(1));
32 |
33 | return data$
34 | .pipe(first())
35 | .toPromise()
36 | .then(() => {
37 | return data$;
38 | });
39 | }
40 |
--------------------------------------------------------------------------------
/src/ui-router-rx.ts:
--------------------------------------------------------------------------------
1 | /** @packageDocumentation @publicapi @module rx */
2 |
3 | import { StateDeclaration, Transition, UIRouter, UIRouterPlugin } from '@uirouter/core';
4 | import { ReplaySubject } from 'rxjs';
5 | import { filter, map, mergeMap } from 'rxjs/operators';
6 |
7 | export interface StatesChangedEvent {
8 | currentStates: StateDeclaration[];
9 | registered: StateDeclaration[];
10 | deregistered: StateDeclaration[];
11 | }
12 |
13 | /** Augments UIRouterGlobals with observables for transition starts, successful transitions, and state parameters */
14 | export class UIRouterRx implements UIRouterPlugin {
15 | name = '@uirouter/rx';
16 | private deregisterFns: Function[] = [];
17 |
18 | constructor(router: UIRouter) {
19 | let start$ = new ReplaySubject(1);
20 | let success$ = start$.pipe(
21 | mergeMap(t =>
22 | t.promise.then(
23 | () => t,
24 | () => null
25 | )
26 | ),
27 | filter(t => !!t)
28 | );
29 | let params$ = success$.pipe(map((transition: Transition) => transition.params()));
30 |
31 | let states$ = new ReplaySubject(1);
32 |
33 | function onStatesChangedEvent(event: string, states: StateDeclaration[]) {
34 | let changeEvent = {
35 | currentStates: router.stateRegistry.get(),
36 | registered: [],
37 | deregistered: [],
38 | };
39 |
40 | if (event) changeEvent[event] = states;
41 | states$.next(changeEvent);
42 | }
43 |
44 | this.deregisterFns.push(router.transitionService.onStart({}, transition => start$.next(transition)));
45 | this.deregisterFns.push(router.stateRegistry.onStatesChanged(onStatesChangedEvent));
46 | onStatesChangedEvent(null, null);
47 | Object.assign(router.globals, { start$, success$, params$, states$ });
48 | }
49 |
50 | dispose() {
51 | this.deregisterFns.forEach(deregisterFn => deregisterFn());
52 | this.deregisterFns = [];
53 | }
54 | }
55 |
56 | export const UIRouterRxPlugin = UIRouterRx;
57 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "declaration": true,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "inlineSources": true,
8 | "lib": ["es6", "dom"],
9 | "module": "commonjs",
10 | "moduleResolution": "node",
11 | "outDir": "lib",
12 | "rootDir": "src",
13 | "skipLibCheck": true,
14 | "sourceMap": true,
15 | "target": "es5"
16 | },
17 | "files": ["src/index.ts"]
18 | }
19 |
--------------------------------------------------------------------------------