soseki 0.0.8 → 0.0.10

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 (37) hide show
  1. package/dist/src/components/browser-router.d.ts +2 -2
  2. package/dist/src/components/browser-router.d.ts.map +1 -1
  3. package/dist/src/components/router.d.ts +32 -2
  4. package/dist/src/components/router.d.ts.map +1 -1
  5. package/dist/src/core/match-route-path.d.ts +46 -0
  6. package/dist/src/core/match-route-path.d.ts.map +1 -0
  7. package/dist/src/core/match-route-path.js +16 -0
  8. package/dist/src/core/route-path.d.ts +46 -8
  9. package/dist/src/core/route-path.d.ts.map +1 -1
  10. package/dist/src/core/route-path.js +42 -14
  11. package/dist/src/core.d.ts +5 -0
  12. package/dist/src/core.d.ts.map +1 -1
  13. package/dist/src/core.js +2 -0
  14. package/dist/src/engines/engine.types.d.ts +8 -13
  15. package/dist/src/engines/engine.types.d.ts.map +1 -1
  16. package/dist/src/engines/navigation-api-engine.d.ts.map +1 -1
  17. package/dist/src/engines/navigation-api-engine.js +14 -23
  18. package/dist/src/hooks/use-navigate.d.ts +8 -0
  19. package/dist/src/hooks/use-navigate.d.ts.map +1 -1
  20. package/dist/src/hooks/use-navigate.js +25 -3
  21. package/dist/src/hooks/use-route-path.d.ts +3 -0
  22. package/dist/src/hooks/use-route-path.d.ts.map +1 -0
  23. package/dist/src/hooks/use-route-path.js +8 -0
  24. package/dist/src/soseki.d.ts +4 -0
  25. package/dist/src/soseki.d.ts.map +1 -1
  26. package/dist/src/soseki.js +2 -0
  27. package/package.json +1 -1
  28. package/src/components/browser-router.tsx +2 -3
  29. package/src/components/router.tsx +24 -2
  30. package/src/core/match-route-path.ts +78 -0
  31. package/src/core/route-path.ts +68 -16
  32. package/src/core.ts +8 -0
  33. package/src/engines/engine.types.ts +8 -15
  34. package/src/engines/navigation-api-engine.ts +14 -24
  35. package/src/hooks/use-navigate.ts +33 -4
  36. package/src/hooks/use-route-path.ts +11 -0
  37. package/src/soseki.ts +6 -0
@@ -1,4 +1,4 @@
1
- import type { RouteDefinition } from "../core/route.types.js";
1
+ import { type RouterRouteDefinition } from "./router.jsx";
2
2
  /**
3
3
  * `BrowserRouter` コンポーネントに引き渡すプロパティーの型定義です。
4
4
  */
@@ -6,7 +6,7 @@ export type BrowserRouterProps = {
6
6
  /**
7
7
  * アプリケーション全体の画面構造を定義したルート定義の配列です。
8
8
  */
9
- routes: readonly RouteDefinition[];
9
+ routes: readonly RouterRouteDefinition[];
10
10
  };
11
11
  /**
12
12
  * ブラウザー環境における SPA ルーティングを開始するための、最上位エントリーポイントコンポーネントです。
@@ -1 +1 @@
1
- {"version":3,"file":"browser-router.d.ts","sourceRoot":"","sources":["../../../src/components/browser-router.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAK9D;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CACpC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAKnF"}
1
+ {"version":3,"file":"browser-router.d.ts","sourceRoot":"","sources":["../../../src/components/browser-router.tsx"],"names":[],"mappings":"AAEA,OAAe,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC1C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAKnF"}
@@ -1,6 +1,36 @@
1
1
  import * as React from "react";
2
- import type { RouteDefinition } from "../core/route.types.js";
3
2
  import type { IEngine } from "../engines/engine.types.js";
3
+ export type RouterRouteDefinitionObject = {
4
+ readonly path: string;
5
+ readonly index?: boolean | undefined;
6
+ readonly action?: {
7
+ (args: any): unknown;
8
+ } | undefined;
9
+ readonly shouldReload?: {
10
+ (args: any): boolean;
11
+ } | undefined;
12
+ readonly loader?: {
13
+ (args: any): unknown;
14
+ } | undefined;
15
+ readonly component?: React.ComponentType<{}> | undefined;
16
+ };
17
+ export type RouterRouteDefinitionModule = {
18
+ readonly path: string;
19
+ readonly index?: boolean | undefined;
20
+ readonly action?: {
21
+ (args: any): unknown;
22
+ } | undefined;
23
+ readonly shouldReload?: {
24
+ (args: any): boolean;
25
+ } | undefined;
26
+ readonly loader?: {
27
+ (args: any): unknown;
28
+ } | undefined;
29
+ readonly component?: React.ComponentType<{}> | undefined;
30
+ readonly default?: React.ComponentType<{}> | undefined;
31
+ get [Symbol.toStringTag](): string;
32
+ };
33
+ export type RouterRouteDefinition = RouterRouteDefinitionObject | RouterRouteDefinitionModule;
4
34
  /**
5
35
  * `Router` コンポーネントに渡されるルートプロパティーの型定義です。
6
36
  */
@@ -12,7 +42,7 @@ export type RouterProps = {
12
42
  /**
13
43
  * ユーザーがアプリケーションに定義したルート定義の配列です。
14
44
  */
15
- routes: readonly RouteDefinition[];
45
+ routes: readonly RouterRouteDefinition[];
16
46
  };
17
47
  /**
18
48
  * 宣言的なルート定義と、命令的なルーティング実行エンジンを仲介・統合し、アプリケーションの最上位でルーティングのライフサイクルと状態管理を司るプロバイダーコンポーネントです。
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/components/router.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAW/B,OAAO,KAAK,EAAkC,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,KAAK,EAAE,OAAO,EAAe,MAAM,4BAA4B,CAAC;AAkEvE;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,EAAE,WAAW,4BAkIhD"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/components/router.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,OAAO,KAAK,EAAE,OAAO,EAAe,MAAM,4BAA4B,CAAC;AAkEvE,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,YAAY,CAAC,EAAE;QAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;CAC1D,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,YAAY,CAAC,EAAE;QAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,GAAG,2BAA2B,CAAC;AAE9F;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC1C,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,EAAE,WAAW,4BAkIhD"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * ルーティングの照合対象となる URL 情報を表すインターフェースです。
3
+ */
4
+ export interface MatchRoutePathTargetURL {
5
+ /**
6
+ * 照合対象のパス名です。
7
+ */
8
+ readonly pathname: string;
9
+ }
10
+ /**
11
+ * `matchRoutePath` 関数の動作を設定するための型定義です。
12
+ */
13
+ export type MatchRoutePathOptions = {
14
+ /**
15
+ * 照合の基準となるルーティングのパターン文字列です。
16
+ */
17
+ readonly pattern: string;
18
+ /**
19
+ * 照合対象とする文字列または URL オブジェクトです。
20
+ */
21
+ readonly target: string | MatchRoutePathTargetURL;
22
+ /**
23
+ * 子ディレクトリーへの前方一致を許可するかどうかを制御するフラグです。
24
+ *
25
+ * @default false
26
+ */
27
+ readonly allowChild?: boolean | undefined;
28
+ };
29
+ /**
30
+ * 指定されたパターンと対象のパス名が一致するかどうかを判定します。
31
+ *
32
+ * @param options パターン、対象、および一致条件を含む設定オブジェクトです。
33
+ * @returns パターンに一致した場合は `true` を、一致しない場合は `false` を返します。
34
+ */
35
+ declare function matchRoutePath(options: MatchRoutePathOptions): boolean;
36
+ /**
37
+ * 指定されたパターンと対象のパス名が一致するかどうかを判定します。
38
+ *
39
+ * @param pattern 照合の基準となるルーティングのパターン文字列です。
40
+ * @param target 照合対象とする文字列または URL オブジェクトです。
41
+ * @param options オプションです。
42
+ * @returns パターンに一致した場合は `true` を、一致しない場合は `false` を返します。
43
+ */
44
+ declare function matchRoutePath(pattern: MatchRoutePathOptions["pattern"], target: MatchRoutePathOptions["target"], options?: Omit<MatchRoutePathOptions, "pattern" | "target">): boolean;
45
+ export default matchRoutePath;
46
+ //# sourceMappingURL=match-route-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match-route-path.d.ts","sourceRoot":"","sources":["../../../src/core/match-route-path.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,uBAAuB,CAAC;IAElD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3C,CAAC;AAEF;;;;;GAKG;AACH,iBAAS,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC;AAEjE;;;;;;;GAOG;AACH,iBAAS,cAAc,CACrB,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,EACzC,MAAM,EAAE,qBAAqB,CAAC,QAAQ,CAAC,EACvC,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAC1D,OAAO,CAAC;AAqBX,eAAe,cAAc,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { parse } from "regexparam";
2
+ import RoutePath from "./route-path.js";
3
+ function matchRoutePath(...args) {
4
+ const { target, pattern, allowChild = false, } = typeof args[0] !== "string"
5
+ ? args[0]
6
+ : {
7
+ ...args[2],
8
+ target: args[1],
9
+ pattern: args[0],
10
+ };
11
+ const patternPathname = new RoutePath(pattern).pathname;
12
+ const targetPathname = typeof target === "string" ? target : target.pathname;
13
+ const patternRegex = parse(patternPathname, allowChild).pattern;
14
+ return patternRegex.test(targetPathname);
15
+ }
16
+ export default matchRoutePath;
@@ -1,10 +1,48 @@
1
- import type { ReadonlyURL } from "./readonly-url.types.js";
1
+ import type { ReadonlyURL, ReadonlyURLSearchParams } from "./readonly-url.types.js";
2
2
  /**
3
3
  * アプリケーション内のルーティングにおけるパスを安全に構築・解析・操作するためのクラスです。
4
4
  *
5
5
  * 内包するホストやプロトコルといった余分な情報を排除し、パス、クエリー、ハッシュのみを一貫した規則で管理します。
6
6
  */
7
- export default class RoutePath {
7
+ export interface ReadonlyRoutePath {
8
+ /**
9
+ * 正規化されたパス部分の文字列です。
10
+ */
11
+ readonly pathname: string;
12
+ /**
13
+ * 先頭に `?` を含むクエリー文字列です。
14
+ *
15
+ * 常にソートされます。
16
+ */
17
+ readonly search: string;
18
+ /**
19
+ * 内部の `URL` インスタンスが保持する、クエリーパラメーターを操作するための `URLSearchParams` オブジェクトを取得します。
20
+ */
21
+ readonly searchParams: ReadonlyURLSearchParams;
22
+ /**
23
+ * 先頭に `#` を含むハッシュ文字列を取得します。
24
+ */
25
+ readonly hash: string;
26
+ /**
27
+ * 現在保持しているすべてのコンポーネントを結合し、ルーティング用のパス文字列として出力します。
28
+ *
29
+ * @returns ソート済みのクエリーおよびハッシュを含んだ、正規化されたパス全体の文字列を返します。
30
+ */
31
+ toString(): string;
32
+ /**
33
+ * このインスタンスを複製し、上書き可能な形式で取得します。
34
+ *
35
+ * @returns 上書き可能な {@link RoutePath|`RoutePath`} インスタンスです。
36
+ */
37
+ clone(): RoutePath;
38
+ }
39
+ /**
40
+ * アプリケーション内のルーティングにおけるパスを安全に構築・解析・操作するためのクラスです。
41
+ *
42
+ * 内包するホストやプロトコルといった余分な情報を排除し、パス、クエリー、ハッシュのみを一貫した規則で管理します。
43
+ */
44
+ export default class RoutePath implements ReadonlyRoutePath {
45
+ #private;
8
46
  /**
9
47
  * 与えられた文字列または URL オブジェクトから、正規化されたパス文字列を即座に生成する静的メソッドです。
10
48
  *
@@ -12,12 +50,6 @@ export default class RoutePath {
12
50
  * @returns 冗長なスラッシュや末尾のスラッシュが取り除かれた、結合済みのパス文字列を返します。
13
51
  */
14
52
  static encode(path: string | Pick<ReadonlyURL, "pathname" | "search" | "hash">): string;
15
- /**
16
- * 内部でパスコンポーネントの解析と検証を委譲するために保持する、組み込みの `URL` インスタンスです。
17
- *
18
- * 基底となる仮想的なオリジンと結合して管理されます。
19
- */
20
- private url;
21
53
  /**
22
54
  * 新しい `RoutePath` インスタンスを初期化します。
23
55
  *
@@ -53,5 +85,11 @@ export default class RoutePath {
53
85
  * @returns ソート済みのクエリーおよびハッシュを含んだ、正規化されたパス全体の文字列を返します。
54
86
  */
55
87
  toString(): string;
88
+ /**
89
+ * このインスタンスを複製し、上書き可能な形式で取得します。
90
+ *
91
+ * @returns 上書き可能な {@link RoutePath|`RoutePath`} インスタンスです。
92
+ */
93
+ clone(): RoutePath;
56
94
  }
57
95
  //# sourceMappingURL=route-path.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route-path.d.ts","sourceRoot":"","sources":["../../../src/core/route-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAO3D;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B;;;;;OAKG;WACW,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC,GAAG,MAAM;IAI9F;;;;OAIG;IACH,OAAO,CAAC,GAAG,CAAM;IAEjB;;;;;;OAMG;gBACgB,IAAI,GAAE,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAM;IAkBxF;;OAEG;IACH,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,IAAW,QAAQ,CAAC,KAAK,EAAE,MAAM,EAQhC;IAED;;;;OAIG;IACH,IAAW,MAAM,IAAI,MAAM,CAG1B;IAED,IAAW,MAAM,CAAC,KAAK,EAAE,MAAM,EAE9B;IAED;;OAEG;IACH,IAAW,YAAY,IAAI,eAAe,CAEzC;IAED;;OAEG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED,IAAW,IAAI,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED;;;;OAIG;IACI,QAAQ,IAAI,MAAM;CAI1B"}
1
+ {"version":3,"file":"route-path.d.ts","sourceRoot":"","sources":["../../../src/core/route-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAOpF;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAE/C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,QAAQ,IAAI,MAAM,CAAC;IAEnB;;;;OAIG;IACH,KAAK,IAAI,SAAS,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,YAAW,iBAAiB;;IACzD;;;;;OAKG;WACW,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC,GAAG,MAAM;IAW9F;;;;;;OAMG;gBACgB,IAAI,GAAE,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAM;IAkBxF;;OAEG;IACH,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,IAAW,QAAQ,CAAC,KAAK,EAAE,MAAM,EAQhC;IAED;;;;OAIG;IACH,IAAW,MAAM,IAAI,MAAM,CAG1B;IAED,IAAW,MAAM,CAAC,KAAK,EAAE,MAAM,EAE9B;IAED;;OAEG;IACH,IAAW,YAAY,IAAI,eAAe,CAEzC;IAED;;OAEG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED,IAAW,IAAI,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED;;;;OAIG;IACI,QAAQ,IAAI,MAAM;IAKzB;;;;OAIG;IACI,KAAK,IAAI,SAAS;CAG1B"}
@@ -1,3 +1,15 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _RoutePath_url;
1
13
  /**
2
14
  * 連続する複数のスラッシュを検出するための正規表現です。
3
15
  */
@@ -7,7 +19,7 @@ const MULTI_SLASH = /\/\/+/gu;
7
19
  *
8
20
  * 内包するホストやプロトコルといった余分な情報を排除し、パス、クエリー、ハッシュのみを一貫した規則で管理します。
9
21
  */
10
- export default class RoutePath {
22
+ class RoutePath {
11
23
  /**
12
24
  * 与えられた文字列または URL オブジェクトから、正規化されたパス文字列を即座に生成する静的メソッドです。
13
25
  *
@@ -25,6 +37,12 @@ export default class RoutePath {
25
37
  * @param path 初期化に使用するパス文字列、または `ReadonlyURL` の一部のプロパティーを持つオブジェクトです。既定値は空文字列です。
26
38
  */
27
39
  constructor(path = "") {
40
+ /**
41
+ * 内部でパスコンポーネントの解析と検証を委譲するために保持する、組み込みの `URL` インスタンスです。
42
+ *
43
+ * 基底となる仮想的なオリジンと結合して管理されます。
44
+ */
45
+ _RoutePath_url.set(this, void 0);
28
46
  if (typeof path === "string") {
29
47
  path = ("/" + path).replace(MULTI_SLASH, "/");
30
48
  }
@@ -33,25 +51,25 @@ export default class RoutePath {
33
51
  path = ("/" + pathname).replace(MULTI_SLASH, "/") + search + hash;
34
52
  }
35
53
  // 組み込みの URL クラスによる厳密な解析機能を利用するため、仮想のプロトコルとホストを前置きして初期化します。
36
- this.url = new URL("x://y" + path);
54
+ __classPrivateFieldSet(this, _RoutePath_url, new URL("x://y" + path), "f");
37
55
  // ルートパス(/)単体である場合を除き、末尾に存在する不要なスラッシュを削除して一貫性を保ちます。
38
- const { pathname } = this.url;
56
+ const { pathname } = __classPrivateFieldGet(this, _RoutePath_url, "f");
39
57
  if (pathname !== "/" && pathname.endsWith("/")) {
40
- this.url.pathname = pathname.substring(0, pathname.length - 1);
58
+ __classPrivateFieldGet(this, _RoutePath_url, "f").pathname = pathname.substring(0, pathname.length - 1);
41
59
  }
42
60
  }
43
61
  /**
44
62
  * 正規化されたパス部分の文字列です。
45
63
  */
46
64
  get pathname() {
47
- return this.url.pathname;
65
+ return __classPrivateFieldGet(this, _RoutePath_url, "f").pathname;
48
66
  }
49
67
  set pathname(value) {
50
- this.url.pathname = value;
68
+ __classPrivateFieldGet(this, _RoutePath_url, "f").pathname = value;
51
69
  // ルートパス(/)単体である場合を除き、末尾に存在する不要なスラッシュを削除して一貫性を保ちます。
52
- const pathname = ("/" + this.url.pathname).replace(MULTI_SLASH, "/");
70
+ const pathname = ("/" + __classPrivateFieldGet(this, _RoutePath_url, "f").pathname).replace(MULTI_SLASH, "/");
53
71
  if (pathname !== "/" && pathname.endsWith("/")) {
54
- this.url.pathname = pathname.substring(0, pathname.length - 1);
72
+ __classPrivateFieldGet(this, _RoutePath_url, "f").pathname = pathname.substring(0, pathname.length - 1);
55
73
  }
56
74
  }
57
75
  /**
@@ -60,26 +78,26 @@ export default class RoutePath {
60
78
  * 常にソートされます。
61
79
  */
62
80
  get search() {
63
- this.url.searchParams.sort();
64
- return this.url.search;
81
+ __classPrivateFieldGet(this, _RoutePath_url, "f").searchParams.sort();
82
+ return __classPrivateFieldGet(this, _RoutePath_url, "f").search;
65
83
  }
66
84
  set search(value) {
67
- this.url.search = value;
85
+ __classPrivateFieldGet(this, _RoutePath_url, "f").search = value;
68
86
  }
69
87
  /**
70
88
  * 内部の `URL` インスタンスが保持する、クエリーパラメーターを操作するための `URLSearchParams` オブジェクトを取得します。
71
89
  */
72
90
  get searchParams() {
73
- return this.url.searchParams;
91
+ return __classPrivateFieldGet(this, _RoutePath_url, "f").searchParams;
74
92
  }
75
93
  /**
76
94
  * 先頭に `#` を含むハッシュ文字列を取得します。
77
95
  */
78
96
  get hash() {
79
- return this.url.hash;
97
+ return __classPrivateFieldGet(this, _RoutePath_url, "f").hash;
80
98
  }
81
99
  set hash(value) {
82
- this.url.hash = value;
100
+ __classPrivateFieldGet(this, _RoutePath_url, "f").hash = value;
83
101
  }
84
102
  /**
85
103
  * 現在保持しているすべてのコンポーネントを結合し、ルーティング用のパス文字列として出力します。
@@ -90,4 +108,14 @@ export default class RoutePath {
90
108
  const { hash, search, pathname } = this;
91
109
  return pathname + search + hash;
92
110
  }
111
+ /**
112
+ * このインスタンスを複製し、上書き可能な形式で取得します。
113
+ *
114
+ * @returns 上書き可能な {@link RoutePath|`RoutePath`} インスタンスです。
115
+ */
116
+ clone() {
117
+ return new RoutePath(this.toString());
118
+ }
93
119
  }
120
+ _RoutePath_url = new WeakMap();
121
+ export default RoutePath;
@@ -22,4 +22,9 @@ export type * from "./core/start-action.js";
22
22
  export { default as startAction } from "./core/start-action.js";
23
23
  export type * from "./core/start-loaders.js";
24
24
  export { default as startLoaders } from "./core/start-loaders.js";
25
+ /**************************************************************************************************/
26
+ export type * from "./hooks/use-route-context.js";
27
+ export { default as useRouteContext } from "./hooks/use-route-context.js";
28
+ export type * from "./hooks/use-router-context.js";
29
+ export { default as useRouterContext } from "./hooks/use-router-context.js";
25
30
  //# sourceMappingURL=core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"AAAA,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE5D,oGAAoG;AAEpG,mBAAmB,6BAA6B,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEtE,mBAAmB,8BAA8B,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAExE,oGAAoG;AAEpG,mBAAmB,gCAAgC,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAE/E,mBAAmB,mCAAmC,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEpF,mBAAmB,oCAAoC,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAEtF,mBAAmB,wBAAwB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEhE,mBAAmB,wBAAwB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEhE,mBAAmB,sBAAsB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE5D,mBAAmB,wBAAwB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEhE,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"AAAA,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE5D,oGAAoG;AAEpG,mBAAmB,6BAA6B,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEtE,mBAAmB,8BAA8B,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAExE,oGAAoG;AAEpG,mBAAmB,gCAAgC,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAE/E,mBAAmB,mCAAmC,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEpF,mBAAmB,oCAAoC,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAEtF,mBAAmB,wBAAwB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEhE,mBAAmB,wBAAwB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEhE,mBAAmB,sBAAsB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE5D,mBAAmB,wBAAwB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEhE,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAElE,oGAAoG;AAEpG,mBAAmB,8BAA8B,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE1E,mBAAmB,+BAA+B,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,+BAA+B,CAAC"}
package/dist/src/core.js CHANGED
@@ -9,3 +9,5 @@ export { default as matchRoutes } from "./core/match-routes.js";
9
9
  export { default as RoutePath } from "./core/route-path.js";
10
10
  export { default as startAction } from "./core/start-action.js";
11
11
  export { default as startLoaders } from "./core/start-loaders.js";
12
+ export { default as useRouteContext } from "./hooks/use-route-context.js";
13
+ export { default as useRouterContext } from "./hooks/use-router-context.js";
@@ -1,4 +1,5 @@
1
1
  import type { NinjaPromise } from "ninja-promise";
2
+ import type { RoutePath } from "../core.js";
2
3
  import type { HistoryEntry } from "../core/expect-history-entry.js";
3
4
  import type { HistoryEntryId } from "../core/history-entry-id-schema.js";
4
5
  import type { MatchedRoute } from "../core/match-routes.js";
@@ -151,28 +152,22 @@ export declare namespace IEngine {
151
152
  /**
152
153
  * URL パスで前方移動する形式です。
153
154
  */
154
- type: "PATH";
155
+ type: "STATIC";
155
156
  /**
156
157
  * URL パスです。
157
158
  */
158
159
  path: string;
159
160
  } | {
160
161
  /**
161
- * URL の各コンポーネントを個別に指定する形式です。
162
+ * URL の各コンポーネントを関数形式で個別に指定する形式です。
162
163
  */
163
- type: "PARTIAL";
164
+ type: "DYNAMIC";
164
165
  /**
165
- * 最初のスラッシュ `/` から始まる URL のパス部分です。
166
- */
167
- pathname?: string | undefined;
168
- /**
169
- * 先頭のクエスチョンマーク `?` を含む URL のクエリー文字列です。
170
- */
171
- search?: string | undefined;
172
- /**
173
- * URL のハッシュ(シャープ記号 `#` を含むフラグメント識別子)です。
166
+ * 動的にパッチを適用する関数です。
167
+ *
168
+ * @param path アプリケーション内のルーティングにおけるパスを安全に構築・解析・操作するためのオブジェクトです。
174
169
  */
175
- hash?: string | undefined;
170
+ patch(path: RoutePath): void;
176
171
  };
177
172
  /**
178
173
  * 履歴スタックへの追加方法を指定します。
@@ -1 +1 @@
1
- {"version":3,"file":"engine.types.d.ts","sourceRoot":"","sources":["../../../src/engines/engine.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEpF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,KAAK,EAAE,YAAY,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,SAAS,CAAC,YAAY,EAAE,GAAG,YAAY,EAAE,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,IAAI,CAAC;CACV;AAED;;GAEG;AACH,yBAAiB,OAAO,CAAC;IACvB;;OAEG;IACH,KAAY,QAAQ,GAAG;QACrB;;WAEG;QACH,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;QAEzB;;WAEG;QACH,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEjF;;WAEG;QACH,SAAS,EAAE,MAAM,WAAW,CAAC;KAC9B,CAAC;IAEF;;;;OAIG;IACH,KAAY,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC;IAE5C;;;;OAIG;IACH,KAAY,SAAS,GAAG;QACtB;;WAEG;QACH,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;QAEzB;;WAEG;QACH,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEjF;;WAEG;QACH,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEjF;;;;WAIG;QACH,MAAM,EAAE;YACN;;eAEG;YACH,IAAI,IAAI,CAAC;YAET;;;;eAIG;YACH,CAAC,cAAc,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;SAC5C,CAAC;QAEF;;WAEG;QACH,SAAS,EAAE,MAAM,WAAW,CAAC;KAC9B,CAAC;IAEF;;;;OAIG;IACH,KAAY,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC;IAE7C;;OAEG;IACH,KAAY,UAAU,GAClB;QACE;;WAEG;QACH,IAAI,EAAE,WAAW,CAAC;QAElB;;WAEG;QACH,MAAM,EAAE,gBAAgB,CAAC;QAEzB;;WAEG;QACH,MAAM,EAAE,MAAM,CAAC;KAChB,GACD;QACE;;WAEG;QACH,IAAI,EAAE,mBAAmB,CAAC;QAE1B;;WAEG;QACH,MAAM,EAAE,uBAAuB,CAAC;QAEhC;;WAEG;QACH,MAAM,EAAE,MAAM,CAAC;QAEf;;;;;WAKG;QACH,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC;KAC7B,CAAC;IAEN;;;;OAIG;IACH,KAAY,YAAY,GACpB;QACE;;WAEG;QACH,IAAI,EAAE,MAAM,CAAC;QAEb;;WAEG;QACH,EAAE,EACE;YACE;;eAEG;YACH,IAAI,EAAE,MAAM,CAAC;YAEb;;eAEG;YACH,IAAI,EAAE,MAAM,CAAC;SACd,GACD;YACE;;eAEG;YACH,IAAI,EAAE,SAAS,CAAC;YAEhB;;eAEG;YACH,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;YAE9B;;eAEG;YACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;YAE5B;;eAEG;YACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;SAC3B,CAAC;QAEN;;;;;WAKG;QACH,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC;KAC7B,GACD;QACE;;WAEG;QACH,IAAI,EAAE,MAAM,CAAC;QAEb;;WAEG;QACH,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACP;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAEjD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAEpD;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAC5C"}
1
+ {"version":3,"file":"engine.types.d.ts","sourceRoot":"","sources":["../../../src/engines/engine.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEpF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,KAAK,EAAE,YAAY,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,SAAS,CAAC,YAAY,EAAE,GAAG,YAAY,EAAE,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,IAAI,CAAC;CACV;AAED;;GAEG;AACH,yBAAiB,OAAO,CAAC;IACvB;;OAEG;IACH,KAAY,QAAQ,GAAG;QACrB;;WAEG;QACH,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;QAEzB;;WAEG;QACH,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEjF;;WAEG;QACH,SAAS,EAAE,MAAM,WAAW,CAAC;KAC9B,CAAC;IAEF;;;;OAIG;IACH,KAAY,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC;IAE5C;;;;OAIG;IACH,KAAY,SAAS,GAAG;QACtB;;WAEG;QACH,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;QAEzB;;WAEG;QACH,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEjF;;WAEG;QACH,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEjF;;;;WAIG;QACH,MAAM,EAAE;YACN;;eAEG;YACH,IAAI,IAAI,CAAC;YAET;;;;eAIG;YACH,CAAC,cAAc,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;SAC5C,CAAC;QAEF;;WAEG;QACH,SAAS,EAAE,MAAM,WAAW,CAAC;KAC9B,CAAC;IAEF;;;;OAIG;IACH,KAAY,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC;IAE7C;;OAEG;IACH,KAAY,UAAU,GAClB;QACE;;WAEG;QACH,IAAI,EAAE,WAAW,CAAC;QAElB;;WAEG;QACH,MAAM,EAAE,gBAAgB,CAAC;QAEzB;;WAEG;QACH,MAAM,EAAE,MAAM,CAAC;KAChB,GACD;QACE;;WAEG;QACH,IAAI,EAAE,mBAAmB,CAAC;QAE1B;;WAEG;QACH,MAAM,EAAE,uBAAuB,CAAC;QAEhC;;WAEG;QACH,MAAM,EAAE,MAAM,CAAC;QAEf;;;;;WAKG;QACH,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC;KAC7B,CAAC;IAEN;;;;OAIG;IACH,KAAY,YAAY,GACpB;QACE;;WAEG;QACH,IAAI,EAAE,MAAM,CAAC;QAEb;;WAEG;QACH,EAAE,EACE;YACE;;eAEG;YACH,IAAI,EAAE,QAAQ,CAAC;YAEf;;eAEG;YACH,IAAI,EAAE,MAAM,CAAC;SACd,GACD;YACE;;eAEG;YACH,IAAI,EAAE,SAAS,CAAC;YAEhB;;;;eAIG;YACH,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;SAC9B,CAAC;QAEN;;;;;WAKG;QACH,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC;KAC7B,GACD;QACE;;WAEG;QACH,IAAI,EAAE,MAAM,CAAC;QAEb;;WAEG;QACH,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACP;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAEjD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAEpD;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAC5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-api-engine.d.ts","sourceRoot":"","sources":["../../../src/engines/navigation-api-engine.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,YAAW,OAAO;IACzD;;OAEG;IACH,OAAO,CAAC,UAAU,CAAa;IAE/B;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAsB;IAEhD;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAyB;IAEnD;;;;OAIG;;IAwBH;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU;IA2BhD;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,WAAW;IAgSnD;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,GAAG,IAAI;IAkCtC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,GAAG,IAAI;CAwE3C"}
1
+ {"version":3,"file":"navigation-api-engine.d.ts","sourceRoot":"","sources":["../../../src/engines/navigation-api-engine.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,YAAW,OAAO;IACzD;;OAEG;IACH,OAAO,CAAC,UAAU,CAAa;IAE/B;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAsB;IAEhD;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAyB;IAEnD;;;;OAIG;;IAwBH;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU;IA2BhD;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,WAAW;IA8RnD;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,GAAG,IAAI;IAkCtC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,GAAG,IAAI;CAgE3C"}
@@ -78,8 +78,7 @@ export default class NavigationApiEngine {
78
78
  * ユーザーのアクションによって発生したすべての遷移要求をインターセプトして処理する、ルーティングの中枢ハンドラーです。
79
79
  */
80
80
  const handleNavigate = (event) => {
81
- // 処理すべきでない通常のブラウザー固有のナビゲーション(ハッシュ変更、ファイルのダウンロードなど)は、
82
- // 標準の挙動を妨げないようにインターセプトせず即座にスルーします。
81
+ // 処理すべきでない通常のブラウザー固有のナビゲーション(ハッシュ変更、ファイルのダウンロードなど)は、標準の挙動を妨げないようにインターセプトせず即座にスルーします。
83
82
  // 参照: https://developer.mozilla.org/docs/Web/API/Navigation_API#handling_a_navigation_using_intercept
84
83
  if (!event.isTrusted ||
85
84
  !event.canIntercept ||
@@ -146,19 +145,18 @@ export default class NavigationApiEngine {
146
145
  switch (action.data.status) {
147
146
  case "rejected": {
148
147
  // アクションがエラーで失敗した場合は、URL を変更せず現在の元のページに強制リダイレクトさせます。
149
- const { pathname, search, hash } = currentEntry.url;
150
- controller.redirect(pathname + search + hash);
148
+ controller.redirect(RoutePath.encode(currentEntry.url));
151
149
  break;
152
150
  }
153
151
  case "fulfilled": {
154
152
  // アクションが正常終了した場合、返り値にリダイレクト指示が含まれていればその目的地へ遷移させます。
155
153
  // リダイレクトがなければそのまま本来の目的地へとブラウザーのコミット先を書き換えます。
156
154
  const { redirectTo = currentEntry.url } = actionResponse;
157
- const { pathname, search, hash } = redirectTo;
158
- controller.redirect(pathname + search + hash);
159
- redirectUrl.pathname = pathname;
160
- redirectUrl.search = search;
161
- redirectUrl.hash = hash;
155
+ const redirectPath = new RoutePath(redirectTo);
156
+ controller.redirect(redirectPath.toString());
157
+ redirectUrl.pathname = redirectPath.pathname;
158
+ redirectUrl.search = redirectPath.search;
159
+ redirectUrl.hash = redirectPath.hash;
162
160
  break;
163
161
  }
164
162
  default:
@@ -356,28 +354,21 @@ export default class NavigationApiEngine {
356
354
  case "LINK": {
357
355
  const { to, history } = args;
358
356
  switch (to.type) {
359
- case "PATH": {
357
+ case "STATIC": {
360
358
  // 完全なパス文字列の余分なスラッシュなどをエンコードして直接遷移します。
361
359
  const path = RoutePath.encode(to.path);
362
360
  this.navigation.navigate(path, { history });
363
361
  break;
364
362
  }
365
- case "PARTIAL": {
366
- // 現在のロケーション情報をベースに、指定されたパーツ(パス名、クエリー、ハッシュのみなど)を部分的にパッチ(上書き)したマージ URL を算出します。
363
+ case "DYNAMIC": {
364
+ // 現在のロケーション情報をベースに、指定されたパーツ(パス名、クエリー、ハッシュのみなど)を部分的に上書きしたマージ URL を算出します。
367
365
  const currentPath = new RoutePath(window.location);
368
- const nextPath = new RoutePath(window.location);
369
- if (typeof to.pathname === "string") {
370
- nextPath.pathname = to.pathname;
371
- }
372
- if (typeof to.search === "string") {
373
- nextPath.search = to.search;
374
- }
375
- if (typeof to.hash === "string") {
376
- nextPath.hash = to.hash;
377
- }
366
+ const currentPathString = currentPath.toString();
367
+ const nextPath = currentPath;
368
+ to.patch(nextPath);
378
369
  const nextPathString = nextPath.toString();
379
370
  // 無駄な遷移履歴を作らないように、URL に実際の変化がある場合のみ navigate を実行します。
380
- if (nextPathString !== currentPath.toString()) {
371
+ if (nextPathString !== currentPathString) {
381
372
  this.navigation.navigate(nextPathString, { history });
382
373
  }
383
374
  break;
@@ -1,3 +1,4 @@
1
+ import type { RoutePath } from "../core.js";
1
2
  /**
2
3
  * 遷移先のアドレスを指定するための表現型です。
3
4
  *
@@ -16,6 +17,13 @@ export type NavigateTo = string | {
16
17
  * 遷移先に付与するハッシュフラグメント(例: `"#profile"`)です。
17
18
  */
18
19
  readonly hash?: string | undefined;
20
+ } | {
21
+ /**
22
+ * 動的にパッチを適用します。
23
+ *
24
+ * @param path アプリケーション内のルーティングにおけるパスを安全に構築・解析・操作するためのオブジェクトです。
25
+ */
26
+ (route: RoutePath): void;
19
27
  };
20
28
  /**
21
29
  * 画面遷移の挙動をカスタマイズするためのオプション型です。
@@ -1 +1 @@
1
- {"version":3,"file":"use-navigate.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-navigate.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAClB,MAAM,GACN;IACE;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAErC;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAEN;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAElD;;;;OAIG;IACH,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,IAAI,gBAAgB,CAwCtD"}
1
+ {"version":3,"file":"use-navigate.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-navigate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAClB,MAAM,GACN;IACE;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAErC;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,GACD;IACE;;;;OAIG;IACH,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEN;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAElD;;;;OAIG;IACH,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,IAAI,gBAAgB,CA6DtD"}
@@ -23,7 +23,27 @@ export default function useNavigate() {
23
23
  return routerNavigate({
24
24
  to: {
25
25
  path: to,
26
- type: "PATH",
26
+ type: "STATIC",
27
+ },
28
+ type: "LINK",
29
+ history,
30
+ });
31
+ }
32
+ else if (typeof to === "object") {
33
+ return routerNavigate({
34
+ to: {
35
+ type: "DYNAMIC",
36
+ patch(path) {
37
+ if (typeof to.pathname === "string") {
38
+ path.pathname = to.pathname;
39
+ }
40
+ if (typeof to.search === "string") {
41
+ path.search = to.search;
42
+ }
43
+ if (typeof to.hash === "string") {
44
+ path.hash = to.hash;
45
+ }
46
+ },
27
47
  },
28
48
  type: "LINK",
29
49
  history,
@@ -32,8 +52,10 @@ export default function useNavigate() {
32
52
  else {
33
53
  return routerNavigate({
34
54
  to: {
35
- ...to,
36
- type: "PARTIAL",
55
+ type: "DYNAMIC",
56
+ patch(path) {
57
+ to(path);
58
+ },
37
59
  },
38
60
  type: "LINK",
39
61
  history,
@@ -0,0 +1,3 @@
1
+ import { type ReadonlyRoutePath } from "../core/route-path.js";
2
+ export default function useRoutePath(): ReadonlyRoutePath;
3
+ //# sourceMappingURL=use-route-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-route-path.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-route-path.ts"],"names":[],"mappings":"AAEA,OAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG1E,MAAM,CAAC,OAAO,UAAU,YAAY,IAAI,iBAAiB,CAKxD"}
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import RoutePath from "../core/route-path.js";
3
+ import useRouterContext from "./use-router-context.js";
4
+ export default function useRoutePath() {
5
+ const url = useRouterContext((router) => router.currentEntry.url);
6
+ const path = React.useMemo(() => new RoutePath(url), [url]);
7
+ return path;
8
+ }
@@ -5,6 +5,8 @@ export { default as Outlet } from "./components/outlet.jsx";
5
5
  /**************************************************************************************************/
6
6
  export type { Issue, UnreachableErrorArgs, UnreachableErrorMeta, LoaderConditionErrorArgs, LoaderConditionErrorMeta, LoaderDataNotFoundErrorArgs, LoaderDataNotFoundErrorMeta, UnexpectedValidationErrorArgs, UnexpectedValidationErrorMeta, } from "./core/errors.js";
7
7
  export { ErrorBase, setErrorMessage, UnreachableError, ValidationErrorBase, LoaderConditionError, LoaderDataNotFoundError, RouteContextMissingError, RouterContextMissingError, UnexpectedValidationError, NavigationApiNotSupportedError, } from "./core/errors.js";
8
+ export type * from "./core/match-route-path.js";
9
+ export { default as matchRoutePath } from "./core/match-route-path.js";
8
10
  export type * from "./core/readonly-form-data.types.js";
9
11
  export type * from "./core/readonly-url.types.js";
10
12
  export type * from "./core/redirect-response.js";
@@ -23,6 +25,8 @@ export type * from "./hooks/use-navigate.js";
23
25
  export { default as useNavigate } from "./hooks/use-navigate.js";
24
26
  export type * from "./hooks/use-params.js";
25
27
  export { default as useParams } from "./hooks/use-params.js";
28
+ export type * from "./hooks/use-route-path.js";
29
+ export { default as useRoutePath } from "./hooks/use-route-path.js";
26
30
  export type * from "./hooks/use-submit.js";
27
31
  export { default as useSubmit } from "./hooks/use-submit.js";
28
32
  /**************************************************************************************************/
@@ -1 +1 @@
1
- {"version":3,"file":"soseki.d.ts","sourceRoot":"","sources":["../../src/soseki.ts"],"names":[],"mappings":"AAAA,mBAAmB,iCAAiC,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAE3E,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE5D,oGAAoG;AAEpG,YAAY,EACV,KAAK,EACL,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,EAC3B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAC;AAE1B,mBAAmB,oCAAoC,CAAC;AAExD,mBAAmB,8BAA8B,CAAC;AAElD,mBAAmB,6BAA6B,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAElE,mBAAmB,uBAAuB,CAAC;AAE3C,oGAAoG;AAEpG,mBAAmB,4BAA4B,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEtE,mBAAmB,4BAA4B,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEtE,mBAAmB,4BAA4B,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEtE,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEjE,mBAAmB,uBAAuB,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE7D,mBAAmB,uBAAuB,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE7D,oGAAoG;AAEpG,mBAAmB,qBAAqB,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"soseki.d.ts","sourceRoot":"","sources":["../../src/soseki.ts"],"names":[],"mappings":"AAAA,mBAAmB,iCAAiC,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAE3E,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE5D,oGAAoG;AAEpG,YAAY,EACV,KAAK,EACL,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,EAC3B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAC;AAE1B,mBAAmB,4BAA4B,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEvE,mBAAmB,oCAAoC,CAAC;AAExD,mBAAmB,8BAA8B,CAAC;AAElD,mBAAmB,6BAA6B,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAElE,mBAAmB,uBAAuB,CAAC;AAE3C,oGAAoG;AAEpG,mBAAmB,4BAA4B,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEtE,mBAAmB,4BAA4B,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEtE,mBAAmB,4BAA4B,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEtE,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEjE,mBAAmB,uBAAuB,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE7D,mBAAmB,2BAA2B,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEpE,mBAAmB,uBAAuB,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE7D,oGAAoG;AAEpG,mBAAmB,qBAAqB,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  export { default as BrowserRouter } from "./components/browser-router.jsx";
2
2
  export { default as Outlet } from "./components/outlet.jsx";
3
3
  export { ErrorBase, setErrorMessage, UnreachableError, ValidationErrorBase, LoaderConditionError, LoaderDataNotFoundError, RouteContextMissingError, RouterContextMissingError, UnexpectedValidationError, NavigationApiNotSupportedError, } from "./core/errors.js";
4
+ export { default as matchRoutePath } from "./core/match-route-path.js";
4
5
  export { default as RedirectResponse } from "./core/redirect-response.js";
5
6
  export { default as RouteRequest } from "./core/route-request.js";
6
7
  export { default as useActionData } from "./hooks/use-action-data.js";
@@ -8,5 +9,6 @@ export { default as useFormAction } from "./hooks/use-form-action.js";
8
9
  export { default as useLoaderData } from "./hooks/use-loader-data.js";
9
10
  export { default as useNavigate } from "./hooks/use-navigate.js";
10
11
  export { default as useParams } from "./hooks/use-params.js";
12
+ export { default as useRoutePath } from "./hooks/use-route-path.js";
11
13
  export { default as useSubmit } from "./hooks/use-submit.js";
12
14
  export { default as redirect } from "./utils/redirect.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soseki",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/tai-kun/soseki.js",
6
6
  "license": "MIT",
@@ -1,7 +1,6 @@
1
- import type { RouteDefinition } from "../core/route.types.js";
2
1
  import NavigationApiEngine from "../engines/navigation-api-engine.js";
3
2
  import useSingleton from "../hooks/_use-singleton.js";
4
- import Router from "./router.jsx";
3
+ import Router, { type RouterRouteDefinition } from "./router.jsx";
5
4
 
6
5
  /**
7
6
  * `BrowserRouter` コンポーネントに引き渡すプロパティーの型定義です。
@@ -10,7 +9,7 @@ export type BrowserRouterProps = {
10
9
  /**
11
10
  * アプリケーション全体の画面構造を定義したルート定義の配列です。
12
11
  */
13
- routes: readonly RouteDefinition[];
12
+ routes: readonly RouterRouteDefinition[];
14
13
  };
15
14
 
16
15
  /**
@@ -10,7 +10,7 @@ import processRoutes from "../core/_process-routes.js";
10
10
  import type { HistoryEntry } from "../core/expect-history-entry.js";
11
11
  import type { HistoryEntryId } from "../core/history-entry-id-schema.js";
12
12
  import type { MatchedRoute } from "../core/match-routes.js";
13
- import type { ActionFunction, LoaderFunction, RouteDefinition } from "../core/route.types.js";
13
+ import type { ActionFunction, LoaderFunction } from "../core/route.types.js";
14
14
  import type { IEngine, RouterState } from "../engines/engine.types.js";
15
15
 
16
16
  /**
@@ -77,6 +77,28 @@ function RouteRenderer(props: RouteRendererProps): React.ReactElement {
77
77
  return <ComponentRenderer route={route} outlet={outlet} />;
78
78
  }
79
79
 
80
+ export type RouterRouteDefinitionObject = {
81
+ readonly path: string;
82
+ readonly index?: boolean | undefined;
83
+ readonly action?: { (args: any): unknown } | undefined;
84
+ readonly shouldReload?: { (args: any): boolean } | undefined;
85
+ readonly loader?: { (args: any): unknown } | undefined;
86
+ readonly component?: React.ComponentType<{}> | undefined;
87
+ };
88
+
89
+ export type RouterRouteDefinitionModule = {
90
+ readonly path: string;
91
+ readonly index?: boolean | undefined;
92
+ readonly action?: { (args: any): unknown } | undefined;
93
+ readonly shouldReload?: { (args: any): boolean } | undefined;
94
+ readonly loader?: { (args: any): unknown } | undefined;
95
+ readonly component?: React.ComponentType<{}> | undefined;
96
+ readonly default?: React.ComponentType<{}> | undefined;
97
+ get [Symbol.toStringTag](): string;
98
+ };
99
+
100
+ export type RouterRouteDefinition = RouterRouteDefinitionObject | RouterRouteDefinitionModule;
101
+
80
102
  /**
81
103
  * `Router` コンポーネントに渡されるルートプロパティーの型定義です。
82
104
  */
@@ -89,7 +111,7 @@ export type RouterProps = {
89
111
  /**
90
112
  * ユーザーがアプリケーションに定義したルート定義の配列です。
91
113
  */
92
- routes: readonly RouteDefinition[];
114
+ routes: readonly RouterRouteDefinition[];
93
115
  };
94
116
 
95
117
  /**
@@ -0,0 +1,78 @@
1
+ import { parse } from "regexparam";
2
+
3
+ import RoutePath from "./route-path.js";
4
+
5
+ /**
6
+ * ルーティングの照合対象となる URL 情報を表すインターフェースです。
7
+ */
8
+ export interface MatchRoutePathTargetURL {
9
+ /**
10
+ * 照合対象のパス名です。
11
+ */
12
+ readonly pathname: string;
13
+ }
14
+
15
+ /**
16
+ * `matchRoutePath` 関数の動作を設定するための型定義です。
17
+ */
18
+ export type MatchRoutePathOptions = {
19
+ /**
20
+ * 照合の基準となるルーティングのパターン文字列です。
21
+ */
22
+ readonly pattern: string;
23
+
24
+ /**
25
+ * 照合対象とする文字列または URL オブジェクトです。
26
+ */
27
+ readonly target: string | MatchRoutePathTargetURL;
28
+
29
+ /**
30
+ * 子ディレクトリーへの前方一致を許可するかどうかを制御するフラグです。
31
+ *
32
+ * @default false
33
+ */
34
+ readonly allowChild?: boolean | undefined;
35
+ };
36
+
37
+ /**
38
+ * 指定されたパターンと対象のパス名が一致するかどうかを判定します。
39
+ *
40
+ * @param options パターン、対象、および一致条件を含む設定オブジェクトです。
41
+ * @returns パターンに一致した場合は `true` を、一致しない場合は `false` を返します。
42
+ */
43
+ function matchRoutePath(options: MatchRoutePathOptions): boolean;
44
+
45
+ /**
46
+ * 指定されたパターンと対象のパス名が一致するかどうかを判定します。
47
+ *
48
+ * @param pattern 照合の基準となるルーティングのパターン文字列です。
49
+ * @param target 照合対象とする文字列または URL オブジェクトです。
50
+ * @param options オプションです。
51
+ * @returns パターンに一致した場合は `true` を、一致しない場合は `false` を返します。
52
+ */
53
+ function matchRoutePath(
54
+ pattern: MatchRoutePathOptions["pattern"],
55
+ target: MatchRoutePathOptions["target"],
56
+ options?: Omit<MatchRoutePathOptions, "pattern" | "target">,
57
+ ): boolean;
58
+
59
+ function matchRoutePath(...args: any): boolean {
60
+ const {
61
+ target,
62
+ pattern,
63
+ allowChild = false,
64
+ }: MatchRoutePathOptions = typeof args[0] !== "string"
65
+ ? args[0]
66
+ : {
67
+ ...args[2],
68
+ target: args[1],
69
+ pattern: args[0],
70
+ };
71
+ const patternPathname = new RoutePath(pattern).pathname;
72
+ const targetPathname = typeof target === "string" ? target : target.pathname;
73
+ const patternRegex = parse(patternPathname, allowChild).pattern;
74
+
75
+ return patternRegex.test(targetPathname);
76
+ }
77
+
78
+ export default matchRoutePath;
@@ -1,4 +1,4 @@
1
- import type { ReadonlyURL } from "./readonly-url.types.js";
1
+ import type { ReadonlyURL, ReadonlyURLSearchParams } from "./readonly-url.types.js";
2
2
 
3
3
  /**
4
4
  * 連続する複数のスラッシュを検出するための正規表現です。
@@ -10,7 +10,50 @@ const MULTI_SLASH = /\/\/+/gu;
10
10
  *
11
11
  * 内包するホストやプロトコルといった余分な情報を排除し、パス、クエリー、ハッシュのみを一貫した規則で管理します。
12
12
  */
13
- export default class RoutePath {
13
+ export interface ReadonlyRoutePath {
14
+ /**
15
+ * 正規化されたパス部分の文字列です。
16
+ */
17
+ readonly pathname: string;
18
+
19
+ /**
20
+ * 先頭に `?` を含むクエリー文字列です。
21
+ *
22
+ * 常にソートされます。
23
+ */
24
+ readonly search: string;
25
+
26
+ /**
27
+ * 内部の `URL` インスタンスが保持する、クエリーパラメーターを操作するための `URLSearchParams` オブジェクトを取得します。
28
+ */
29
+ readonly searchParams: ReadonlyURLSearchParams;
30
+
31
+ /**
32
+ * 先頭に `#` を含むハッシュ文字列を取得します。
33
+ */
34
+ readonly hash: string;
35
+
36
+ /**
37
+ * 現在保持しているすべてのコンポーネントを結合し、ルーティング用のパス文字列として出力します。
38
+ *
39
+ * @returns ソート済みのクエリーおよびハッシュを含んだ、正規化されたパス全体の文字列を返します。
40
+ */
41
+ toString(): string;
42
+
43
+ /**
44
+ * このインスタンスを複製し、上書き可能な形式で取得します。
45
+ *
46
+ * @returns 上書き可能な {@link RoutePath|`RoutePath`} インスタンスです。
47
+ */
48
+ clone(): RoutePath;
49
+ }
50
+
51
+ /**
52
+ * アプリケーション内のルーティングにおけるパスを安全に構築・解析・操作するためのクラスです。
53
+ *
54
+ * 内包するホストやプロトコルといった余分な情報を排除し、パス、クエリー、ハッシュのみを一貫した規則で管理します。
55
+ */
56
+ export default class RoutePath implements ReadonlyRoutePath {
14
57
  /**
15
58
  * 与えられた文字列または URL オブジェクトから、正規化されたパス文字列を即座に生成する静的メソッドです。
16
59
  *
@@ -26,7 +69,7 @@ export default class RoutePath {
26
69
  *
27
70
  * 基底となる仮想的なオリジンと結合して管理されます。
28
71
  */
29
- private url: URL;
72
+ readonly #url: URL;
30
73
 
31
74
  /**
32
75
  * 新しい `RoutePath` インスタンスを初期化します。
@@ -44,12 +87,12 @@ export default class RoutePath {
44
87
  }
45
88
 
46
89
  // 組み込みの URL クラスによる厳密な解析機能を利用するため、仮想のプロトコルとホストを前置きして初期化します。
47
- this.url = new URL("x://y" + path);
90
+ this.#url = new URL("x://y" + path);
48
91
 
49
92
  // ルートパス(/)単体である場合を除き、末尾に存在する不要なスラッシュを削除して一貫性を保ちます。
50
- const { pathname } = this.url;
93
+ const { pathname } = this.#url;
51
94
  if (pathname !== "/" && pathname.endsWith("/")) {
52
- this.url.pathname = pathname.substring(0, pathname.length - 1);
95
+ this.#url.pathname = pathname.substring(0, pathname.length - 1);
53
96
  }
54
97
  }
55
98
 
@@ -57,16 +100,16 @@ export default class RoutePath {
57
100
  * 正規化されたパス部分の文字列です。
58
101
  */
59
102
  public get pathname(): string {
60
- return this.url.pathname;
103
+ return this.#url.pathname;
61
104
  }
62
105
 
63
106
  public set pathname(value: string) {
64
- this.url.pathname = value;
107
+ this.#url.pathname = value;
65
108
 
66
109
  // ルートパス(/)単体である場合を除き、末尾に存在する不要なスラッシュを削除して一貫性を保ちます。
67
- const pathname = ("/" + this.url.pathname).replace(MULTI_SLASH, "/");
110
+ const pathname = ("/" + this.#url.pathname).replace(MULTI_SLASH, "/");
68
111
  if (pathname !== "/" && pathname.endsWith("/")) {
69
- this.url.pathname = pathname.substring(0, pathname.length - 1);
112
+ this.#url.pathname = pathname.substring(0, pathname.length - 1);
70
113
  }
71
114
  }
72
115
 
@@ -76,30 +119,30 @@ export default class RoutePath {
76
119
  * 常にソートされます。
77
120
  */
78
121
  public get search(): string {
79
- this.url.searchParams.sort();
80
- return this.url.search;
122
+ this.#url.searchParams.sort();
123
+ return this.#url.search;
81
124
  }
82
125
 
83
126
  public set search(value: string) {
84
- this.url.search = value;
127
+ this.#url.search = value;
85
128
  }
86
129
 
87
130
  /**
88
131
  * 内部の `URL` インスタンスが保持する、クエリーパラメーターを操作するための `URLSearchParams` オブジェクトを取得します。
89
132
  */
90
133
  public get searchParams(): URLSearchParams {
91
- return this.url.searchParams;
134
+ return this.#url.searchParams;
92
135
  }
93
136
 
94
137
  /**
95
138
  * 先頭に `#` を含むハッシュ文字列を取得します。
96
139
  */
97
140
  public get hash(): string {
98
- return this.url.hash;
141
+ return this.#url.hash;
99
142
  }
100
143
 
101
144
  public set hash(value: string) {
102
- this.url.hash = value;
145
+ this.#url.hash = value;
103
146
  }
104
147
 
105
148
  /**
@@ -111,4 +154,13 @@ export default class RoutePath {
111
154
  const { hash, search, pathname } = this;
112
155
  return pathname + search + hash;
113
156
  }
157
+
158
+ /**
159
+ * このインスタンスを複製し、上書き可能な形式で取得します。
160
+ *
161
+ * @returns 上書き可能な {@link RoutePath|`RoutePath`} インスタンスです。
162
+ */
163
+ public clone(): RoutePath {
164
+ return new RoutePath(this.toString());
165
+ }
114
166
  }
package/src/core.ts CHANGED
@@ -34,3 +34,11 @@ export { default as startAction } from "./core/start-action.js";
34
34
 
35
35
  export type * from "./core/start-loaders.js";
36
36
  export { default as startLoaders } from "./core/start-loaders.js";
37
+
38
+ /**************************************************************************************************/
39
+
40
+ export type * from "./hooks/use-route-context.js";
41
+ export { default as useRouteContext } from "./hooks/use-route-context.js";
42
+
43
+ export type * from "./hooks/use-router-context.js";
44
+ export { default as useRouterContext } from "./hooks/use-router-context.js";
@@ -1,5 +1,6 @@
1
1
  import type { NinjaPromise } from "ninja-promise";
2
2
 
3
+ import type { RoutePath } from "../core.js";
3
4
  import type { HistoryEntry } from "../core/expect-history-entry.js";
4
5
  import type { HistoryEntryId } from "../core/history-entry-id-schema.js";
5
6
  import type { MatchedRoute } from "../core/match-routes.js";
@@ -178,7 +179,7 @@ export namespace IEngine {
178
179
  /**
179
180
  * URL パスで前方移動する形式です。
180
181
  */
181
- type: "PATH";
182
+ type: "STATIC";
182
183
 
183
184
  /**
184
185
  * URL パスです。
@@ -187,24 +188,16 @@ export namespace IEngine {
187
188
  }
188
189
  | {
189
190
  /**
190
- * URL の各コンポーネントを個別に指定する形式です。
191
+ * URL の各コンポーネントを関数形式で個別に指定する形式です。
191
192
  */
192
- type: "PARTIAL";
193
+ type: "DYNAMIC";
193
194
 
194
195
  /**
195
- * 最初のスラッシュ `/` から始まる URL のパス部分です。
196
+ * 動的にパッチを適用する関数です。
197
+ *
198
+ * @param path アプリケーション内のルーティングにおけるパスを安全に構築・解析・操作するためのオブジェクトです。
196
199
  */
197
- pathname?: string | undefined;
198
-
199
- /**
200
- * 先頭のクエスチョンマーク `?` を含む URL のクエリー文字列です。
201
- */
202
- search?: string | undefined;
203
-
204
- /**
205
- * URL のハッシュ(シャープ記号 `#` を含むフラグメント識別子)です。
206
- */
207
- hash?: string | undefined;
200
+ patch(path: RoutePath): void;
208
201
  };
209
202
 
210
203
  /**
@@ -105,8 +105,7 @@ export default class NavigationApiEngine implements IEngine {
105
105
  * ユーザーのアクションによって発生したすべての遷移要求をインターセプトして処理する、ルーティングの中枢ハンドラーです。
106
106
  */
107
107
  const handleNavigate = (event: NavigateEvent): void => {
108
- // 処理すべきでない通常のブラウザー固有のナビゲーション(ハッシュ変更、ファイルのダウンロードなど)は、
109
- // 標準の挙動を妨げないようにインターセプトせず即座にスルーします。
108
+ // 処理すべきでない通常のブラウザー固有のナビゲーション(ハッシュ変更、ファイルのダウンロードなど)は、標準の挙動を妨げないようにインターセプトせず即座にスルーします。
110
109
  // 参照: https://developer.mozilla.org/docs/Web/API/Navigation_API#handling_a_navigation_using_intercept
111
110
  if (
112
111
  !event.isTrusted ||
@@ -187,8 +186,7 @@ export default class NavigationApiEngine implements IEngine {
187
186
  switch (action.data.status) {
188
187
  case "rejected": {
189
188
  // アクションがエラーで失敗した場合は、URL を変更せず現在の元のページに強制リダイレクトさせます。
190
- const { pathname, search, hash } = currentEntry.url;
191
- controller.redirect(pathname + search + hash);
189
+ controller.redirect(RoutePath.encode(currentEntry.url));
192
190
 
193
191
  break;
194
192
  }
@@ -197,12 +195,12 @@ export default class NavigationApiEngine implements IEngine {
197
195
  // アクションが正常終了した場合、返り値にリダイレクト指示が含まれていればその目的地へ遷移させます。
198
196
  // リダイレクトがなければそのまま本来の目的地へとブラウザーのコミット先を書き換えます。
199
197
  const { redirectTo = currentEntry.url } = actionResponse;
200
- const { pathname, search, hash } = redirectTo;
201
- controller.redirect(pathname + search + hash);
198
+ const redirectPath = new RoutePath(redirectTo);
199
+ controller.redirect(redirectPath.toString());
202
200
 
203
- redirectUrl.pathname = pathname;
204
- redirectUrl.search = search;
205
- redirectUrl.hash = hash;
201
+ redirectUrl.pathname = redirectPath.pathname;
202
+ redirectUrl.search = redirectPath.search;
203
+ redirectUrl.hash = redirectPath.hash;
206
204
 
207
205
  break;
208
206
  }
@@ -435,7 +433,7 @@ export default class NavigationApiEngine implements IEngine {
435
433
  case "LINK": {
436
434
  const { to, history } = args;
437
435
  switch (to.type) {
438
- case "PATH": {
436
+ case "STATIC": {
439
437
  // 完全なパス文字列の余分なスラッシュなどをエンコードして直接遷移します。
440
438
 
441
439
  const path = RoutePath.encode(to.path);
@@ -444,25 +442,17 @@ export default class NavigationApiEngine implements IEngine {
444
442
  break;
445
443
  }
446
444
 
447
- case "PARTIAL": {
448
- // 現在のロケーション情報をベースに、指定されたパーツ(パス名、クエリー、ハッシュのみなど)を部分的にパッチ(上書き)したマージ URL を算出します。
445
+ case "DYNAMIC": {
446
+ // 現在のロケーション情報をベースに、指定されたパーツ(パス名、クエリー、ハッシュのみなど)を部分的に上書きしたマージ URL を算出します。
449
447
 
450
448
  const currentPath = new RoutePath(window.location);
451
- const nextPath = new RoutePath(window.location);
452
- if (typeof to.pathname === "string") {
453
- nextPath.pathname = to.pathname;
454
- }
455
- if (typeof to.search === "string") {
456
- nextPath.search = to.search;
457
- }
458
- if (typeof to.hash === "string") {
459
- nextPath.hash = to.hash;
460
- }
461
-
449
+ const currentPathString = currentPath.toString();
450
+ const nextPath = currentPath;
451
+ to.patch(nextPath);
462
452
  const nextPathString = nextPath.toString();
463
453
 
464
454
  // 無駄な遷移履歴を作らないように、URL に実際の変化がある場合のみ navigate を実行します。
465
- if (nextPathString !== currentPath.toString()) {
455
+ if (nextPathString !== currentPathString) {
466
456
  this.navigation.navigate(nextPathString, { history });
467
457
  }
468
458
 
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
 
3
- import type {} from "../engines/engine.types.js";
3
+ import type { RoutePath } from "../core.js";
4
4
  import useRouterContext from "./use-router-context.js";
5
5
 
6
6
  /**
@@ -25,6 +25,14 @@ export type NavigateTo =
25
25
  * 遷移先に付与するハッシュフラグメント(例: `"#profile"`)です。
26
26
  */
27
27
  readonly hash?: string | undefined;
28
+ }
29
+ | {
30
+ /**
31
+ * 動的にパッチを適用します。
32
+ *
33
+ * @param path アプリケーション内のルーティングにおけるパスを安全に構築・解析・操作するためのオブジェクトです。
34
+ */
35
+ (route: RoutePath): void;
28
36
  };
29
37
 
30
38
  /**
@@ -84,7 +92,26 @@ export default function useNavigate(): NavigateFunction {
84
92
  return routerNavigate({
85
93
  to: {
86
94
  path: to,
87
- type: "PATH",
95
+ type: "STATIC",
96
+ },
97
+ type: "LINK",
98
+ history,
99
+ });
100
+ } else if (typeof to === "object") {
101
+ return routerNavigate({
102
+ to: {
103
+ type: "DYNAMIC",
104
+ patch(path) {
105
+ if (typeof to.pathname === "string") {
106
+ path.pathname = to.pathname;
107
+ }
108
+ if (typeof to.search === "string") {
109
+ path.search = to.search;
110
+ }
111
+ if (typeof to.hash === "string") {
112
+ path.hash = to.hash;
113
+ }
114
+ },
88
115
  },
89
116
  type: "LINK",
90
117
  history,
@@ -92,8 +119,10 @@ export default function useNavigate(): NavigateFunction {
92
119
  } else {
93
120
  return routerNavigate({
94
121
  to: {
95
- ...to,
96
- type: "PARTIAL",
122
+ type: "DYNAMIC",
123
+ patch(path) {
124
+ to(path);
125
+ },
97
126
  },
98
127
  type: "LINK",
99
128
  history,
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+
3
+ import RoutePath, { type ReadonlyRoutePath } from "../core/route-path.js";
4
+ import useRouterContext from "./use-router-context.js";
5
+
6
+ export default function useRoutePath(): ReadonlyRoutePath {
7
+ const url = useRouterContext((router) => router.currentEntry.url);
8
+ const path = React.useMemo(() => new RoutePath(url), [url]);
9
+
10
+ return path;
11
+ }
package/src/soseki.ts CHANGED
@@ -30,6 +30,9 @@ export {
30
30
  NavigationApiNotSupportedError,
31
31
  } from "./core/errors.js";
32
32
 
33
+ export type * from "./core/match-route-path.js";
34
+ export { default as matchRoutePath } from "./core/match-route-path.js";
35
+
33
36
  export type * from "./core/readonly-form-data.types.js";
34
37
 
35
38
  export type * from "./core/readonly-url.types.js";
@@ -59,6 +62,9 @@ export { default as useNavigate } from "./hooks/use-navigate.js";
59
62
  export type * from "./hooks/use-params.js";
60
63
  export { default as useParams } from "./hooks/use-params.js";
61
64
 
65
+ export type * from "./hooks/use-route-path.js";
66
+ export { default as useRoutePath } from "./hooks/use-route-path.js";
67
+
62
68
  export type * from "./hooks/use-submit.js";
63
69
  export { default as useSubmit } from "./hooks/use-submit.js";
64
70