Skip to content

Commit b2c01dd

Browse files
committed
feat: 🎸 add possibility to generate absolute url address
1 parent d980d22 commit b2c01dd

File tree

3 files changed

+71
-9
lines changed

3 files changed

+71
-9
lines changed

Diff for: docs/docs/router-plugin.md

+25-8
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export default defineWidget({
6666

6767
We have a `router.{link|redirect|getCurrentRoute}` methods available on the widget now.
6868

69-
After that we must initialize universal router with own routes and options in setup phase of creation widget where structure for `routes` and `options` are defined from [universal-router](https://github.com/kriasoft/universal-router/blob/main/docs/api.md) and returns type from `route.action` method is defined by Merkur router plugin. It is a object with `PageView` as main rendered component for defined path and controller life cycle methods **(init, load, activate, deactivate, destroy)** which extend `load`, `mount`, `unmount` methods from `@merkur/plugin-component` and other controller custom methods with logic for defined path.
69+
After that we must initialize universal router with own routes and options in setup phase of creation widget where structure for `routes` and `options` are defined from [universal-router](https://github.com/kriasoft/universal-router/blob/main/docs/api.md). The `options` are extended by Merkur with optional settings `protocol` and `host` for generating absolute url address from `link` method. Returns type from `route.action` method is defined by Merkur router plugin. It is a object with `PageView` as main rendered component for defined path and controller life cycle methods **(init, load, activate, deactivate, destroy)** which extend `load`, `mount`, `unmount` methods from `@merkur/plugin-component` and other controller custom methods with logic for defined path.
7070
The `mount` method use under the hood `widget.viewFactory` method to resolving component for render. So we must set View in createViewFactory as route PageView. If you don't have slots you can set `slotFactories` as empty array or set as route slots.
7171

7272
```javascript
@@ -81,7 +81,7 @@ export default defineWidget({
8181
slotFactories: [],
8282
})),
8383
$plugins: [componentPlugin, eventEmitterPlugin, routerPlugin, errorPlugin],
84-
setup(widget) {
84+
setup(widget) {
8585
const routes = [
8686
{
8787
name: 'home',
@@ -113,8 +113,13 @@ export default defineWidget({
113113
},
114114
];
115115

116+
116117
const options = {
117118
baseUrl: ``,
119+
// other options from https://github.com/kriasoft/universal-router/blob/main/docs/api.md
120+
// merkur specific options for generating absolute url from link method
121+
protocol: 'https',
122+
host: 'www.example.com',
118123
};
119124

120125
createRouter(widget, routes, options);
@@ -148,26 +153,38 @@ export default defineWidget({
148153
});
149154
```
150155

151-
Merkur resolve current route from pathname in `widget.props`. So we must set it in `./server/routes/widget/widgetAPI.js`. Logic for defined `pathname` is on your use case. For example you can read it from `req.query.pathname` and you must update `merkur.config.mjs` file to send `pathname` from playground page to widget API throught `playground.widgetParams` method and of course change `playground.path` for extending playground page to works for more paths than default '/' path.
156+
Merkur resolve current route from pathname in `widget.props`. So we must set it in `./server/routes/widget/widgetAPI.js`. Logic for defined `pathname` is on your use case. For example you can read it from `req.query.pathname`. The `req.query.*` or `req.body.*` are inputs to your API endpoint widget. You must validate it before you use in `widget.props`.
152157

153-
```javascrip
158+
```javascript
154159
// ./server/routes/widget/widgetAPI.js
160+
function getStringQueryParams(req) {
161+
return Object.entries(req.query).reduce((params, [key, value]) => {
162+
params[key] = Array.isArray(value) ? value.pop() : value;
163+
164+
return params;
165+
}, {});
166+
}
167+
155168
router.get(
156169
'/widget',
157170
asyncMiddleware(async (req, res) => {
171+
const { name, pathname } = getStringQueryParams(req);
172+
158173
const merkurModule = requireUncached(`${buildFolder}/widget.cjs`);
159174
const widget = await merkurModule.createWidget({
160175
props: {
161-
name: req.query.name,
176+
name,
162177
environment: widgetEnvironment,
163-
pathname: req.query.pathname,
178+
pathname,
164179
},
165180
});
166181

167182
const { html, slot = {} } = await widget.mount();
168183
```
169184
170-
```javascrip
185+
After that you must update `merkur.config.mjs` file to send `pathname` from playground page to widget API through `playground.widgetParams` method and of course change `playground.path` for extending playground page to works for more paths than default '/' path.
186+
187+
```javascript
171188
// ./merkur.config.mjs
172189

173190
/**
@@ -198,7 +215,7 @@ Returned object from `route.action` method for current route.
198215
- name - route defined name
199216
- data - route arguments
200217
201-
Returns url for route name with filling route pattern with data.
218+
Returns url for route name with filling route pattern with data. If you define `protocol` and `host` settings in createRouter options then the url address generated from link method will be absolute.
202219
203220
### redirect
204221
- url - redirecting url

Diff for: packages/plugin-router/src/__tests__/indexSpec.js

+22
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ describe('createWidget method with router plugin', () => {
4747
"isBootstrapCalled": false,
4848
"isMounting": false,
4949
"isRouteActivated": false,
50+
"options": {},
5051
"pathname": null,
5152
"route": null,
5253
},
@@ -153,6 +154,27 @@ describe('createWidget method with router plugin', () => {
153154
jest.clearAllMocks();
154155
});
155156

157+
it('should generate absolute url for defined options protocol and host', async () => {
158+
createRouter(widget, routes, {
159+
protocol: 'https:',
160+
host: 'merkur.js.org',
161+
});
162+
await widget.mount();
163+
164+
expect(widget.router.link('home')).toEqual('https://merkur.js.org');
165+
expect(widget.router.link('other')).toEqual(
166+
'https://merkur.js.org/other',
167+
);
168+
});
169+
170+
it('should generate absolute url withour protocol for defined host', async () => {
171+
createRouter(widget, routes, { host: 'merkur.js.org' });
172+
await widget.mount();
173+
174+
expect(widget.router.link('home')).toEqual('//merkur.js.org');
175+
expect(widget.router.link('other')).toEqual('//merkur.js.org/other');
176+
});
177+
156178
it('should resolve route to home', async () => {
157179
await widget.mount();
158180

Diff for: packages/plugin-router/src/index.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export function createRouter(widget, routes, options) {
2525
widget.$dependencies.link = generateUrls(widget.$dependencies.router, {
2626
stringifyQueryParams: (params) => new URLSearchParams(params).toString(),
2727
});
28+
widget.$in.router.options = options;
2829
}
2930

3031
export function routerPlugin() {
@@ -34,6 +35,7 @@ export function routerPlugin() {
3435

3536
widget.$in.router = {
3637
route: null,
38+
options: {},
3739
pathname: null,
3840
isMounting: false,
3941
isRouteActivated: false,
@@ -75,14 +77,35 @@ export function routerPlugin() {
7577
};
7678
}
7779

80+
function getOrigin(widget) {
81+
const { protocol, host } = widget.$in.router.options;
82+
83+
if (!host) {
84+
return '';
85+
}
86+
87+
if (!protocol) {
88+
return `//${host}`;
89+
}
90+
91+
return `${protocol.replace(':', '').trim()}://${host.trim()}`;
92+
}
93+
7894
function routerAPI() {
7995
return {
8096
router: {
8197
redirect(widget, url, data = {}) {
8298
widget.emit(RouterEvents.REDIRECT, { url, ...data });
8399
},
84100
link(widget, routeName, data = {}) {
85-
return widget.$dependencies.link(routeName, data);
101+
const origin = getOrigin(widget);
102+
const path = widget.$dependencies.link(routeName, data);
103+
104+
if (origin && path === '/') {
105+
return origin;
106+
}
107+
108+
return `${origin}${path}`;
86109
},
87110
getCurrentRoute(widget) {
88111
return widget.$in.router.route;

0 commit comments

Comments
 (0)