rescript-relay 1.0.0-rc.4 → 1.0.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.
package/CHANGELOG.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # master
2
2
 
3
- # 1.0.0-rc.4
3
+ # 1.0.0
4
4
 
5
5
  _[Here's a commit showing a project being upgraded to this version](https://github.com/zth/rescript-relay/commit/5831c2f1f0f13eedc1cb60468c32fd32b2dc01d3)_
6
6
 
7
- The time has finally come - RescriptRelay `1.0.0` is in beta! The one, big major thing this release brings is that the ReScript type generation for the Relay compiler has been completely rewritten, and fully integrated into the new Relay Rust compiler. The RescriptRelay fork of the compiler is available and maintained [here])(https://github.com/zth/relay/tree/rescript-relay).
7
+ The time has finally come - RescriptRelay `1.0.0` is released! This version brings a ton of new features and improvements. One of the the big major things this release brings is that the ReScript type generation for the Relay compiler has been completely rewritten, and fully integrated into the new Relay Rust compiler. The RescriptRelay fork of the compiler is available and maintained [here])(https://github.com/zth/relay/tree/rescript-relay).
8
8
 
9
9
  ## Upgrade versions
10
10
 
11
- - `react-relay` and `relay-runtime` to `14.1.0`
12
- - `react` and `react-dom` to `18.0.0`
11
+ - `react-relay` and `relay-runtime` to `>=14.1.0`
12
+ - `react` and `react-dom` to `>=18.0.0`
13
13
 
14
14
  ## Remove Packages
15
15
 
@@ -29,6 +29,15 @@ You can go ahead and remove these packages, that are no longer needed, as the co
29
29
  - Bindings for `requiredFieldLogger` for logging when missing fields are encountered (kudos [Emilios1995](https://github.com/Emilios1995)).
30
30
  - Improved utils for [dealing with enums](https://rescript-relay-documentation.vercel.app/docs/enums).
31
31
  - `recordSourceRecords` is now typed as `Js.Json.t` rather than being abstract.
32
+ - Project now works in `"type": "module"` mode in `package.json` (kudos [cometkim](https://github.com/cometkim))
33
+ - The field name of the `id` field of the `Node` interface is now configurable via `schemaConfig: {nodeInterfaceIdField: "idNameHere"}`.
34
+ - Add support for experimental [Relay Resolvers](https://relay.dev/docs/next/guides/relay-resolvers). Undocumented so far, but looking at the [test](https://github.com/zth/rescript-relay/blob/master/packages/rescript-relay/__tests__/Test_relayResolvers.res) and [definition file](https://github.com/zth/rescript-relay/blob/master/packages/rescript-relay/__tests__/TestRelayUserResolver.res) should give you a hint of how it works.
35
+ - Support `@rescriptRelayIgnoreUnused` directive on fragment definitions to insert annotations that makes `reanalyze` consider all fields in the fragment used, even if they aren't.
36
+ - Support `@rescriptRelayAllowUnsafeEnum` directive on fields selecting enums, which will ignore safety measures for enums, meaning you won't need to add a catch all clause, etc. It'll essentially output the enum as an _input_ enum (as desribed in the docs).
37
+ - Support [provided variables](https://relay.dev/docs/api-reference/graphql-and-directives/#provided-variables). More info in the docs.
38
+ - Windows support! :tada:
39
+ - A `RelaySchemaAssets_graphql.res` is now emitted, containing type definitions for all enums and all input objects. This is designed to help with accessing and using enums and input objects outside of Relay's context. This means it'll be much easier to share makers for input objects, pass enums around, etc.
40
+ - Each fragment with a `@connection` now emits a `makeConnectionId` function that allows you to generate _type safe_ connection IDs. More on why this is useful in the documentation.
32
41
 
33
42
  ## Breaking changes
34
43
 
@@ -45,8 +54,27 @@ You can go ahead and remove these packages, that are no longer needed, as the co
45
54
 
46
55
  More details on this [in the docs](https://rescript-relay-documentation.vercel.app/docs/refetching-and-loading-more-data#makerefetchvariables). Thanks to [@tsnobip](https://github.com/tsnobip) for fixing this!
47
56
 
57
+ - All enum type definitions now reside in `RelaySchemaAssets_graphql.enum_<yourEnumName>`, and are _not_ generated on the operation itself anymore. So, if you previously referred to the actual enum _type_, like `Fragment.Types.enum_MyFineEnum`, you'll now need to refer to that enum type as `RelaySchemaAssets_graphql.enum_MyFineEnum`.
58
+ - Input object fields with illegal names in ReScript previously had their maker function argument names suffixed with `_`, like `~type_: string`. This is now instead _prefixed_, like `~_type: string`. Prefixing like this instead of suffixing means we can ship fully zero cost maker functions, which we couldn't before for input objects with illegal field names.
59
+
60
+ ## Assorted Changes and Fixes
61
+
62
+ - Add Environment isServer option @MoOx
63
+ - Remove rescript from dependencies @anmonteiro
64
+ - Add undocumented holdGC method on relay store @MoOx
65
+ - Fixed `setLinkedRecordToNull`, `setLinkedRecordToUndefined`, `setLinkedRecordsToNull` and `setLinkedRecordsToUndefined` methods by binding them to `setValue` instead of `setLinkedRecord/s`. Previously they were throwing an error because `setLinkedRecord/s` did not support "deleting" values using them. (@reck753)
66
+ - Fix long standing bug that would make whether connection helpers were emitted or not unreliable.
67
+
48
68
  ## 1.0.0 development changelog
49
69
 
70
+ - Fix bug in the new type safe connection ID makers where null default values wouldn't turn the variable into a `Js.Null.t<t>`, leading to type errors.
71
+ - Fix bug with connection handling where connections behind a `@include` or `@skip` directive would not be found.
72
+ - Move `getConnectionNodes` back into generated and auto included `Utils` module. This means that failing to generate `getConnectionNodes`, which can happen for various reasons, won't break the build, but rather not just emit the helper.
73
+
74
+ ### rc.5
75
+
76
+ - Fix compat with `rescript@10.1.0-alpha.1`.
77
+
50
78
  ### rc.4
51
79
 
52
80
  - _potentially breaking_ All enum type definitions now reside in `RelaySchemaAssets_graphql.enum_<yourEnumName>`, and are _not_ generated on the operation itself anymore. So, if you previously referred to the actual enum _type_, like `Fragment.Types.enum_MyFineEnum`, you'll now need to refer to that enum type as `RelaySchemaAssets_graphql.enum_MyFineEnum`.
@@ -63,7 +91,6 @@ You can go ahead and remove these packages, that are no longer needed, as the co
63
91
  ### rc.1
64
92
 
65
93
  - Restore half-broken connection helper function inlining.
66
- -
67
94
 
68
95
  ### rc.0
69
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rescript-relay",
3
- "version": "1.0.0-rc.4",
3
+ "version": "1.0.0",
4
4
  "main": "src/RescriptRelay.res",
5
5
  "license": "MIT",
6
6
  "author": "Gabriel Nordeborn",
package/ppx-linux CHANGED
Binary file
package/ppx-macos-latest CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -573,8 +573,8 @@ module Store = {
573
573
  make(
574
574
  source,
575
575
  {
576
- gcReleaseBufferSize: gcReleaseBufferSize,
577
- queryCacheExpirationTime: queryCacheExpirationTime,
576
+ gcReleaseBufferSize,
577
+ queryCacheExpirationTime,
578
578
  },
579
579
  )
580
580
 
@@ -751,9 +751,9 @@ module MakeLoadQuery = (C: MakeLoadQueryConfig) => {
751
751
  C.query,
752
752
  variables->C.convertVariables,
753
753
  {
754
- fetchKey: fetchKey,
754
+ fetchKey,
755
755
  fetchPolicy: fetchPolicy->mapFetchPolicy,
756
- networkCacheConfig: networkCacheConfig,
756
+ networkCacheConfig,
757
757
  },
758
758
  )
759
759
 
@@ -770,10 +770,8 @@ module MakeLoadQuery = (C: MakeLoadQueryConfig) => {
770
770
  switch token->queryRefToObservable {
771
771
  | None => resolve(. Error())
772
772
  | Some(o) =>
773
- let _: Observable.subscription = o->{
774
- open Observable
775
- subscribe(makeObserver(~complete=() => resolve(. Ok()), ()))
776
- }
773
+ open Observable
774
+ let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(. Ok()), ()))
777
775
  }
778
776
  })
779
777
  }
@@ -1,424 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
- 'use strict';
3
-
4
- var Curry = require("rescript/lib/js/curry.js");
5
- var React = require("react");
6
- var Js_dict = require("rescript/lib/js/js_dict.js");
7
- var Caml_obj = require("rescript/lib/js/caml_obj.js");
8
- var Belt_Array = require("rescript/lib/js/belt_Array.js");
9
- var Belt_Option = require("rescript/lib/js/belt_Option.js");
10
- var Caml_option = require("rescript/lib/js/caml_option.js");
11
- var Caml_exceptions = require("rescript/lib/js/caml_exceptions.js");
12
- var ReactExperimental = require("../ReactExperimental.bs.js");
13
- var RescriptReactRouter = require("@rescript/react/src/RescriptReactRouter.bs.js");
14
-
15
- function matchesUrl(t, url) {
16
- return Curry._1(t.matchesUrl, url);
17
- }
18
-
19
- function preload(t, url) {
20
- return Curry._1(t.preload, url);
21
- }
22
-
23
- function render(t, url) {
24
- return Curry._1(t.render, url);
25
- }
26
-
27
- function make(matchUrl, prepare, render) {
28
- return {
29
- preload: (function (url) {
30
- var params = Curry._1(matchUrl, url);
31
- if (params !== undefined) {
32
- Curry._1(prepare, Caml_option.valFromOption(params));
33
- return ;
34
- }
35
-
36
- }),
37
- matchesUrl: (function (url) {
38
- return Curry._1(matchUrl, url) !== undefined;
39
- }),
40
- render: (function (url) {
41
- var params = Curry._1(matchUrl, url);
42
- if (params !== undefined) {
43
- return Curry._1(render, Curry._1(prepare, Caml_option.valFromOption(params)));
44
- } else {
45
- return null;
46
- }
47
- })
48
- };
49
- }
50
-
51
- var RouteFamily = {
52
- matchesUrl: matchesUrl,
53
- preload: preload,
54
- render: render,
55
- make: make
56
- };
57
-
58
- function getHash(url) {
59
- var index = url.indexOf("#");
60
- if (index >= 0) {
61
- return url.slice(index + 1 | 0);
62
- } else {
63
- return String("");
64
- }
65
- }
66
-
67
- function pathParse(str) {
68
- switch (str) {
69
- case "" :
70
- case "/" :
71
- return /* [] */0;
72
- default:
73
- var raw = str.slice(1);
74
- var match = raw[raw.length - 1 | 0];
75
- var raw$1 = match === "/" ? raw.slice(0, -1) : raw;
76
- var match$1 = raw$1.split("?", 2);
77
- var raw$2 = match$1.length !== 2 ? raw$1 : match$1[0];
78
- var a = raw$2.split("/").filter(function (item) {
79
- return item.length !== 0;
80
- });
81
- var _i = a.length - 1 | 0;
82
- var _res = /* [] */0;
83
- while(true) {
84
- var res = _res;
85
- var i = _i;
86
- if (i < 0) {
87
- return res;
88
- }
89
- _res = {
90
- hd: a[i],
91
- tl: res
92
- };
93
- _i = i - 1 | 0;
94
- continue ;
95
- };
96
- }
97
- }
98
-
99
- function searchParse(str) {
100
- switch (str) {
101
- case "" :
102
- case "?" :
103
- return "";
104
- default:
105
- var match = str.split("?", 2);
106
- if (match.length !== 2) {
107
- return "";
108
- } else {
109
- return match[1];
110
- }
111
- }
112
- }
113
-
114
- function parseUrl(url) {
115
- return {
116
- path: pathParse(url),
117
- hash: getHash(url),
118
- search: searchParse(url)
119
- };
120
- }
121
-
122
- var context = React.createContext(undefined);
123
-
124
- var make$1 = context.Provider;
125
-
126
- function makeProps(value, children, param) {
127
- return {
128
- value: value,
129
- children: children
130
- };
131
- }
132
-
133
- var No_router_in_context = /* @__PURE__ */Caml_exceptions.create("RescriptRelayRouter.No_router_in_context");
134
-
135
- function replaceShallow(path) {
136
- var match = typeof history === "undefined" ? undefined : history;
137
- var match$1 = typeof window === "undefined" ? undefined : window;
138
- if (match !== undefined && match$1 !== undefined) {
139
- match.replaceState(null, "", path);
140
- return ;
141
- }
142
-
143
- }
144
-
145
- function pushShallow(path) {
146
- var match = typeof history === "undefined" ? undefined : history;
147
- var match$1 = typeof window === "undefined" ? undefined : window;
148
- if (match !== undefined && match$1 !== undefined) {
149
- match.pushState(null, "", path);
150
- return ;
151
- }
152
-
153
- }
154
-
155
- var NavigationUtils = {
156
- replaceShallow: replaceShallow,
157
- pushShallow: pushShallow
158
- };
159
-
160
- function make$2(routes) {
161
- var initialUrl = RescriptReactRouter.dangerouslyGetInitialUrl(undefined, undefined);
162
- var matchRoutes = function (routes, url) {
163
- return Belt_Array.getBy(routes, (function (r) {
164
- return Curry._1(r.matchesUrl, url);
165
- }));
166
- };
167
- var currentRoute = {
168
- contents: {
169
- route: matchRoutes(routes, initialUrl),
170
- url: initialUrl
171
- }
172
- };
173
- var subscribers = {};
174
- var subId = {
175
- contents: 0
176
- };
177
- var getNextId = function (param) {
178
- subId.contents = subId.contents + 1 | 0;
179
- return String(subId.contents);
180
- };
181
- RescriptReactRouter.watchUrl(function (url) {
182
- if (!Caml_obj.caml_notequal(url.path, currentRoute.contents.url.path)) {
183
- return ;
184
- }
185
- var route = matchRoutes(routes, url);
186
- if (route !== undefined) {
187
- Curry._1(route.preload, url);
188
- }
189
- var nextEntry = {
190
- route: route,
191
- url: url
192
- };
193
- currentRoute.contents = nextEntry;
194
- return Belt_Array.forEach(Js_dict.values(subscribers), (function (x) {
195
- if (x !== undefined) {
196
- return Curry._1(x, nextEntry);
197
- }
198
-
199
- }));
200
- });
201
- return {
202
- get: (function (param) {
203
- return currentRoute.contents;
204
- }),
205
- preload: (function (url) {
206
- var asRouterUrl = parseUrl(url);
207
- var r = matchRoutes(routes, asRouterUrl);
208
- if (r !== undefined) {
209
- return Curry._1(r.preload, asRouterUrl);
210
- }
211
-
212
- }),
213
- subscribe: (function (cb) {
214
- var id = getNextId(undefined);
215
- subscribers[id] = cb;
216
- return function (param) {
217
- subscribers[id] = undefined;
218
-
219
- };
220
- })
221
- };
222
- }
223
-
224
- function useRouter(param) {
225
- var router = React.useContext(context);
226
- return React.useMemo((function () {
227
- return {
228
- push: RescriptReactRouter.push,
229
- pushShallow: pushShallow,
230
- replace: RescriptReactRouter.replace,
231
- replaceShallow: pushShallow,
232
- preload: router.preload
233
- };
234
- }), [router.preload]);
235
- }
236
-
237
- function RescriptRelayRouter$RouteRenderer(Props) {
238
- var renderPending = Props.renderPending;
239
- var renderFallback = Props.renderFallback;
240
- var renderNotFound = Props.renderNotFound;
241
- var match = React.useState(function () {
242
- return false;
243
- });
244
- var initialized = match[0];
245
- var router = React.useContext(context);
246
- var match$1 = ReactExperimental.useTransition(undefined);
247
- var startTransition = match$1[1];
248
- var match$2 = React.useState(function () {
249
- return Curry._1(router.get, undefined);
250
- });
251
- var setRouteEntry = match$2[1];
252
- var routeEntry = match$2[0];
253
- var match$3 = routeEntry.route;
254
- if (initialized || match$3 === undefined) {
255
-
256
- } else {
257
- Curry._1(match$3.preload, routeEntry.url);
258
- Curry._1(match[1], (function (param) {
259
- return true;
260
- }));
261
- }
262
- React.useEffect((function () {
263
- var currentEntry = Curry._1(router.get, undefined);
264
- if (Caml_obj.caml_notequal(routeEntry.url, currentEntry.url)) {
265
- Curry._1(setRouteEntry, (function (param) {
266
- return currentEntry;
267
- }));
268
- return ;
269
- } else {
270
- return Curry._1(router.subscribe, (function (nextRoute) {
271
- return Curry._1(startTransition, (function (param) {
272
- return Curry._1(setRouteEntry, (function (param) {
273
- return nextRoute;
274
- }));
275
- }));
276
- }));
277
- }
278
- }), [
279
- router,
280
- startTransition
281
- ]);
282
- var match$4 = routeEntry.route;
283
- var renderedContent = initialized && match$4 !== undefined ? Caml_option.some(Curry._1(match$4.render, routeEntry.url)) : undefined;
284
- if (initialized) {
285
- return React.createElement(React.Fragment, undefined, renderPending !== undefined ? Curry._1(renderPending, match$1[0]) : null, React.createElement(React.Suspense, {
286
- children: renderedContent !== undefined ? Caml_option.valFromOption(renderedContent) : (
287
- renderNotFound !== undefined ? Curry._1(renderNotFound, routeEntry.url) : null
288
- ),
289
- fallback: renderFallback !== undefined ? Curry._1(renderFallback, undefined) : null
290
- }));
291
- } else {
292
- return null;
293
- }
294
- }
295
-
296
- var RouteRenderer = {
297
- make: RescriptRelayRouter$RouteRenderer
298
- };
299
-
300
- function isModifiedEvent(e) {
301
- var match = e.metaKey;
302
- var match$1 = e.altKey;
303
- var match$2 = e.ctrlKey;
304
- var match$3 = e.shiftKey;
305
- if (match$2 || match$3 || match$1 || match) {
306
- return true;
307
- } else {
308
- return false;
309
- }
310
- }
311
-
312
- function RescriptRelayRouter$Link(Props) {
313
- var to_ = Props.to_;
314
- var title = Props.title;
315
- var id = Props.id;
316
- var className = Props.className;
317
- var classNameDynamic = Props.classNameDynamic;
318
- var browserTarget = Props.target;
319
- var tabIndex = Props.tabIndex;
320
- var mode = Props.mode;
321
- var render = Props.render;
322
- var preloadOnHover = Props.preloadOnHover;
323
- var children = Props.children;
324
- var onClick = Props.onClick;
325
- var router = React.useContext(context);
326
- var url = RescriptReactRouter.useUrl(undefined, undefined);
327
- var changeRoute = React.useCallback((function (e) {
328
- var match = e.isDefaultPrevented();
329
- var match$1 = e.button;
330
- var match$2 = isModifiedEvent(e);
331
- if (match) {
332
- return ;
333
- }
334
- if (match$1 !== 0) {
335
- return ;
336
- }
337
- if (browserTarget !== undefined && browserTarget !== "self") {
338
- return ;
339
- }
340
- if (match$2) {
341
- return ;
342
- }
343
- e.preventDefault();
344
- var navigate = function (param) {
345
- if (mode === "replace") {
346
- return RescriptReactRouter.replace(to_);
347
- } else {
348
- return RescriptReactRouter.push(to_);
349
- }
350
- };
351
- navigate(undefined);
352
-
353
- }), [
354
- to_,
355
- router
356
- ]);
357
- var preload = React.useCallback((function (param) {
358
- return Curry._1(router.preload, to_);
359
- }), [
360
- to_,
361
- router
362
- ]);
363
- if (render !== undefined) {
364
- var linkUrl = parseUrl(to_);
365
- return Curry._4(render, preload, changeRoute, url, linkUrl);
366
- }
367
- var tmp = {
368
- className: Belt_Option.getWithDefault(className, "") + (
369
- classNameDynamic !== undefined ? " " + Curry._2(classNameDynamic, url, parseUrl(to_)) : ""
370
- ),
371
- href: to_,
372
- target: browserTarget !== undefined ? (
373
- browserTarget === "blank" ? "_blank" : "_self"
374
- ) : "",
375
- onClick: (function (e) {
376
- Curry._1(changeRoute, e);
377
- if (onClick !== undefined) {
378
- return Curry._1(onClick, undefined);
379
- }
380
-
381
- }),
382
- onMouseDown: (function (param) {
383
- return Curry._1(preload, undefined);
384
- }),
385
- onMouseEnter: (function (param) {
386
- if (preloadOnHover !== undefined && preloadOnHover) {
387
- return Curry._1(preload, undefined);
388
- }
389
-
390
- }),
391
- onTouchStart: (function (param) {
392
- return Curry._1(preload, undefined);
393
- })
394
- };
395
- if (id !== undefined) {
396
- tmp.id = Caml_option.valFromOption(id);
397
- }
398
- if (tabIndex !== undefined) {
399
- tmp.tabIndex = Caml_option.valFromOption(tabIndex);
400
- }
401
- if (title !== undefined) {
402
- tmp.title = Caml_option.valFromOption(title);
403
- }
404
- return React.createElement("a", tmp, children);
405
- }
406
-
407
- var Link = {
408
- make: RescriptRelayRouter$Link
409
- };
410
-
411
- var Provider = {
412
- makeProps: makeProps,
413
- make: make$1
414
- };
415
-
416
- exports.RouteFamily = RouteFamily;
417
- exports.Provider = Provider;
418
- exports.No_router_in_context = No_router_in_context;
419
- exports.useRouter = useRouter;
420
- exports.make = make$2;
421
- exports.RouteRenderer = RouteRenderer;
422
- exports.Link = Link;
423
- exports.NavigationUtils = NavigationUtils;
424
- /* context Not a pure module */
@@ -1,379 +0,0 @@
1
- module RouteFamily = {
2
- type t = {
3
- preload: RescriptReactRouter.url => unit,
4
- matchesUrl: RescriptReactRouter.url => bool,
5
- render: RescriptReactRouter.url => React.element,
6
- }
7
-
8
- let matchesUrl = (t, url) => t.matchesUrl(url)
9
- let preload = (t, url) => t.preload(url)
10
- let render = (t, url) => t.render(url)
11
-
12
- let make = (~matchUrl, ~prepare, ~render) => {
13
- preload: url =>
14
- switch matchUrl(url) {
15
- | Some(params) =>
16
- let _ = prepare(params)
17
- | None => ()
18
- },
19
- matchesUrl: url =>
20
- switch matchUrl(url) {
21
- | Some(_) => true
22
- | None => false
23
- },
24
- render: url =>
25
- switch matchUrl(url) {
26
- | Some(params) => params->prepare->render
27
- | None => React.null
28
- },
29
- }
30
- }
31
-
32
- module Url = {
33
- type t = RescriptReactRouter.url
34
-
35
- let getHash = url => {
36
- switch url->Js.String2.indexOf("#") {
37
- | index if index >= 0 => url->Js.String2.sliceToEnd(~from=index + 1)
38
- | _ => Js.String2.make("")
39
- }
40
- }
41
-
42
- // Start - taken from RescriptReactRouter
43
- let arrayToList = a => {
44
- let rec tolist = (i, res) =>
45
- if i < 0 {
46
- res
47
- } else {
48
- tolist(i - 1, list{Js.Array.unsafe_get(a, i), ...res})
49
- }
50
- tolist(Js.Array.length(a) - 1, list{})
51
- }
52
- let pathParse = str =>
53
- switch str {
54
- | ""
55
- | "/" => list{}
56
- | raw =>
57
- /* remove the preceeding /, which every pathname seems to have */
58
- let raw = Js.String.sliceToEnd(~from=1, raw)
59
- /* remove the trailing /, which some pathnames might have. Ugh */
60
- let raw = switch Js.String.get(raw, Js.String.length(raw) - 1) {
61
- | "/" => Js.String.slice(~from=0, ~to_=-1, raw)
62
- | _ => raw
63
- }
64
- /* remove search portion if present in string */
65
- let raw = switch raw |> Js.String.splitAtMost("?", ~limit=2) {
66
- | [path, _] => path
67
- | _ => raw
68
- }
69
-
70
- raw
71
- |> Js.String.split("/")
72
- |> Js.Array.filter(item => String.length(item) != 0)
73
- |> arrayToList
74
- }
75
-
76
- let searchParse = str =>
77
- switch str {
78
- | ""
79
- | "?" => ""
80
- | raw =>
81
- switch raw |> Js.String.splitAtMost("?", ~limit=2) {
82
- | [_, search] => search
83
- | _ => ""
84
- }
85
- }
86
- // End - taken from RescriptReactRouter
87
-
88
- let parseUrl = (url: string): t => {
89
- path: url->pathParse,
90
- hash: url->getHash,
91
- search: url->searchParse,
92
- }
93
-
94
- let make = url => url->parseUrl
95
- }
96
-
97
- type routeEntry = {
98
- route: option<RouteFamily.t>,
99
- url: RescriptReactRouter.url,
100
- }
101
-
102
- type subscriberCallback = routeEntry => unit
103
- type disposeSubscriber = unit => unit
104
-
105
- type routerContext = {
106
- get: unit => routeEntry,
107
- preload: string => unit,
108
- subscribe: subscriberCallback => disposeSubscriber,
109
- }
110
-
111
- let context = React.createContext(Obj.magic())
112
-
113
- module Provider = {
114
- let make = React.Context.provider(context)
115
-
116
- let makeProps = (~value, ~children, ()) =>
117
- {
118
- "value": value,
119
- "children": children,
120
- }
121
- }
122
-
123
- exception No_router_in_context
124
-
125
- module NavigationUtils = {
126
- @send
127
- external replaceShallow: (Dom.history, @as(json`null`) _, @as("") _, ~href: string) => unit =
128
- "replaceState"
129
-
130
- let replaceShallow = path =>
131
- switch (%external(history), %external(window)) {
132
- | (None, _)
133
- | (_, None) => ()
134
- | (Some(history: Dom.history), Some(_window: Dom.window)) => replaceShallow(history, ~href=path)
135
- }
136
-
137
- @send
138
- external pushShallow: (Dom.history, @as(json`null`) _, @as("") _, ~href: string) => unit =
139
- "pushState"
140
-
141
- let pushShallow = path =>
142
- switch (%external(history), %external(window)) {
143
- | (None, _)
144
- | (_, None) => ()
145
- | (Some(history: Dom.history), Some(_window: Dom.window)) => pushShallow(history, ~href=path)
146
- }
147
- }
148
-
149
- let use = (): routerContext => React.useContext(context)
150
-
151
- let make = routes => {
152
- let initialUrl = RescriptReactRouter.dangerouslyGetInitialUrl()
153
-
154
- let matchRoutes = (routes, url): option<RouteFamily.t> =>
155
- routes->Belt.Array.getBy(r => r->RouteFamily.matchesUrl(url))
156
-
157
- let currentRoute: ref<routeEntry> = ref({url: initialUrl, route: matchRoutes(routes, initialUrl)})
158
-
159
- let subscribers: Js.Dict.t<option<subscriberCallback>> = Js.Dict.empty()
160
- let subId = ref(0)
161
-
162
- let getNextId = () => {
163
- subId := subId.contents + 1
164
- subId.contents->string_of_int
165
- }
166
-
167
- let _ = RescriptReactRouter.watchUrl(url =>
168
- if url.path != currentRoute.contents.url.path {
169
- let route = matchRoutes(routes, url)
170
-
171
- switch route {
172
- | Some(r) => r->RouteFamily.preload(url)
173
- | None => ()
174
- }
175
-
176
- let nextEntry = {route: route, url: url}
177
-
178
- currentRoute := nextEntry
179
- subscribers
180
- ->Js.Dict.values
181
- ->Belt.Array.forEach(x =>
182
- switch x {
183
- | Some(cb) => cb(nextEntry)
184
- | None => ()
185
- }
186
- )
187
- }
188
- )
189
-
190
- let router = {
191
- get: () => currentRoute.contents,
192
- preload: url => {
193
- let asRouterUrl = url->Url.make
194
-
195
- switch routes->matchRoutes(asRouterUrl) {
196
- | Some(r) => r->RouteFamily.preload(asRouterUrl)
197
- | None => ()
198
- }
199
- },
200
- subscribe: cb => {
201
- let id = getNextId()
202
- subscribers->Js.Dict.set(id, Some(cb))
203
- () => subscribers->Js.Dict.set(id, None)
204
- },
205
- }
206
-
207
- router
208
- }
209
-
210
- type router = {
211
- push: string => unit,
212
- pushShallow: string => unit,
213
- replace: string => unit,
214
- replaceShallow: string => unit,
215
- preload: string => unit,
216
- }
217
-
218
- let useRouter = () => {
219
- let router = use()
220
-
221
- React.useMemo1(() => {
222
- push: RescriptReactRouter.push,
223
- pushShallow: NavigationUtils.pushShallow,
224
- replace: RescriptReactRouter.replace,
225
- replaceShallow: NavigationUtils.pushShallow,
226
- preload: router.preload,
227
- }, [router.preload])
228
- }
229
-
230
- module RouteRenderer = {
231
- @react.component
232
- let make = (~renderPending=?, ~renderFallback=?, ~renderNotFound=?, ()) => {
233
- let (initialized, setInitialized) = React.useState(() => false)
234
- let router = use()
235
-
236
- let (isPending, startTransition) = ReactExperimental.useTransition()
237
-
238
- let (routeEntry, setRouteEntry) = React.useState(() => router.get())
239
-
240
- switch (initialized, routeEntry.route) {
241
- | (false, Some(r)) =>
242
- r->RouteFamily.preload(routeEntry.url)
243
- setInitialized(_ => true)
244
- | _ => ()
245
- }
246
-
247
- React.useEffect2(() => {
248
- let currentEntry = router.get()
249
- if routeEntry.url != currentEntry.url {
250
- setRouteEntry(_ => currentEntry)
251
- None
252
- } else {
253
- let dispose = router.subscribe(nextRoute =>
254
- startTransition(() => setRouteEntry(_ => nextRoute))
255
- )
256
-
257
- Some(dispose)
258
- }
259
- }, (router, startTransition))
260
-
261
- let renderedContent = switch (initialized, routeEntry.route) {
262
- | (false, Some(_) | None) => None
263
- | (true, Some(r)) => Some(r->RouteFamily.render(routeEntry.url))
264
- | (true, None) => None
265
- }
266
-
267
- initialized
268
- ? <>
269
- {switch renderPending {
270
- | Some(renderPending) => renderPending(isPending)
271
- | None => React.null
272
- }}
273
- <React.Suspense
274
- fallback={switch renderFallback {
275
- | Some(renderFallback) => renderFallback()
276
- | None => React.null
277
- }}>
278
- {switch (renderedContent, renderNotFound) {
279
- | (Some(content), _) => content
280
- | (None, Some(renderNotFound)) => renderNotFound(routeEntry.url)
281
- | (None, None) => React.null
282
- }}
283
- </React.Suspense>
284
- </>
285
- : React.null
286
- }
287
- }
288
-
289
- let isModifiedEvent = e => {
290
- open ReactEvent.Mouse
291
- switch (e->metaKey, e->altKey, e->ctrlKey, e->shiftKey) {
292
- | (true, _, _, _)
293
- | (_, true, _, _) => true
294
- | (_, _, true, _) => true
295
- | (_, _, _, true) => true
296
- | _ => false
297
- }
298
- }
299
-
300
- module Link = {
301
- @react.component
302
- let make = (
303
- ~to_,
304
- ~title=?,
305
- ~id=?,
306
- ~className=?,
307
- ~classNameDynamic=?,
308
- ~target as browserTarget=?,
309
- ~tabIndex=?,
310
- ~mode=?,
311
- ~render=?,
312
- ~preloadOnHover=?,
313
- ~children,
314
- ~onClick=?,
315
- (),
316
- ) => {
317
- let router = use()
318
- let url = RescriptReactRouter.useUrl()
319
-
320
- let changeRoute = React.useCallback2(e => {
321
- open ReactEvent.Mouse
322
- switch (e->isDefaultPrevented, e->button, browserTarget, e->isModifiedEvent) {
323
- | (false, 0, None | Some(#self), false) =>
324
- e->preventDefault
325
- let navigate = () =>
326
- switch mode {
327
- | None
328
- | Some(#push) =>
329
- RescriptReactRouter.push(to_)
330
- | Some(#replace) => RescriptReactRouter.replace(to_)
331
- }
332
-
333
- navigate()
334
- ()
335
- | _ => ()
336
- }
337
- }, (to_, router))
338
-
339
- let preload = React.useCallback2(() => router.preload(to_), (to_, router))
340
-
341
- switch render {
342
- | Some(render) =>
343
- let linkUrl = to_->Url.make
344
- render(~preload, ~changeRoute, ~currentUrl=url, ~linkUrl)
345
- | None =>
346
- <a
347
- href=to_
348
- target={switch browserTarget {
349
- | Some(#self) => "_self"
350
- | Some(#blank) => "_blank"
351
- | None => ""
352
- }}
353
- ?title
354
- ?id
355
- ?tabIndex
356
- className={className->Belt.Option.getWithDefault("") ++
357
- switch classNameDynamic {
358
- | Some(f) => " " ++ f(url, to_->Url.make)
359
- | None => ""
360
- }}
361
- onClick={e => {
362
- changeRoute(e)
363
- switch onClick {
364
- | None => ()
365
- | Some(onClick) => onClick()
366
- }
367
- }}
368
- onMouseDown={_ => preload()}
369
- onTouchStart={_ => preload()}
370
- onMouseEnter={_ =>
371
- switch preloadOnHover {
372
- | Some(true) => preload()
373
- | Some(false) | None => ()
374
- }}>
375
- children
376
- </a>
377
- }
378
- }
379
- }
@@ -1,77 +0,0 @@
1
- module RouteFamily: {
2
- type t
3
-
4
- let matchesUrl: (t, RescriptReactRouter.url) => bool
5
- let preload: (t, RescriptReactRouter.url) => unit
6
- let render: (t, RescriptReactRouter.url) => React.element
7
- let make: (
8
- ~matchUrl: RescriptReactRouter.url => option<'routeVariables>,
9
- ~prepare: 'routeVariables => 'prepared,
10
- ~render: 'prepared => React.element,
11
- ) => t
12
- }
13
-
14
- type routerContext
15
-
16
- module Provider: {
17
- let makeProps: (
18
- ~value: routerContext,
19
- ~children: React.element,
20
- unit,
21
- ) => {"value": routerContext, "children": React.element}
22
-
23
- let make: React.component<{"value": routerContext, "children": React.element}>
24
- }
25
-
26
- exception No_router_in_context
27
-
28
- type router = {
29
- push: string => unit,
30
- pushShallow: string => unit,
31
- replace: string => unit,
32
- replaceShallow: string => unit,
33
- preload: string => unit,
34
- }
35
-
36
- let useRouter: unit => router
37
-
38
- let make: array<RouteFamily.t> => routerContext
39
-
40
- module RouteRenderer: {
41
- @react.component
42
- let make: (
43
- ~renderPending: bool => React.element=?,
44
- ~renderFallback: unit => React.element=?,
45
- ~renderNotFound: RescriptReactRouter.url => React.element=?,
46
- unit,
47
- ) => React.element
48
- }
49
-
50
- module Link: {
51
- @react.component
52
- let make: (
53
- ~to_: string,
54
- ~title: string=?,
55
- ~id: string=?,
56
- ~className: string=?,
57
- ~classNameDynamic: (RescriptReactRouter.url, RescriptReactRouter.url) => string=?,
58
- ~target: [#self | #blank]=?,
59
- ~tabIndex: int=?,
60
- ~mode: [#push | #replace]=?,
61
- ~render: (
62
- ~preload: unit => unit,
63
- ~changeRoute: ReactEvent.Mouse.t => unit,
64
- ~currentUrl: RescriptReactRouter.url,
65
- ~linkUrl: RescriptReactRouter.url,
66
- ) => React.element=?,
67
- ~preloadOnHover: bool=?,
68
- ~children: React.element,
69
- ~onClick: unit => unit=?,
70
- unit,
71
- ) => React.element
72
- }
73
-
74
- module NavigationUtils: {
75
- let replaceShallow: string => unit
76
- let pushShallow: string => unit
77
- }