@farbenmeer/router 0.5.0 → 0.6.1

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/dist/context.d.ts CHANGED
@@ -6,9 +6,10 @@ export declare const RouterContext: import("react").Context<{
6
6
  push: (href: string) => void;
7
7
  replace: (href: string) => void;
8
8
  }>;
9
- export declare const RouteContext: import("react").Context<{
9
+ export interface RouteContextValue {
10
10
  path: string;
11
11
  params: Record<string, string | string[]>;
12
12
  matchedPathname: string;
13
- }>;
13
+ }
14
+ export declare const RouteContext: import("react").Context<RouteContextValue>;
14
15
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,eAAO,MAAM,eAAe,iCAA6B,CAAC;AAE1D,eAAO,MAAM,mBAAmB,gDAE/B,CAAC;AAEF,eAAO,MAAM,WAAW,iCAA4B,CAAC;AAErD,eAAO,MAAM,aAAa;UAClB,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;aACnB,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;EAI/B,CAAC;AAEH,eAAO,MAAM,YAAY;UACjB,MAAM;YACJ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;qBACxB,MAAM;EAKvB,CAAC"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,eAAO,MAAM,eAAe,iCAA6B,CAAC;AAE1D,eAAO,MAAM,mBAAmB,gDAE/B,CAAC;AAEF,eAAO,MAAM,WAAW,iCAA4B,CAAC;AAErD,eAAO,MAAM,aAAa;UAClB,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;aACnB,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;EAI/B,CAAC;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,YAAY,4CAIvB,CAAC"}
package/dist/path.d.ts CHANGED
@@ -6,5 +6,8 @@ interface Options {
6
6
  }
7
7
  export declare function resolve(path: string, { pathname, parentPathname, searchParams }: Options): string;
8
8
  export declare function removeTrailingSlash(path: string): string;
9
+ export declare function compilePathRegex(path: string): RegExp;
10
+ export declare function compileExactPathRegex(path: string): RegExp;
11
+ export declare function buildFullPath(parentPath: string, path?: string): string;
9
12
  export {};
10
13
  //# sourceMappingURL=path.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,UAAU,OAAO;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,qBAAqB,CAAC;CACrC;AAED,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,OAAO,UAepD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxD"}
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,UAAU,OAAO;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,qBAAqB,CAAC;CACrC;AAED,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,OAAO,UAepD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBrD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU1D;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,UAa9D"}
package/dist/path.js CHANGED
@@ -19,3 +19,42 @@ export function removeTrailingSlash(path) {
19
19
  return path;
20
20
  return path.endsWith("/") ? path.slice(0, -1) : path;
21
21
  }
22
+ export function compilePathRegex(path) {
23
+ if (path === "/") {
24
+ return /^\//;
25
+ }
26
+ // Handle wildcards: *name captures as named group, * catches all without capturing
27
+ const pattern = path
28
+ .replaceAll(/\*(\w+)/g, "(?<$1>.+)") // *name -> named capture group
29
+ .replaceAll(/\*/g, ".+") // * -> match everything including /
30
+ .replaceAll(/:(\w+)/g, "(?<$1>[^\\/]+)"); // :param -> named capture group
31
+ // If pattern contains a wildcard, it already matches everything - use exact match
32
+ if (path.includes("*")) {
33
+ return new RegExp(`^(${pattern})$`);
34
+ }
35
+ // For non-wildcard paths, allow optional trailing paths
36
+ return new RegExp(`^(${pattern})(/.*)?$`);
37
+ }
38
+ export function compileExactPathRegex(path) {
39
+ if (path === "/") {
40
+ return /^\/$/;
41
+ }
42
+ // Handle wildcards: *name captures as named group, * catches all without capturing
43
+ const pattern = path
44
+ .replaceAll(/\*(\w+)/g, "(?<$1>.+)") // *name -> named capture group
45
+ .replaceAll(/\*/g, ".+") // * -> match everything including /
46
+ .replaceAll(/:(\w+)/g, "(?<$1>[^\\/]+)"); // :param -> named capture group
47
+ return new RegExp(`^(${pattern})$`);
48
+ }
49
+ export function buildFullPath(parentPath, path) {
50
+ if (path?.startsWith("/")) {
51
+ return path;
52
+ }
53
+ if (path) {
54
+ if (parentPath === "/") {
55
+ return "/" + path;
56
+ }
57
+ return parentPath + "/" + path;
58
+ }
59
+ return parentPath;
60
+ }
package/dist/route.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- interface Props {
1
+ export interface RouteProps {
2
2
  path?: string;
3
3
  exact?: boolean;
4
4
  children: React.ReactNode;
5
5
  }
6
- export declare function Route({ path, exact, children }: Props): import("react/jsx-runtime").JSX.Element | null;
7
- export {};
6
+ export declare function Route({ path, exact, children }: RouteProps): import("react/jsx-runtime").JSX.Element | null;
8
7
  //# sourceMappingURL=route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../src/route.tsx"],"names":[],"mappings":"AAGA,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,kDA0BrD"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../src/route.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,UAAU,kDA0B1D"}
package/dist/route.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { use, useMemo } from "react";
3
3
  import { PathnameContext, RouteContext } from "./context";
4
+ import { buildFullPath, compileExactPathRegex, compilePathRegex } from "./path";
4
5
  export function Route({ path, exact, children }) {
5
6
  const parentRoute = use(RouteContext);
6
7
  const pathname = use(PathnameContext);
@@ -16,42 +17,3 @@ export function Route({ path, exact, children }) {
16
17
  return null;
17
18
  return _jsx(RouteContext, { value: routeContextValue, children: children });
18
19
  }
19
- function compilePathRegex(path) {
20
- if (path === "/") {
21
- return /^\//;
22
- }
23
- // Handle wildcards: *name captures as named group, * catches all without capturing
24
- const pattern = path
25
- .replaceAll(/\*(\w+)/g, "(?<$1>.+)") // *name -> named capture group
26
- .replaceAll(/\*/g, ".+") // * -> match everything including /
27
- .replaceAll(/:(\w+)/g, "(?<$1>[\\w-]+)"); // :param -> named capture group
28
- // If pattern contains a wildcard, it already matches everything - use exact match
29
- if (path.includes("*")) {
30
- return new RegExp(`^(${pattern})$`);
31
- }
32
- // For non-wildcard paths, allow optional trailing paths
33
- return new RegExp(`^(${pattern})(/.*)?$`);
34
- }
35
- function compileExactPathRegex(path) {
36
- if (path === "/") {
37
- return /^\/$/;
38
- }
39
- // Handle wildcards: *name captures as named group, * catches all without capturing
40
- const pattern = path
41
- .replaceAll(/\*(\w+)/g, "(?<$1>.+)") // *name -> named capture group
42
- .replaceAll(/\*/g, ".+") // * -> match everything including /
43
- .replaceAll(/:(\w+)/g, "(?<$1>[\\w-]+)"); // :param -> named capture group
44
- return new RegExp(`^(${pattern})$`);
45
- }
46
- function buildFullPath(parentPath, path) {
47
- if (path?.startsWith("/")) {
48
- return path;
49
- }
50
- if (path) {
51
- if (parentPath === "/") {
52
- return "/" + path;
53
- }
54
- return parentPath + "/" + path;
55
- }
56
- return parentPath;
57
- }
@@ -0,0 +1,8 @@
1
+ import { type ReactElement } from "react";
2
+ import type { Route, RouteProps } from "./route";
3
+ interface Props {
4
+ children: ReactElement<RouteProps, typeof Route> | ReactElement<RouteProps, typeof Route>[];
5
+ }
6
+ export declare function Switch({ children }: Props): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
8
+ //# sourceMappingURL=switch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../src/switch.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAQjD,UAAU,KAAK;IACb,QAAQ,EACJ,YAAY,CAAC,UAAU,EAAE,OAAO,KAAK,CAAC,GACtC,YAAY,CAAC,UAAU,EAAE,OAAO,KAAK,CAAC,EAAE,CAAC;CAC9C;AAED,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,kDAmDzC"}
package/dist/switch.js ADDED
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Children, use, useMemo } from "react";
3
+ import { PathnameContext, RouteContext, } from "./context";
4
+ import { buildFullPath, compileExactPathRegex, compilePathRegex } from "./path";
5
+ export function Switch({ children }) {
6
+ const parentRoute = use(RouteContext);
7
+ const pathname = use(PathnameContext);
8
+ const props = Children.map(children, (route) => route.props);
9
+ const routeMeta = useMemo(() => props.map((route) => {
10
+ const path = route.path ?? "";
11
+ const fullPath = buildFullPath(parentRoute.path, path);
12
+ return {
13
+ path,
14
+ fullPath,
15
+ pathRegex: route.exact
16
+ ? compileExactPathRegex(fullPath)
17
+ : compilePathRegex(fullPath),
18
+ };
19
+ }), [
20
+ parentRoute.path,
21
+ props.map((route) => (route.exact ? "e" : "l" + route.path)).join(" "),
22
+ ]);
23
+ const match = useMemo(() => {
24
+ for (const meta of routeMeta) {
25
+ const match = pathname.match(meta.pathRegex);
26
+ if (match)
27
+ return [
28
+ meta.path,
29
+ {
30
+ path: meta.fullPath,
31
+ params: match?.groups ?? {},
32
+ matchedPathname: match?.[1] ?? "",
33
+ },
34
+ ];
35
+ }
36
+ return null;
37
+ }, [routeMeta]);
38
+ if (!match) {
39
+ return null;
40
+ }
41
+ const [path, context] = match;
42
+ return (_jsx(RouteContext, { value: context, children: props.find((route) => route.path === path)?.children }));
43
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farbenmeer/router",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "author": {
5
5
  "name": "Michel Smola",
6
6
  "email": "michel.smola@farbenmeer.de"
@@ -25,6 +25,7 @@
25
25
  "@vitest/browser-playwright": "^4.0.16",
26
26
  "react": "^19.1.1",
27
27
  "vitest": "^4.0.16",
28
+ "playwright": "^1.58.1",
28
29
  "vitest-browser-react": "^2.0.2"
29
30
  },
30
31
  "peerDependencies": {
@@ -34,6 +35,7 @@
34
35
  "scripts": {
35
36
  "build": "tsc -p tsconfig.build.json",
36
37
  "release": "pnpm run build && pnpm publish",
37
- "test": "vitest"
38
+ "test": "vitest",
39
+ "ci-setup": "playwright install --with-deps"
38
40
  }
39
41
  }