@octanejs/remix-router 0.1.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -0
  3. package/package.json +53 -0
  4. package/src/dom.ts +16 -0
  5. package/src/index.ts +267 -0
  6. package/src/internal.ts +37 -0
  7. package/src/lib/Await.tsrx +145 -0
  8. package/src/lib/Await.tsrx.d.ts +6 -0
  9. package/src/lib/DefaultErrorComponent.tsrx +47 -0
  10. package/src/lib/DefaultErrorComponent.tsrx.d.ts +2 -0
  11. package/src/lib/RenderErrorBoundary.tsrx +117 -0
  12. package/src/lib/RenderErrorBoundary.tsrx.d.ts +14 -0
  13. package/src/lib/actions.ts +90 -0
  14. package/src/lib/components/MemoryRouter.tsrx +42 -0
  15. package/src/lib/components/MemoryRouter.tsrx.d.ts +10 -0
  16. package/src/lib/components/Navigate.ts +60 -0
  17. package/src/lib/components/Router.tsrx +88 -0
  18. package/src/lib/components/Router.tsrx.d.ts +13 -0
  19. package/src/lib/components/RouterProvider.tsrx +320 -0
  20. package/src/lib/components/RouterProvider.tsrx.d.ts +21 -0
  21. package/src/lib/components/Routes.tsrx +53 -0
  22. package/src/lib/components/Routes.tsrx.d.ts +7 -0
  23. package/src/lib/components/routes-collector.ts +317 -0
  24. package/src/lib/components/utils.ts +171 -0
  25. package/src/lib/components/with-props.ts +72 -0
  26. package/src/lib/context.ts +129 -0
  27. package/src/lib/dom/Form.tsrx +76 -0
  28. package/src/lib/dom/Form.tsrx.d.ts +22 -0
  29. package/src/lib/dom/Link.tsrx +91 -0
  30. package/src/lib/dom/Link.tsrx.d.ts +22 -0
  31. package/src/lib/dom/NavLink.tsrx +118 -0
  32. package/src/lib/dom/NavLink.tsrx.d.ts +33 -0
  33. package/src/lib/dom/dom.ts +344 -0
  34. package/src/lib/dom/hooks.ts +93 -0
  35. package/src/lib/dom/lib.ts +822 -0
  36. package/src/lib/dom/routers.tsrx +104 -0
  37. package/src/lib/dom/routers.tsrx.d.ts +23 -0
  38. package/src/lib/dom/server.tsrx +350 -0
  39. package/src/lib/dom/server.tsrx.d.ts +25 -0
  40. package/src/lib/errors.ts +84 -0
  41. package/src/lib/framework-stubs.ts +103 -0
  42. package/src/lib/hooks.ts +1797 -0
  43. package/src/lib/href.ts +71 -0
  44. package/src/lib/react-types.ts +10 -0
  45. package/src/lib/router/history.ts +763 -0
  46. package/src/lib/router/instrumentation.ts +496 -0
  47. package/src/lib/router/links.ts +192 -0
  48. package/src/lib/router/router.ts +7165 -0
  49. package/src/lib/router/server-runtime-types.ts +12 -0
  50. package/src/lib/router/url.ts +8 -0
  51. package/src/lib/router/utils.ts +2179 -0
  52. package/src/lib/server-runtime/cookies.ts +240 -0
  53. package/src/lib/server-runtime/crypto.ts +55 -0
  54. package/src/lib/server-runtime/mode.ts +16 -0
  55. package/src/lib/server-runtime/sessions/cookieStorage.ts +54 -0
  56. package/src/lib/server-runtime/sessions/memoryStorage.ts +59 -0
  57. package/src/lib/server-runtime/sessions.ts +291 -0
  58. package/src/lib/server-runtime/warnings.ts +10 -0
  59. package/src/lib/types/future.ts +16 -0
  60. package/src/lib/types/params.ts +8 -0
  61. package/src/lib/types/register.ts +39 -0
  62. package/src/lib/types/route-module.ts +17 -0
  63. package/src/lib/types/utils.ts +37 -0
@@ -0,0 +1,76 @@
1
+ // Form — transcribed from react-router@7.18.1 lib/dom/lib.tsx onto octane.
2
+ // forwardRef → ref-as-prop. The submit handler is a NATIVE delegated `submit`
3
+ // listener (octane has no synthetic events): `event.submitter` is read
4
+ // directly off the native SubmitEvent where React goes through
5
+ // `event.nativeEvent.submitter` — same value, no behavior change.
6
+ import { startTransition, useContext } from 'octane';
7
+ import { NavigationContext } from '../context.ts';
8
+ import { ABSOLUTE_URL_REGEX } from '../router/url.ts';
9
+ import { useFormAction, useSubmit } from './lib.ts';
10
+
11
+ const defaultMethod = 'get';
12
+
13
+ export function Form(props) @{
14
+ const {
15
+ discover = 'render',
16
+ fetcherKey,
17
+ navigate,
18
+ reloadDocument,
19
+ replace,
20
+ state,
21
+ method = defaultMethod,
22
+ action,
23
+ onSubmit,
24
+ relative,
25
+ preventScrollReset,
26
+ viewTransition,
27
+ defaultShouldRevalidate,
28
+ ref,
29
+ children,
30
+ ...rest
31
+ } = props;
32
+
33
+ const { useTransitions } = useContext(NavigationContext);
34
+ const submit = useSubmit();
35
+ const formAction = useFormAction(action, { relative });
36
+ const formMethod =
37
+ method.toLowerCase() === 'get' ? 'get' : 'post';
38
+ const isAbsolute = typeof action === 'string' && ABSOLUTE_URL_REGEX.test(action);
39
+
40
+ function submitHandler(event) {
41
+ onSubmit && onSubmit(event);
42
+ if (event.defaultPrevented) return;
43
+ event.preventDefault();
44
+
45
+ const submitter = event.submitter ?? null;
46
+
47
+ const submitMethod = submitter && submitter.getAttribute('formmethod') || method;
48
+
49
+ const doSubmit = () => submit(submitter || event.currentTarget, {
50
+ fetcherKey,
51
+ method: submitMethod,
52
+ navigate,
53
+ replace,
54
+ state,
55
+ relative,
56
+ preventScrollReset,
57
+ viewTransition,
58
+ defaultShouldRevalidate,
59
+ });
60
+
61
+ if (useTransitions && navigate !== false) {
62
+ startTransition(() => doSubmit());
63
+ } else {
64
+ doSubmit();
65
+ }
66
+ }
67
+
68
+ <form
69
+ ref={ref}
70
+ method={formMethod}
71
+ action={formAction}
72
+ onSubmit={reloadDocument ? onSubmit : submitHandler}
73
+ {...rest}
74
+ data-discover={!isAbsolute && discover === 'render' ? 'true' : undefined}
75
+ >{children}</form>
76
+ }
@@ -0,0 +1,22 @@
1
+ // Type declaration for the .tsrx component (resolved by relative path).
2
+ import type { RelativeRoutingType } from '../router/router';
3
+
4
+ export declare const Form: (props: {
5
+ children?: unknown;
6
+ method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | string;
7
+ action?: string;
8
+ encType?: 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain';
9
+ navigate?: boolean;
10
+ fetcherKey?: string;
11
+ replace?: boolean;
12
+ state?: any;
13
+ relative?: RelativeRoutingType;
14
+ preventScrollReset?: boolean;
15
+ reloadDocument?: boolean;
16
+ viewTransition?: boolean;
17
+ defaultShouldRevalidate?: boolean;
18
+ discover?: 'render' | 'none';
19
+ onSubmit?: (event: SubmitEvent) => void;
20
+ ref?: unknown;
21
+ [key: string]: unknown;
22
+ }) => unknown;
@@ -0,0 +1,91 @@
1
+ // Link — transcribed from react-router@7.18.1 lib/dom/lib.tsx onto octane.
2
+ // forwardRef → ref-as-prop (octane refs are props; documented divergence).
3
+ // Framework-mode prefetching (usePrefetchBehavior / PrefetchPageLinks) is
4
+ // inert outside a FrameworkContext upstream and is dropped here — the
5
+ // `prefetch` prop is accepted and ignored (framework mode is out of scope).
6
+ import { useContext } from 'octane';
7
+ import { NavigationContext } from '../context.ts';
8
+ import { ABSOLUTE_URL_REGEX } from '../router/url.ts';
9
+ import { parseToInfo, resolveTo, joinPaths } from '../router/utils.ts';
10
+ import { useHref, useLocation } from '../hooks.ts';
11
+ import { useLinkClickHandler } from './hooks.ts';
12
+
13
+ export function Link(props) @{
14
+ const {
15
+ onClick,
16
+ discover = 'render',
17
+ prefetch = 'none',
18
+ relative,
19
+ reloadDocument,
20
+ replace,
21
+ mask,
22
+ state,
23
+ target,
24
+ to: toProp,
25
+ preventScrollReset,
26
+ viewTransition,
27
+ defaultShouldRevalidate,
28
+ ref,
29
+ children,
30
+ ...rest
31
+ } = props;
32
+
33
+ const { basename, navigator, useTransitions } = useContext(NavigationContext);
34
+ const isAbsolute = typeof toProp === 'string' && ABSOLUTE_URL_REGEX.test(toProp);
35
+
36
+ const parsed = parseToInfo(toProp, basename);
37
+ const to = parsed.to;
38
+
39
+ // Rendered into <a href> for relative URLs
40
+ const href = useHref(to, { relative });
41
+ const location = useLocation();
42
+
43
+ let maskedHref = null;
44
+
45
+ if (mask) {
46
+ // Inlined version of the `useHref` logic operating off the masked location
47
+ // instead of the current location
48
+ const resolved = resolveTo(mask, [], location.mask ? location.mask.pathname : '/', true);
49
+
50
+ // If we're operating within a basename, prepend it to the pathname prior
51
+ // to creating the href. If this is a root navigation, then just use the raw
52
+ // basename which allows the basename to have full control over the presence
53
+ // of a trailing slash on root links
54
+ if (basename !== '/') {
55
+ resolved.pathname = resolved.pathname === '/'
56
+ ? basename
57
+ : joinPaths([basename, resolved.pathname]);
58
+ }
59
+
60
+ maskedHref = navigator.createHref(resolved);
61
+ }
62
+
63
+ const internalOnClick = useLinkClickHandler(to, {
64
+ replace,
65
+ mask,
66
+ state,
67
+ target,
68
+ preventScrollReset,
69
+ relative,
70
+ viewTransition,
71
+ defaultShouldRevalidate,
72
+ useTransitions,
73
+ });
74
+ function handleClick(event) {
75
+ if (onClick) onClick(event);
76
+ if (!event.defaultPrevented) {
77
+ internalOnClick(event);
78
+ }
79
+ }
80
+
81
+ const isSpaLink = !(parsed.isExternal || reloadDocument);
82
+
83
+ <a
84
+ {...rest}
85
+ href={(isSpaLink ? maskedHref : undefined) || parsed.absoluteURL || href}
86
+ onClick={isSpaLink ? handleClick : onClick}
87
+ ref={ref}
88
+ target={target}
89
+ data-discover={!isAbsolute && discover === 'render' ? 'true' : undefined}
90
+ >{children}</a>
91
+ }
@@ -0,0 +1,22 @@
1
+ // Type declaration for the .tsrx component (resolved by relative path).
2
+ import type { To } from '../router/history';
3
+ import type { RelativeRoutingType } from '../router/router';
4
+
5
+ export declare const Link: (props: {
6
+ to: To;
7
+ children?: unknown;
8
+ onClick?: (event: MouseEvent) => void;
9
+ discover?: 'render' | 'none';
10
+ prefetch?: 'none' | 'intent' | 'render' | 'viewport';
11
+ relative?: RelativeRoutingType;
12
+ reloadDocument?: boolean;
13
+ replace?: boolean;
14
+ mask?: To;
15
+ state?: any;
16
+ target?: string;
17
+ preventScrollReset?: boolean;
18
+ viewTransition?: boolean;
19
+ defaultShouldRevalidate?: boolean;
20
+ ref?: unknown;
21
+ [key: string]: unknown;
22
+ }) => unknown;
@@ -0,0 +1,118 @@
1
+ // NavLink — transcribed from react-router@7.18.1 lib/dom/lib.tsx onto octane.
2
+ // forwardRef → ref-as-prop. Renders <Link> with active/pending-derived
3
+ // className/style/aria-current; `className`, `style`, and `children` accept
4
+ // render props receiving { isActive, isPending, isTransitioning }. Octane
5
+ // note: a render-prop children is a plain function; the natural block
6
+ // children pass through untouched (isChildrenBlock guard, as in Await).
7
+ import { isChildrenBlock, useContext } from 'octane';
8
+ import { DataRouterStateContext, NavigationContext } from '../context.ts';
9
+ import { stripBasename } from '../router/utils.ts';
10
+ import { useLocation, useResolvedPath } from '../hooks.ts';
11
+ import { useViewTransitionState } from './lib.ts';
12
+ import { Link } from './Link.tsrx';
13
+
14
+ export function NavLink(props) @{
15
+ const {
16
+ 'aria-current': ariaCurrentProp = 'page',
17
+ caseSensitive = false,
18
+ className: classNameProp = '',
19
+ end = false,
20
+ style: styleProp,
21
+ to,
22
+ viewTransition,
23
+ children,
24
+ ref,
25
+ ...rest
26
+ } = props;
27
+
28
+ const path = useResolvedPath(to, { relative: rest.relative });
29
+ const location = useLocation();
30
+ const routerState = useContext(DataRouterStateContext);
31
+ const { navigator, basename } = useContext(NavigationContext);
32
+ const isTransitioning =
33
+ routerState != null &&
34
+ // Conditional usage is OK here because the usage of a data router is static
35
+ useViewTransitionState(path) &&
36
+ viewTransition === true;
37
+
38
+ let toPathname = navigator.encodeLocation
39
+ ? navigator.encodeLocation(path).pathname
40
+ : path.pathname;
41
+ let locationPathname = location.pathname;
42
+ let nextLocationPathname =
43
+ routerState && routerState.navigation && routerState.navigation.location
44
+ ? routerState.navigation.location.pathname
45
+ : null;
46
+
47
+ if (!caseSensitive) {
48
+ locationPathname = locationPathname.toLowerCase();
49
+ nextLocationPathname = nextLocationPathname ? nextLocationPathname.toLowerCase() : null;
50
+ toPathname = toPathname.toLowerCase();
51
+ }
52
+
53
+ if (nextLocationPathname && basename) {
54
+ nextLocationPathname = stripBasename(nextLocationPathname, basename) || nextLocationPathname;
55
+ }
56
+
57
+ // If the `to` has a trailing slash, look at that exact spot. Otherwise,
58
+ // we're looking for a slash _after_ what's in `to`. For example:
59
+ //
60
+ // <NavLink to="/users"> and <NavLink to="/users/">
61
+ // both want to look for a / at index 6 to match URL `/users/matt`
62
+ const endSlashPosition =
63
+ toPathname !== '/' && toPathname.endsWith('/') ? toPathname.length - 1 : toPathname.length;
64
+ const isActive =
65
+ locationPathname === toPathname ||
66
+ !end && locationPathname.startsWith(toPathname) &&
67
+ locationPathname.charAt(endSlashPosition) === '/';
68
+
69
+ const isPending =
70
+ nextLocationPathname != null &&
71
+ (nextLocationPathname === toPathname ||
72
+ !end && nextLocationPathname.startsWith(toPathname) &&
73
+ nextLocationPathname.charAt(toPathname.length) === '/');
74
+
75
+ const renderProps = {
76
+ isActive,
77
+ isPending,
78
+ isTransitioning,
79
+ };
80
+
81
+ const ariaCurrent = isActive ? ariaCurrentProp : undefined;
82
+
83
+ let className;
84
+ if (typeof classNameProp === 'function') {
85
+ className = classNameProp(renderProps);
86
+ } else {
87
+ // If the className prop is not a function, we use a default `active`
88
+ // class for <NavLink />s that are active. In v5 `active` was the default
89
+ // value for `activeClassName`, but we are removing that API and can still
90
+ // use the old default behavior for a cleaner upgrade path and keep the
91
+ // simple styling rules working as they currently do.
92
+ className = [
93
+ classNameProp,
94
+ isActive ? 'active' : null,
95
+ isPending ? 'pending' : null,
96
+ isTransitioning ? 'transitioning' : null,
97
+ ].filter(Boolean).join(' ');
98
+ }
99
+
100
+ const style =
101
+ typeof styleProp === 'function' ? styleProp(renderProps) : styleProp;
102
+
103
+ // Render-prop children are called with the state object; block children
104
+ // pass through untouched (PROP position — see the children-forwarding rule).
105
+ const resolvedChildren =
106
+ typeof children === 'function' && !isChildrenBlock(children) ? children(renderProps) : children;
107
+
108
+ <Link
109
+ {...rest}
110
+ aria-current={ariaCurrent}
111
+ className={className}
112
+ ref={ref}
113
+ style={style}
114
+ to={to}
115
+ viewTransition={viewTransition}
116
+ children={resolvedChildren}
117
+ />
118
+ }
@@ -0,0 +1,33 @@
1
+ // Type declaration for the .tsrx component (resolved by relative path).
2
+ import type { To } from '../router/history';
3
+ import type { RelativeRoutingType } from '../router/router';
4
+
5
+ export type NavLinkRenderProps = {
6
+ isActive: boolean;
7
+ isPending: boolean;
8
+ isTransitioning: boolean;
9
+ };
10
+
11
+ export declare const NavLink: (props: {
12
+ to: To;
13
+ children?: unknown | ((props: NavLinkRenderProps) => unknown);
14
+ 'aria-current'?: string;
15
+ caseSensitive?: boolean;
16
+ className?: string | ((props: NavLinkRenderProps) => string | undefined);
17
+ end?: boolean;
18
+ style?: object | ((props: NavLinkRenderProps) => object | undefined);
19
+ onClick?: (event: MouseEvent) => void;
20
+ discover?: 'render' | 'none';
21
+ prefetch?: 'none' | 'intent' | 'render' | 'viewport';
22
+ relative?: RelativeRoutingType;
23
+ reloadDocument?: boolean;
24
+ replace?: boolean;
25
+ mask?: To;
26
+ state?: any;
27
+ target?: string;
28
+ preventScrollReset?: boolean;
29
+ viewTransition?: boolean;
30
+ defaultShouldRevalidate?: boolean;
31
+ ref?: unknown;
32
+ [key: string]: unknown;
33
+ }) => unknown;