@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.
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/package.json +53 -0
- package/src/dom.ts +16 -0
- package/src/index.ts +267 -0
- package/src/internal.ts +37 -0
- package/src/lib/Await.tsrx +145 -0
- package/src/lib/Await.tsrx.d.ts +6 -0
- package/src/lib/DefaultErrorComponent.tsrx +47 -0
- package/src/lib/DefaultErrorComponent.tsrx.d.ts +2 -0
- package/src/lib/RenderErrorBoundary.tsrx +117 -0
- package/src/lib/RenderErrorBoundary.tsrx.d.ts +14 -0
- package/src/lib/actions.ts +90 -0
- package/src/lib/components/MemoryRouter.tsrx +42 -0
- package/src/lib/components/MemoryRouter.tsrx.d.ts +10 -0
- package/src/lib/components/Navigate.ts +60 -0
- package/src/lib/components/Router.tsrx +88 -0
- package/src/lib/components/Router.tsrx.d.ts +13 -0
- package/src/lib/components/RouterProvider.tsrx +320 -0
- package/src/lib/components/RouterProvider.tsrx.d.ts +21 -0
- package/src/lib/components/Routes.tsrx +53 -0
- package/src/lib/components/Routes.tsrx.d.ts +7 -0
- package/src/lib/components/routes-collector.ts +317 -0
- package/src/lib/components/utils.ts +171 -0
- package/src/lib/components/with-props.ts +72 -0
- package/src/lib/context.ts +129 -0
- package/src/lib/dom/Form.tsrx +76 -0
- package/src/lib/dom/Form.tsrx.d.ts +22 -0
- package/src/lib/dom/Link.tsrx +91 -0
- package/src/lib/dom/Link.tsrx.d.ts +22 -0
- package/src/lib/dom/NavLink.tsrx +118 -0
- package/src/lib/dom/NavLink.tsrx.d.ts +33 -0
- package/src/lib/dom/dom.ts +344 -0
- package/src/lib/dom/hooks.ts +93 -0
- package/src/lib/dom/lib.ts +822 -0
- package/src/lib/dom/routers.tsrx +104 -0
- package/src/lib/dom/routers.tsrx.d.ts +23 -0
- package/src/lib/dom/server.tsrx +350 -0
- package/src/lib/dom/server.tsrx.d.ts +25 -0
- package/src/lib/errors.ts +84 -0
- package/src/lib/framework-stubs.ts +103 -0
- package/src/lib/hooks.ts +1797 -0
- package/src/lib/href.ts +71 -0
- package/src/lib/react-types.ts +10 -0
- package/src/lib/router/history.ts +763 -0
- package/src/lib/router/instrumentation.ts +496 -0
- package/src/lib/router/links.ts +192 -0
- package/src/lib/router/router.ts +7165 -0
- package/src/lib/router/server-runtime-types.ts +12 -0
- package/src/lib/router/url.ts +8 -0
- package/src/lib/router/utils.ts +2179 -0
- package/src/lib/server-runtime/cookies.ts +240 -0
- package/src/lib/server-runtime/crypto.ts +55 -0
- package/src/lib/server-runtime/mode.ts +16 -0
- package/src/lib/server-runtime/sessions/cookieStorage.ts +54 -0
- package/src/lib/server-runtime/sessions/memoryStorage.ts +59 -0
- package/src/lib/server-runtime/sessions.ts +291 -0
- package/src/lib/server-runtime/warnings.ts +10 -0
- package/src/lib/types/future.ts +16 -0
- package/src/lib/types/params.ts +8 -0
- package/src/lib/types/register.ts +39 -0
- package/src/lib/types/route-module.ts +17 -0
- 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;
|