@merkur/plugin-router 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,226 @@
1
+ import { bindWidgetToFunctions, setDefaultValueForUndefined, hookMethod, isFunction } from '@merkur/core';
2
+ import UniversalRouter from 'universal-router';
3
+ import generateUrls from 'universal-router/generateUrls';
4
+ var RouterEvents = Object.freeze({
5
+ REDIRECT: '@merkur/plugin-router.redirect'
6
+ });
7
+ const DEV = 'development';
8
+ const ENV = typeof process !== 'undefined' && process && process.env ? process.env.NODE_ENV : DEV;
9
+
10
+ function createRouter(widget, routes, options) {
11
+ widget.$dependencies.router = new UniversalRouter(routes, options);
12
+ widget.$dependencies.link = generateUrls(widget.$dependencies.router);
13
+ }
14
+
15
+ function routerPlugin() {
16
+ return {
17
+ async setup(widget) {
18
+ widget = { ...routerAPI(),
19
+ ...widget
20
+ };
21
+ widget.$in.router = {
22
+ route: null,
23
+ pathname: null,
24
+ isMounting: false,
25
+ isRouteActivated: false,
26
+ isBootstrapCalled: false
27
+ };
28
+ bindWidgetToFunctions(widget, widget.router);
29
+ return widget;
30
+ },
31
+
32
+ async create(widget) {
33
+ if (ENV === DEV) {
34
+ if (!widget.$in.component) {
35
+ throw new Error('You must install missing plugin: npm i @merkur/plugin-component');
36
+ }
37
+
38
+ if (!widget.$in.eventEmitter) {
39
+ throw new Error('You must install missing plugin: npm i @merkur/plugin-event-emitter');
40
+ }
41
+ }
42
+
43
+ widget.$in.component.lifeCycle = setDefaultValueForUndefined(widget.$in.component.lifeCycle, ['load'], () => {});
44
+ hookMethod(widget, '$in.component.lifeCycle.load', loadHook);
45
+ hookMethod(widget, 'bootstrap', bootstrapHook);
46
+ hookMethod(widget, 'mount', mountHook);
47
+ hookMethod(widget, 'unmount', unmountHook);
48
+ hookMethod(widget, 'update', updateHook);
49
+ return widget;
50
+ }
51
+
52
+ };
53
+ }
54
+
55
+ function routerAPI() {
56
+ return {
57
+ router: {
58
+ redirect(widget, url, data = {}) {
59
+ widget.emit(RouterEvents.REDIRECT, {
60
+ url,
61
+ ...data
62
+ });
63
+ },
64
+
65
+ link(widget, routeName, data = {}) {
66
+ return widget.$dependencies.link(routeName, data);
67
+ },
68
+
69
+ getCurrentRoute(widget) {
70
+ return widget.$in.router.route;
71
+ }
72
+
73
+ }
74
+ };
75
+ }
76
+
77
+ async function bootstrapHook(widget, originalBootstrap, ...rest) {
78
+ if (widget.$in.router.isBootstrapCalled) {
79
+ return;
80
+ }
81
+
82
+ widget.$in.router.isBootstrapCalled = true;
83
+ return originalBootstrap(...rest);
84
+ } // hook Component
85
+
86
+
87
+ async function loadHook(widget, originalLoad, ...rest) {
88
+ const plugin = widget.$in.router;
89
+
90
+ if (!plugin.isMounting && widget.props.pathname !== plugin.pathname) {
91
+ await tearDownRouterCycle(widget, ...rest);
92
+ await setupRouterCycle(widget, ...rest);
93
+ }
94
+
95
+ if (!isFunction(plugin.route.load)) {
96
+ throw new Error('The load method is mandatory.');
97
+ }
98
+
99
+ const globalStatePromise = isFunction(originalLoad) ? originalLoad(widget, ...rest) : Promise.resolve({});
100
+ const routeStatePromise = plugin.route.load(widget, {
101
+ route: plugin.route,
102
+ args: rest,
103
+ globalState: globalStatePromise
104
+ });
105
+ const [globalState, routeState] = await Promise.all([globalStatePromise, routeStatePromise]);
106
+ return { ...globalState,
107
+ ...routeState
108
+ };
109
+ } // hook Component
110
+
111
+
112
+ async function mountHook(widget, originalMount, ...rest) {
113
+ await widget.bootstrap(...rest);
114
+ const plugin = widget.$in.router;
115
+
116
+ if (!plugin.route) {
117
+ await resolveRoute(widget);
118
+ plugin.isMounting = true;
119
+ }
120
+
121
+ const result = await originalMount(...rest);
122
+
123
+ if (plugin.isMounting && isFunction(plugin.route.init)) {
124
+ await plugin.route.init(widget, {
125
+ route: plugin.route,
126
+ args: rest
127
+ });
128
+ }
129
+
130
+ if (isFunction(plugin.route.activate) && isClient() && !plugin.isRouteActivated) {
131
+ plugin.isRouteActivated = true;
132
+ plugin.route.activate(widget, {
133
+ route: plugin.route,
134
+ args: rest
135
+ });
136
+ }
137
+
138
+ plugin.isMounting = false;
139
+ return result;
140
+ } // hook Component
141
+
142
+
143
+ async function updateHook(widget, originalUpdate, ...rest) {
144
+ const result = await originalUpdate(...rest);
145
+ const plugin = widget.$in.router;
146
+
147
+ if (isFunction(plugin.route.activate) && isClient() && !plugin.isRouteActivated) {
148
+ plugin.isRouteActivated = true;
149
+ plugin.route.activate(widget, {
150
+ route: plugin.route,
151
+ args: rest
152
+ });
153
+ }
154
+
155
+ return result;
156
+ } // hook Component
157
+
158
+
159
+ async function unmountHook(widget, originalUnmount, ...rest) {
160
+ const result = await originalUnmount(...rest);
161
+ await tearDownRouterCycle(widget, ...rest);
162
+ return result;
163
+ }
164
+
165
+ async function resolveRoute(widget) {
166
+ if (ENV === DEV) {
167
+ if (!widget.props.pathname) {
168
+ throw new Error('The props pathname is not defined.');
169
+ }
170
+
171
+ if (!widget.$dependencies.router) {
172
+ throw new Error('You must add calling of createRouter(widget, routes, options) to widget.setup method.');
173
+ }
174
+ }
175
+
176
+ const route = await widget.$dependencies.router.resolve({
177
+ pathname: widget.props.pathname,
178
+ widget
179
+ });
180
+ widget.$in.router.route = route;
181
+ widget.$in.router.pathname = widget.props.pathname;
182
+ return route;
183
+ }
184
+
185
+ async function setupRouterCycle(widget, ...rest) {
186
+ const route = await resolveRoute(widget);
187
+
188
+ if (isFunction(route.init)) {
189
+ await route.init(widget, {
190
+ route,
191
+ args: rest
192
+ });
193
+ }
194
+ }
195
+
196
+ async function tearDownRouterCycle(widget, ...rest) {
197
+ const plugin = widget.$in.router;
198
+ const {
199
+ route,
200
+ isRouteActivated
201
+ } = plugin;
202
+
203
+ if (route) {
204
+ if (isFunction(route.deactivate) && isRouteActivated === true) {
205
+ await route.deactivate(widget, {
206
+ route,
207
+ args: rest
208
+ });
209
+ }
210
+
211
+ if (isFunction(route.destroy)) {
212
+ await route.destroy(widget, {
213
+ route,
214
+ args: rest
215
+ });
216
+ }
217
+ }
218
+
219
+ plugin.isRouteActivated = false;
220
+ }
221
+
222
+ function isClient() {
223
+ return typeof window !== 'undefined';
224
+ }
225
+
226
+ export { createRouter, routerPlugin };
package/lib/index.js ADDED
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var core = require('@merkur/core');
6
+ var UniversalRouter = require('universal-router');
7
+ var generateUrls = require('universal-router/generateUrls');
8
+
9
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
+
11
+ var UniversalRouter__default = /*#__PURE__*/_interopDefaultLegacy(UniversalRouter);
12
+ var generateUrls__default = /*#__PURE__*/_interopDefaultLegacy(generateUrls);
13
+
14
+ var RouterEvents = Object.freeze({
15
+ REDIRECT: '@merkur/plugin-router.redirect',
16
+ });
17
+
18
+ const DEV = 'development';
19
+ const ENV =
20
+ typeof process !== 'undefined' && process && process.env
21
+ ? process.env.NODE_ENV
22
+ : DEV;
23
+
24
+ function createRouter(widget, routes, options) {
25
+ widget.$dependencies.router = new UniversalRouter__default['default'](routes, options);
26
+ widget.$dependencies.link = generateUrls__default['default'](widget.$dependencies.router);
27
+ }
28
+
29
+ function routerPlugin() {
30
+ return {
31
+ async setup(widget) {
32
+ widget = {
33
+ ...routerAPI(),
34
+ ...widget,
35
+ };
36
+
37
+ widget.$in.router = {
38
+ route: null,
39
+ pathname: null,
40
+ isMounting: false,
41
+ isRouteActivated: false,
42
+ isBootstrapCalled: false,
43
+ };
44
+
45
+ core.bindWidgetToFunctions(widget, widget.router);
46
+
47
+ return widget;
48
+ },
49
+ async create(widget) {
50
+ if (ENV === DEV) {
51
+ if (!widget.$in.component) {
52
+ throw new Error(
53
+ 'You must install missing plugin: npm i @merkur/plugin-component'
54
+ );
55
+ }
56
+
57
+ if (!widget.$in.eventEmitter) {
58
+ throw new Error(
59
+ 'You must install missing plugin: npm i @merkur/plugin-event-emitter'
60
+ );
61
+ }
62
+ }
63
+
64
+ widget.$in.component.lifeCycle = core.setDefaultValueForUndefined(
65
+ widget.$in.component.lifeCycle,
66
+ ['load'],
67
+ () => {}
68
+ );
69
+ core.hookMethod(widget, '$in.component.lifeCycle.load', loadHook);
70
+ core.hookMethod(widget, 'bootstrap', bootstrapHook);
71
+ core.hookMethod(widget, 'mount', mountHook);
72
+ core.hookMethod(widget, 'unmount', unmountHook);
73
+ core.hookMethod(widget, 'update', updateHook);
74
+
75
+ return widget;
76
+ },
77
+ };
78
+ }
79
+
80
+ function routerAPI() {
81
+ return {
82
+ router: {
83
+ redirect(widget, url, data = {}) {
84
+ widget.emit(RouterEvents.REDIRECT, { url, ...data });
85
+ },
86
+ link(widget, routeName, data = {}) {
87
+ return widget.$dependencies.link(routeName, data);
88
+ },
89
+ getCurrentRoute(widget) {
90
+ return widget.$in.router.route;
91
+ },
92
+ },
93
+ };
94
+ }
95
+
96
+ async function bootstrapHook(widget, originalBootstrap, ...rest) {
97
+ if (widget.$in.router.isBootstrapCalled) {
98
+ return;
99
+ }
100
+
101
+ widget.$in.router.isBootstrapCalled = true;
102
+
103
+ return originalBootstrap(...rest);
104
+ }
105
+
106
+ // hook Component
107
+ async function loadHook(widget, originalLoad, ...rest) {
108
+ const plugin = widget.$in.router;
109
+
110
+ if (!plugin.isMounting && widget.props.pathname !== plugin.pathname) {
111
+ await tearDownRouterCycle(widget, ...rest);
112
+
113
+ await setupRouterCycle(widget, ...rest);
114
+ }
115
+
116
+ if (!core.isFunction(plugin.route.load)) {
117
+ throw new Error('The load method is mandatory.');
118
+ }
119
+
120
+ const globalStatePromise = core.isFunction(originalLoad)
121
+ ? originalLoad(widget, ...rest)
122
+ : Promise.resolve({});
123
+ const routeStatePromise = plugin.route.load(widget, {
124
+ route: plugin.route,
125
+ args: rest,
126
+ globalState: globalStatePromise,
127
+ });
128
+
129
+ const [globalState, routeState] = await Promise.all([
130
+ globalStatePromise,
131
+ routeStatePromise,
132
+ ]);
133
+
134
+ return { ...globalState, ...routeState };
135
+ }
136
+
137
+ // hook Component
138
+ async function mountHook(widget, originalMount, ...rest) {
139
+ await widget.bootstrap(...rest);
140
+
141
+ const plugin = widget.$in.router;
142
+ if (!plugin.route) {
143
+ await resolveRoute(widget);
144
+ plugin.isMounting = true;
145
+ }
146
+
147
+ const result = await originalMount(...rest);
148
+
149
+ if (plugin.isMounting && core.isFunction(plugin.route.init)) {
150
+ await plugin.route.init(widget, { route: plugin.route, args: rest });
151
+ }
152
+
153
+ if (
154
+ core.isFunction(plugin.route.activate) &&
155
+ isClient() &&
156
+ !plugin.isRouteActivated
157
+ ) {
158
+ plugin.isRouteActivated = true;
159
+ plugin.route.activate(widget, { route: plugin.route, args: rest });
160
+ }
161
+
162
+ plugin.isMounting = false;
163
+
164
+ return result;
165
+ }
166
+
167
+ // hook Component
168
+ async function updateHook(widget, originalUpdate, ...rest) {
169
+ const result = await originalUpdate(...rest);
170
+
171
+ const plugin = widget.$in.router;
172
+
173
+ if (
174
+ core.isFunction(plugin.route.activate) &&
175
+ isClient() &&
176
+ !plugin.isRouteActivated
177
+ ) {
178
+ plugin.isRouteActivated = true;
179
+ plugin.route.activate(widget, { route: plugin.route, args: rest });
180
+ }
181
+
182
+ return result;
183
+ }
184
+
185
+ // hook Component
186
+ async function unmountHook(widget, originalUnmount, ...rest) {
187
+ const result = await originalUnmount(...rest);
188
+
189
+ await tearDownRouterCycle(widget, ...rest);
190
+
191
+ return result;
192
+ }
193
+
194
+ async function resolveRoute(widget) {
195
+ if (ENV === DEV) {
196
+ if (!widget.props.pathname) {
197
+ throw new Error('The props pathname is not defined.');
198
+ }
199
+
200
+ if (!widget.$dependencies.router) {
201
+ throw new Error(
202
+ 'You must add calling of createRouter(widget, routes, options) to widget.setup method.'
203
+ );
204
+ }
205
+ }
206
+
207
+ const route = await widget.$dependencies.router.resolve({
208
+ pathname: widget.props.pathname,
209
+ widget,
210
+ });
211
+
212
+ widget.$in.router.route = route;
213
+ widget.$in.router.pathname = widget.props.pathname;
214
+
215
+ return route;
216
+ }
217
+
218
+ async function setupRouterCycle(widget, ...rest) {
219
+ const route = await resolveRoute(widget);
220
+
221
+ if (core.isFunction(route.init)) {
222
+ await route.init(widget, { route, args: rest });
223
+ }
224
+ }
225
+
226
+ async function tearDownRouterCycle(widget, ...rest) {
227
+ const plugin = widget.$in.router;
228
+
229
+ const { route, isRouteActivated } = plugin;
230
+
231
+ if (route) {
232
+ if (core.isFunction(route.deactivate) && isRouteActivated === true) {
233
+ await route.deactivate(widget, { route, args: rest });
234
+ }
235
+
236
+ if (core.isFunction(route.destroy)) {
237
+ await route.destroy(widget, { route, args: rest });
238
+ }
239
+ }
240
+
241
+ plugin.isRouteActivated = false;
242
+ }
243
+
244
+ function isClient() {
245
+ return typeof window !== 'undefined';
246
+ }
247
+
248
+ exports.createRouter = createRouter;
249
+ exports.routerPlugin = routerPlugin;
package/lib/index.mjs ADDED
@@ -0,0 +1,239 @@
1
+ import { bindWidgetToFunctions, setDefaultValueForUndefined, hookMethod, isFunction } from '@merkur/core';
2
+ import UniversalRouter from 'universal-router';
3
+ import generateUrls from 'universal-router/generateUrls';
4
+
5
+ var RouterEvents = Object.freeze({
6
+ REDIRECT: '@merkur/plugin-router.redirect',
7
+ });
8
+
9
+ const DEV = 'development';
10
+ const ENV =
11
+ typeof process !== 'undefined' && process && process.env
12
+ ? process.env.NODE_ENV
13
+ : DEV;
14
+
15
+ function createRouter(widget, routes, options) {
16
+ widget.$dependencies.router = new UniversalRouter(routes, options);
17
+ widget.$dependencies.link = generateUrls(widget.$dependencies.router);
18
+ }
19
+
20
+ function routerPlugin() {
21
+ return {
22
+ async setup(widget) {
23
+ widget = {
24
+ ...routerAPI(),
25
+ ...widget,
26
+ };
27
+
28
+ widget.$in.router = {
29
+ route: null,
30
+ pathname: null,
31
+ isMounting: false,
32
+ isRouteActivated: false,
33
+ isBootstrapCalled: false,
34
+ };
35
+
36
+ bindWidgetToFunctions(widget, widget.router);
37
+
38
+ return widget;
39
+ },
40
+ async create(widget) {
41
+ if (ENV === DEV) {
42
+ if (!widget.$in.component) {
43
+ throw new Error(
44
+ 'You must install missing plugin: npm i @merkur/plugin-component'
45
+ );
46
+ }
47
+
48
+ if (!widget.$in.eventEmitter) {
49
+ throw new Error(
50
+ 'You must install missing plugin: npm i @merkur/plugin-event-emitter'
51
+ );
52
+ }
53
+ }
54
+
55
+ widget.$in.component.lifeCycle = setDefaultValueForUndefined(
56
+ widget.$in.component.lifeCycle,
57
+ ['load'],
58
+ () => {}
59
+ );
60
+ hookMethod(widget, '$in.component.lifeCycle.load', loadHook);
61
+ hookMethod(widget, 'bootstrap', bootstrapHook);
62
+ hookMethod(widget, 'mount', mountHook);
63
+ hookMethod(widget, 'unmount', unmountHook);
64
+ hookMethod(widget, 'update', updateHook);
65
+
66
+ return widget;
67
+ },
68
+ };
69
+ }
70
+
71
+ function routerAPI() {
72
+ return {
73
+ router: {
74
+ redirect(widget, url, data = {}) {
75
+ widget.emit(RouterEvents.REDIRECT, { url, ...data });
76
+ },
77
+ link(widget, routeName, data = {}) {
78
+ return widget.$dependencies.link(routeName, data);
79
+ },
80
+ getCurrentRoute(widget) {
81
+ return widget.$in.router.route;
82
+ },
83
+ },
84
+ };
85
+ }
86
+
87
+ async function bootstrapHook(widget, originalBootstrap, ...rest) {
88
+ if (widget.$in.router.isBootstrapCalled) {
89
+ return;
90
+ }
91
+
92
+ widget.$in.router.isBootstrapCalled = true;
93
+
94
+ return originalBootstrap(...rest);
95
+ }
96
+
97
+ // hook Component
98
+ async function loadHook(widget, originalLoad, ...rest) {
99
+ const plugin = widget.$in.router;
100
+
101
+ if (!plugin.isMounting && widget.props.pathname !== plugin.pathname) {
102
+ await tearDownRouterCycle(widget, ...rest);
103
+
104
+ await setupRouterCycle(widget, ...rest);
105
+ }
106
+
107
+ if (!isFunction(plugin.route.load)) {
108
+ throw new Error('The load method is mandatory.');
109
+ }
110
+
111
+ const globalStatePromise = isFunction(originalLoad)
112
+ ? originalLoad(widget, ...rest)
113
+ : Promise.resolve({});
114
+ const routeStatePromise = plugin.route.load(widget, {
115
+ route: plugin.route,
116
+ args: rest,
117
+ globalState: globalStatePromise,
118
+ });
119
+
120
+ const [globalState, routeState] = await Promise.all([
121
+ globalStatePromise,
122
+ routeStatePromise,
123
+ ]);
124
+
125
+ return { ...globalState, ...routeState };
126
+ }
127
+
128
+ // hook Component
129
+ async function mountHook(widget, originalMount, ...rest) {
130
+ await widget.bootstrap(...rest);
131
+
132
+ const plugin = widget.$in.router;
133
+ if (!plugin.route) {
134
+ await resolveRoute(widget);
135
+ plugin.isMounting = true;
136
+ }
137
+
138
+ const result = await originalMount(...rest);
139
+
140
+ if (plugin.isMounting && isFunction(plugin.route.init)) {
141
+ await plugin.route.init(widget, { route: plugin.route, args: rest });
142
+ }
143
+
144
+ if (
145
+ isFunction(plugin.route.activate) &&
146
+ isClient() &&
147
+ !plugin.isRouteActivated
148
+ ) {
149
+ plugin.isRouteActivated = true;
150
+ plugin.route.activate(widget, { route: plugin.route, args: rest });
151
+ }
152
+
153
+ plugin.isMounting = false;
154
+
155
+ return result;
156
+ }
157
+
158
+ // hook Component
159
+ async function updateHook(widget, originalUpdate, ...rest) {
160
+ const result = await originalUpdate(...rest);
161
+
162
+ const plugin = widget.$in.router;
163
+
164
+ if (
165
+ isFunction(plugin.route.activate) &&
166
+ isClient() &&
167
+ !plugin.isRouteActivated
168
+ ) {
169
+ plugin.isRouteActivated = true;
170
+ plugin.route.activate(widget, { route: plugin.route, args: rest });
171
+ }
172
+
173
+ return result;
174
+ }
175
+
176
+ // hook Component
177
+ async function unmountHook(widget, originalUnmount, ...rest) {
178
+ const result = await originalUnmount(...rest);
179
+
180
+ await tearDownRouterCycle(widget, ...rest);
181
+
182
+ return result;
183
+ }
184
+
185
+ async function resolveRoute(widget) {
186
+ if (ENV === DEV) {
187
+ if (!widget.props.pathname) {
188
+ throw new Error('The props pathname is not defined.');
189
+ }
190
+
191
+ if (!widget.$dependencies.router) {
192
+ throw new Error(
193
+ 'You must add calling of createRouter(widget, routes, options) to widget.setup method.'
194
+ );
195
+ }
196
+ }
197
+
198
+ const route = await widget.$dependencies.router.resolve({
199
+ pathname: widget.props.pathname,
200
+ widget,
201
+ });
202
+
203
+ widget.$in.router.route = route;
204
+ widget.$in.router.pathname = widget.props.pathname;
205
+
206
+ return route;
207
+ }
208
+
209
+ async function setupRouterCycle(widget, ...rest) {
210
+ const route = await resolveRoute(widget);
211
+
212
+ if (isFunction(route.init)) {
213
+ await route.init(widget, { route, args: rest });
214
+ }
215
+ }
216
+
217
+ async function tearDownRouterCycle(widget, ...rest) {
218
+ const plugin = widget.$in.router;
219
+
220
+ const { route, isRouteActivated } = plugin;
221
+
222
+ if (route) {
223
+ if (isFunction(route.deactivate) && isRouteActivated === true) {
224
+ await route.deactivate(widget, { route, args: rest });
225
+ }
226
+
227
+ if (isFunction(route.destroy)) {
228
+ await route.destroy(widget, { route, args: rest });
229
+ }
230
+ }
231
+
232
+ plugin.isRouteActivated = false;
233
+ }
234
+
235
+ function isClient() {
236
+ return typeof window !== 'undefined';
237
+ }
238
+
239
+ export { createRouter, routerPlugin };