@wcstack/router 1.3.11

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 (118) hide show
  1. package/README.ja.md +488 -0
  2. package/README.md +486 -0
  3. package/dist/GuardCancel.d.ts +6 -0
  4. package/dist/GuardCancel.d.ts.map +1 -0
  5. package/dist/GuardCancel.js +8 -0
  6. package/dist/GuardCancel.js.map +1 -0
  7. package/dist/Navigation.d.ts +7 -0
  8. package/dist/Navigation.d.ts.map +1 -0
  9. package/dist/Navigation.js +11 -0
  10. package/dist/Navigation.js.map +1 -0
  11. package/dist/applyRoute.d.ts +3 -0
  12. package/dist/applyRoute.d.ts.map +1 -0
  13. package/dist/applyRoute.js +40 -0
  14. package/dist/applyRoute.js.map +1 -0
  15. package/dist/assignParams.d.ts +2 -0
  16. package/dist/assignParams.d.ts.map +1 -0
  17. package/dist/assignParams.js +52 -0
  18. package/dist/assignParams.js.map +1 -0
  19. package/dist/auto.js +3 -0
  20. package/dist/auto.min.js +3 -0
  21. package/dist/bootstrapRouter.d.ts +8 -0
  22. package/dist/bootstrapRouter.d.ts.map +1 -0
  23. package/dist/bootstrapRouter.js +14 -0
  24. package/dist/bootstrapRouter.js.map +1 -0
  25. package/dist/builtinParamTypes.d.ts +38 -0
  26. package/dist/builtinParamTypes.d.ts.map +1 -0
  27. package/dist/builtinParamTypes.js +79 -0
  28. package/dist/builtinParamTypes.js.map +1 -0
  29. package/dist/components/Head.d.ts +29 -0
  30. package/dist/components/Head.d.ts.map +1 -0
  31. package/dist/components/Head.js +173 -0
  32. package/dist/components/Head.js.map +1 -0
  33. package/dist/components/Layout.d.ts +15 -0
  34. package/dist/components/Layout.d.ts.map +1 -0
  35. package/dist/components/Layout.js +86 -0
  36. package/dist/components/Layout.js.map +1 -0
  37. package/dist/components/LayoutOutlet.d.ts +15 -0
  38. package/dist/components/LayoutOutlet.d.ts.map +1 -0
  39. package/dist/components/LayoutOutlet.js +101 -0
  40. package/dist/components/LayoutOutlet.js.map +1 -0
  41. package/dist/components/Link.d.ts +25 -0
  42. package/dist/components/Link.d.ts.map +1 -0
  43. package/dist/components/Link.js +155 -0
  44. package/dist/components/Link.js.map +1 -0
  45. package/dist/components/Outlet.d.ts +16 -0
  46. package/dist/components/Outlet.d.ts.map +1 -0
  47. package/dist/components/Outlet.js +46 -0
  48. package/dist/components/Outlet.js.map +1 -0
  49. package/dist/components/Route.d.ts +61 -0
  50. package/dist/components/Route.d.ts.map +1 -0
  51. package/dist/components/Route.js +348 -0
  52. package/dist/components/Route.js.map +1 -0
  53. package/dist/components/Router.d.ts +62 -0
  54. package/dist/components/Router.d.ts.map +1 -0
  55. package/dist/components/Router.js +237 -0
  56. package/dist/components/Router.js.map +1 -0
  57. package/dist/components/types.d.ts +86 -0
  58. package/dist/components/types.d.ts.map +1 -0
  59. package/dist/components/types.js +2 -0
  60. package/dist/components/types.js.map +1 -0
  61. package/dist/config.d.ts +5 -0
  62. package/dist/config.d.ts.map +1 -0
  63. package/dist/config.js +53 -0
  64. package/dist/config.js.map +1 -0
  65. package/dist/exports.d.ts +3 -0
  66. package/dist/exports.d.ts.map +1 -0
  67. package/dist/exports.js +2 -0
  68. package/dist/exports.js.map +1 -0
  69. package/dist/getCustomTagName.d.ts +2 -0
  70. package/dist/getCustomTagName.d.ts.map +1 -0
  71. package/dist/getCustomTagName.js +12 -0
  72. package/dist/getCustomTagName.js.map +1 -0
  73. package/dist/getUUID.d.ts +2 -0
  74. package/dist/getUUID.d.ts.map +1 -0
  75. package/dist/getUUID.js +11 -0
  76. package/dist/getUUID.js.map +1 -0
  77. package/dist/hideRoute.d.ts +3 -0
  78. package/dist/hideRoute.d.ts.map +1 -0
  79. package/dist/hideRoute.js +7 -0
  80. package/dist/hideRoute.js.map +1 -0
  81. package/dist/index.d.ts +24 -0
  82. package/dist/index.esm.js +1682 -0
  83. package/dist/index.esm.js.map +1 -0
  84. package/dist/index.esm.min.js +2 -0
  85. package/dist/index.esm.min.js.map +1 -0
  86. package/dist/matchRoutes.d.ts +3 -0
  87. package/dist/matchRoutes.d.ts.map +1 -0
  88. package/dist/matchRoutes.js +52 -0
  89. package/dist/matchRoutes.js.map +1 -0
  90. package/dist/parse.d.ts +3 -0
  91. package/dist/parse.d.ts.map +1 -0
  92. package/dist/parse.js +77 -0
  93. package/dist/parse.js.map +1 -0
  94. package/dist/raiseError.d.ts +2 -0
  95. package/dist/raiseError.d.ts.map +1 -0
  96. package/dist/raiseError.js +4 -0
  97. package/dist/raiseError.js.map +1 -0
  98. package/dist/registerComponents.d.ts +2 -0
  99. package/dist/registerComponents.d.ts.map +1 -0
  100. package/dist/registerComponents.js +33 -0
  101. package/dist/registerComponents.js.map +1 -0
  102. package/dist/showRoute.d.ts +3 -0
  103. package/dist/showRoute.d.ts.map +1 -0
  104. package/dist/showRoute.js +38 -0
  105. package/dist/showRoute.js.map +1 -0
  106. package/dist/showRouteContent.d.ts +3 -0
  107. package/dist/showRouteContent.d.ts.map +1 -0
  108. package/dist/showRouteContent.js +38 -0
  109. package/dist/showRouteContent.js.map +1 -0
  110. package/dist/testPath.d.ts +3 -0
  111. package/dist/testPath.d.ts.map +1 -0
  112. package/dist/testPath.js +85 -0
  113. package/dist/testPath.js.map +1 -0
  114. package/dist/types.d.ts +38 -0
  115. package/dist/types.d.ts.map +1 -0
  116. package/dist/types.js +2 -0
  117. package/dist/types.js.map +1 -0
  118. package/package.json +74 -0
package/README.ja.md ADDED
@@ -0,0 +1,488 @@
1
+ # @wcstack/router
2
+
3
+ カスタム要素を使って宣言的に定義し、SPAのルーティングを行います
4
+
5
+ ## 特徴
6
+
7
+ ### 基本機能
8
+ * **宣言的ルーティング**: HTMLの `<template>` 内に `<wcs-route>` タグを並べるだけで定義完了。JS設定オブジェクト不要。
9
+ * **階層化されたルート定義**: ネスト構造で `/products/:id` などを直感的に表現。
10
+ * **パラメータサポート**: パスパラメータ(`:id`)対応。
11
+ * **フォールバック (404)**: `<wcs-route fallback>` で未定義パスをハンドリング。
12
+ * **Navigation API ベース**: モダンな標準規格 Navigation API を採用し、ブラウザネイティブの挙動と親和性が高い。
13
+ * **ゼロコンフィグ / ビルドレス**: バンドル不要でブラウザでそのまま動作。
14
+
15
+ ### ユニークな機能
16
+ * **Light DOM レイアウトシステム**: Shadow DOM を強制せず、通常のDOM(Light DOM)上でレイアウトテンプレートを展開。グローバルCSSが適用しやすく、`<slot>` 差し込みも容易。
17
+ * **型付きパラメータ (`Typed Parameters`)**: `:id(int)` のように型制約を指定可能。自動的に `number` 型への変換も行います。
18
+ * **レイアウトとルートの混在定義**: ルーティングツリー内に `<wcs-layout>` を自由にネストでき、エリアごとのレイアウト切り替えがHTML構造だけで完結。
19
+ * **自動バインディング**: `data-bind` 属性でURLパラメータをコンポーネントに自動注入(`props`, `states`, `attr`, 直接プロパティの4モード対応)。
20
+ * **宣言的な `<head>` 管理**: `<wcs-head>` でページごとの `title` や `meta` を宣言的に切り替え。
21
+
22
+ ## 使い方
23
+
24
+ ```html
25
+ <wcs-router>
26
+ <template>
27
+ <!-- pathが"/"の場合 -->
28
+ <wcs-route path="/">
29
+ <!-- "main-layout"レイアウトを適用 -->
30
+ <wcs-layout layout="main-layout">
31
+ <main-header slot="header"></main-header>
32
+ <main-body>
33
+ <!-- pathが"/"の場合 -->
34
+ <wcs-route index>
35
+ <wcs-head>
36
+ <title>Main Page</title>
37
+ </wcs-head>
38
+ <main-dashboard></main-dashboard>
39
+ </wcs-route>
40
+
41
+ <!-- pathが"/products"の場合、トップレベル以外は相対パス -->
42
+ <wcs-route path="products">
43
+ <wcs-head>
44
+ <title>Product Page</title>
45
+ </wcs-head>
46
+ <!-- pathが"/products"の場合 -->
47
+ <wcs-route index>
48
+ <product-list></product-list>
49
+ </wcs-route>
50
+ <!-- pathが"/products/:productId"の場合 -->
51
+ <wcs-route path=":productId">
52
+ <!-- productItem.props.productId = productId -->
53
+ <product-item data-bind="props"></product-item>
54
+ </wcs-route>
55
+ </wcs-route>
56
+ </main-body>
57
+ </wcs-layout>
58
+ </wcs-route>
59
+
60
+ <!-- pathが"/admin"の場合 -->
61
+ <wcs-route path="/admin">
62
+ <!-- "admin-layout"レイアウトを適用 -->
63
+ <wcs-layout layout="admin-layout">
64
+ <wcs-head>
65
+ <title>Admin Page</title>
66
+ </wcs-head>
67
+ <admin-header slot="header"></admin-header>
68
+ <admin-body></admin-body>
69
+ </wcs-layout>
70
+ </wcs-route>
71
+
72
+ <!-- pathが一致しない場合 -->
73
+ <wcs-route fallback>
74
+ <error-404></error-404>
75
+ </wcs-route>
76
+ </template>
77
+ </wcs-router>
78
+
79
+ <wcs-outlet>
80
+ <!-- ルートパス・レイアウトに従ったDOMツリーを作成し、ここに表示 -->
81
+ </wcs-outlet>
82
+
83
+ <!-- "main-layout"レイアウト -->
84
+ <template id="main-layout">
85
+ <section>
86
+ <h1> Main </h1>
87
+ <slot name="header"></slot>
88
+ </section>
89
+ <section>
90
+ <slot></slot>
91
+ </section>
92
+ </template>
93
+
94
+ <!-- "admin-layout"レイアウト -->
95
+ <template id="admin-layout">
96
+ <section>
97
+ <h1> Admin Main </h1>
98
+ <slot name="header"></slot>
99
+ </section>
100
+ <section>
101
+ <slot></slot>
102
+ </section>
103
+ </template>
104
+
105
+ ```
106
+
107
+ ※<main-header><main-body><main-dashboard><product-list><product-item><admin-header><admin-body><error-404>はアプリ側のカスタムコンポーネント
108
+ ※上記カスタム要素は、オートローダーやコードによる定義が別途必要
109
+
110
+ ## リファレンス
111
+
112
+ ### Router(wcs-router)
113
+
114
+ 子要素のtemplateタグ内にルーティング・レイアウトスロット定義する。ドキュメント内で1つのみ存在可能。直下にtemplateタグが必要。定義に従って、`<wcs-outlet>`へ出力する
115
+
116
+ | 属性 | 説明 |
117
+ |------|------|
118
+ | `basename` | サブフォルダのURLでルーティングする場合に、サブフォルダを指定。サブフォルダで動作させない場合は、指定不要 |
119
+
120
+ ### Route(wcs-route)
121
+
122
+ ルートパスが一致する場合、子要素を表示。パスの一致の優先順位は静的パス>パラメータ。
123
+
124
+ | 属性 | 説明 |
125
+ |------|------|
126
+ | `path` | トップレベルルートの場合、"/"で始まる絶対パスを指定、それ以外は相対パスを指定。パラメータを指定する場合、`:パラメータ名`。キャッチオールは`*`。トップレベルルートでは相対パスを指定できない。 |
127
+ | `index` | 上位のパスを引き継ぐ |
128
+ | `fallback` | ルートパスに対応するルートがない場合、表示する |
129
+ | `fullpath` | 上位ルートを含むパス、読み取り専用 |
130
+ | `name` | 識別用 |
131
+ | `guard` | ガード処理を実施。値にはガードキャンセル時の絶対ルートパスを指定 |
132
+
133
+ | プロパティ | 説明 |
134
+ |------|------|
135
+ | `params` | マッチしたパラメータ(文字列)を取得 |
136
+ | `typedParams` | マッチしたパラメータ(型変換済み)を取得 |
137
+ | `guardHandler` | ガード判定関数を設定 |
138
+
139
+ ガード判定関数の型:
140
+ function (toPath: string, fromPath: string): boolean | Promise<boolean>
141
+
142
+ #### 型付きパラメータ
143
+
144
+ パスパラメータに型を指定することで、値の検証と自動変換が行えます。
145
+
146
+ **構文**: `:パラメータ名(型名)`
147
+
148
+ ```html
149
+ <!-- 数値型パラメータ -->
150
+ <wcs-route path="/users/:userId(int)">
151
+ <user-detail></user-detail>
152
+ </wcs-route>
153
+
154
+ <!-- 複合型パラメータ -->
155
+ <wcs-route path="/posts/:date(isoDate)/:slug(slug)">
156
+ <post-detail></post-detail>
157
+ </wcs-route>
158
+ ```
159
+
160
+ **ビルトイン型**:
161
+
162
+ | 型名 | 説明 | 例 | 変換後の型 |
163
+ |------|------|------|------|
164
+ | `int` | 整数 | `123`, `-45` | `number` |
165
+ | `float` | 浮動小数点数 | `3.14`, `-2.5` | `number` |
166
+ | `bool` | 真偽値 | `true`, `false`, `0`, `1` | `boolean` |
167
+ | `uuid` | UUID v1-5 | `550e8400-e29b-41d4-a716-446655440000` | `string` |
168
+ | `slug` | スラッグ(小文字英数字とハイフン) | `my-post-title` | `string` |
169
+ | `isoDate` | ISO 8601 日付 | `2024-01-23` | `Date` |
170
+ | `any` | 任意の文字列(デフォルト) | 任意 | `string` |
171
+
172
+ **値の取得**:
173
+
174
+ ```javascript
175
+ // ルート要素から取得
176
+ const route = document.querySelector('wcs-route[path="/users/:userId(int)"]');
177
+
178
+ // 文字列として取得
179
+ console.log(route.params.userId); // "123"
180
+
181
+ // 型変換済みの値として取得
182
+ console.log(route.typedParams.userId); // 123 (number)
183
+ ```
184
+
185
+ **動作仕様**:
186
+ - 型に一致しない値の場合、そのルートはマッチしません(エラーにはなりません)
187
+ - 型を指定しない場合は `any` として扱われます(従来の動作と同じ)
188
+ - 未知の型名を指定した場合も `any` にフォールバックします
189
+
190
+ ### Layout(wcs-layout)
191
+
192
+ テンプレートを読み込み、子要素を`<slot>`へ挿入して`<wcs-layout-outlet>`へ書き出す。Light DOM対応。外部ファイル対応。
193
+
194
+ | 属性 | 説明 |
195
+ |------|------|
196
+ | `layout` | テンプレートとなる`<template>`タグのid属性 |
197
+ | `src` | 外部ファイルテンプレートのURL |
198
+ | `name` | 識別名、`wcs-layout-outlet`へ引き継がれる |
199
+ | `enable-shadow-root` | `<wcs-layout-outlet>`でShadow DOMを使用 |
200
+ | `disable-shadow-root` | `<wcs-layout-outlet>`でLight DOMを使用 |
201
+
202
+ ### Outlet(wcs-outlet)
203
+
204
+ ルーティング・レイアウト設定に従いDOMツリーを表示する。HTML内に定義するか、ない場合は`<wcs-router>`により作成される。
205
+
206
+ ### LayoutOutlet(wcs-layout-outlet)
207
+
208
+ レイアウト(`<wcs-layout>`)設定に従いDOMツリーを`<wcs-outlet>`へ表示する。`<wcs-layout>`の名前属性を引き継ぐ。スタイリングの設定時、name属性で識別する。
209
+
210
+ | 属性 | 説明 |
211
+ |------|------|
212
+ | `name` | `<wcs-layout>`の名前属性。スタイリングの設定時、name属性で識別する。 |
213
+
214
+ #### Light DOMの制限事項
215
+
216
+ `disable-shadow-root`(Light DOM)の場合、スロット置換は`<wcs-layout>`の**直接の子要素のみ**が対象です。`<wcs-route>`の中にある`slot`属性付き要素はスロットに配置されません。
217
+
218
+ ```html
219
+ <!-- NG: <div slot="header">はwcs-layoutの直接の子ではないため、スロットに入らない -->
220
+ <wcs-layout layout="main" disable-shadow-root>
221
+ <wcs-route path="/page">
222
+ <div slot="header">Header Content</div>
223
+ </wcs-route>
224
+ </wcs-layout>
225
+
226
+ <!-- OK: slot属性付き要素をwcs-layoutの直接の子にする -->
227
+ <wcs-layout layout="main" disable-shadow-root>
228
+ <div slot="header">Header Content</div>
229
+ <wcs-route path="/page">
230
+ <!-- ページ本体 -->
231
+ </wcs-route>
232
+ </wcs-layout>
233
+ ```
234
+
235
+ `enable-shadow-root`(Shadow DOM)の場合は、ネイティブの`<slot>`機能が使われるため、この制限はありません。
236
+
237
+ ### Head(wcs-head)
238
+
239
+ ルートごとにドキュメントの `<head>` 要素を管理する。スタックベースで、最後に接続された Head が優先される。
240
+
241
+ ```html
242
+ <wcs-route path="/about">
243
+ <wcs-head>
244
+ <title>About Us</title>
245
+ <meta name="description" content="About our company">
246
+ </wcs-head>
247
+ <about-page></about-page>
248
+ </wcs-route>
249
+ ```
250
+
251
+ **対応要素**: `<title>`, `<meta>`, `<link>`, `<base>`, `<script>`, `<style>`
252
+
253
+ **動作**:
254
+ - 初回接続時に初期の `<head>` 状態をキャプチャ
255
+ - 複数の `<wcs-head>` が同時にアクティブな場合、最後に接続されたものが優先(上書き)
256
+ - 全ての `<wcs-head>` が切断されると、初期状態に復元
257
+ - 要素はキーで識別(例: `<meta>` は `name`/`property`/`http-equiv`、`<link>` は `rel`/`href`)
258
+
259
+ ### Link(wcs-link)
260
+
261
+ リンク。`<a>`へ変換され、to属性で指定されたパスはURLへ変換される。リンクのパスが現在のURLと一致する場合、生成された `<a>` 要素に `active` CSSクラスが自動付与される。
262
+
263
+ | 属性 | 説明 |
264
+ |------|------|
265
+ | `to` | 遷移先の絶対ルートパスもしくはURL。`/`で始まる場合はルートパス(basenameが付与される)。それ以外は外部URLとして扱われる |
266
+
267
+ **アクティブ状態**: 生成された `<a>` はパスが現在のロケーションと一致する場合に `active` クラスを受け取る。ナビゲーションイベント(`currententrychange`, `wcs:navigate`, `popstate`)で更新される。
268
+
269
+ ```css
270
+ /* アクティブなリンクのスタイル */
271
+ a.active { font-weight: bold; color: blue; }
272
+ ```
273
+
274
+ ## 自動バインディング (`data-bind`)
275
+
276
+ `data-bind` 属性を持つ要素は、マッチしたルートパラメータを自動的に受け取る。4つのバインディングモードに対応:
277
+
278
+ | `data-bind` の値 | ターゲット | 説明 |
279
+ |------|------|------|
280
+ | `"props"` | `element.props` | `props` プロパティにパラメータをマージ |
281
+ | `"states"` | `element.states` | `states` プロパティにパラメータをマージ |
282
+ | `"attr"` | HTML属性 | `setAttribute()` でパラメータをHTML属性に設定 |
283
+ | `""` (空文字) | 直接プロパティ | 要素に直接プロパティを設定(例: `element.id = value`) |
284
+
285
+ ```html
286
+ <wcs-route path="/users/:userId(int)">
287
+ <!-- element.props = { userId: 123 } -->
288
+ <user-detail data-bind="props"></user-detail>
289
+
290
+ <!-- element.setAttribute("userId", 123) -->
291
+ <div data-bind="attr"></div>
292
+ </wcs-route>
293
+ ```
294
+
295
+ パラメータは `connectedCallback` の発火前に割り当てられる。未定義のカスタム要素の場合、`customElements.whenDefined()` の解決後に割り当てが遅延される。
296
+
297
+ ## 設定
298
+
299
+ `bootstrapRouter()` でオプション設定を指定して初期化:
300
+
301
+ ```javascript
302
+ import { bootstrapRouter } from '@wcstack/router';
303
+
304
+ bootstrapRouter({
305
+ // カスタムタグ名(すべてオプション)
306
+ tagNames: {
307
+ router: 'wcs-router', // デフォルト
308
+ route: 'wcs-route', // デフォルト
309
+ outlet: 'wcs-outlet', // デフォルト
310
+ layout: 'wcs-layout', // デフォルト
311
+ layoutOutlet: 'wcs-layout-outlet', // デフォルト
312
+ link: 'wcs-link', // デフォルト
313
+ head: 'wcs-head' // デフォルト
314
+ },
315
+ // outlet で Shadow DOM を使用(デフォルト: false)
316
+ enableShadowRoot: false,
317
+ // basename から除去するファイル拡張子(デフォルト: [".html"])
318
+ basenameFileExtensions: [".html"]
319
+ });
320
+ ```
321
+
322
+ ## パス仕様案(Router / Route / Link 共通)
323
+
324
+ ### 用語
325
+
326
+ * **URL Pathname**: `location.pathname`(例: `/app/products/42`)
327
+ * **basename**: アプリの“マウント先”のパス(例: `/app`)
328
+ * **internalPath**: basename を除いたアプリ内ルーティング用パス(例: `/products/42`)
329
+
330
+ ---
331
+
332
+ ## 1) basename の仕様
333
+
334
+ ### 1.1 basename の決定順
335
+
336
+ 1. `<wcs-router basename="/app">` の `basename` 属性
337
+ 2. `<base href="/app/">` がある場合は `new URL(document.baseURI).pathname` から導出
338
+ 3. どちらも無い場合は **空文字** `""`(= ルート直下で動く想定)
339
+
340
+ ### 1.2 basename の正規化(重要)
341
+
342
+ basename は **必ず次に正規化**する:
343
+
344
+ * 先頭 `/` を付ける(空はそのまま)
345
+ * 連続スラッシュを 1つに畳む
346
+ * 末尾の `/` は削除(ただし `/` そのものは空 `""` と等価扱い)
347
+ * `.../index.html` や `.../*.html` はファイルとみなし削除
348
+ * 結果が `/` になったら basename は `""` とする
349
+
350
+ 例:
351
+
352
+ * `"/"` → `""`
353
+ * `"/app/"` → `"/app"`
354
+ * `"/app/index.html"` → `"/app"`
355
+
356
+ ### 1.3 basename と直リンクの整合性
357
+
358
+ * basename が `""` で `<base>` も無いのに、初期表示の `pathname !== "/"` の場合は **エラー**(現行思想を踏襲)
359
+ * basename が `"/app"` の場合:
360
+
361
+ * `"/app"` と `"/app/"` は **同じ意味**(アプリの root)
362
+ * `"/app"` は `"/app"` または `"/app/..."` にのみ一致(`"/appX"` には一致しない)
363
+
364
+ ---
365
+
366
+ ## 2) internalPath の仕様
367
+
368
+ ### 2.1 internalPath の正規化
369
+
370
+ internalPath は常に **絶対パス形式**で扱う。
371
+
372
+ * 先頭 `/` を付ける
373
+ * 連続スラッシュを畳む
374
+ * 末尾 `/` は削除(ただし root `/` は保持)
375
+ * 空になったら `/`
376
+ * Router が扱う internalPath の正規化では末尾が `*.html` の場合は削除
377
+
378
+ 例:
379
+
380
+ * `""` → `/`
381
+ * `"products"` → `/products`
382
+ * `"/products/"` → `/products`
383
+ * `"///a//b/"` → `/a/b`
384
+
385
+ ### 2.2 URLから internalPath を得る
386
+
387
+ `URL Pathname` を `basename` と突き合わせて得る。
388
+
389
+ * `pathname === basename` なら `internalPath = "/"`
390
+ * `pathname` が `basename + "/"` で始まるなら `internalPath = pathname.slice(basename.length)`
391
+ * それ以外は `internalPath = pathname`
392
+ * slice 結果が `""` なら `internalPath = "/"`
393
+
394
+ 例:basename=`/app`
395
+
396
+ * pathname=`/app` → internalPath=`/`
397
+ * pathname=`/app/` → internalPath=`/`
398
+ * pathname=`/app/products/42` → internalPath=`/products/42`
399
+
400
+ ---
401
+
402
+ ## 3) `<wcs-route path="...">` の仕様
403
+
404
+ ### 3.1 path の書き方
405
+
406
+ `<wcs-route path="...">` の `path` は **internalPath のルールに従う**。
407
+
408
+ * ルート(トップ)は `"/"`
409
+ * 子routeは **相対**を許可する(推奨は相対)
410
+
411
+ * 例: 親が `/products`、子が `":id"` → `/products/:id`
412
+
413
+ > ただし実装側では、解析時に「絶対化」して持つ方が事故が少ないです(相対のまま保持しない)。
414
+
415
+ ### 3.2 マッチング規則
416
+
417
+ * **完全一致**(セグメント単位)
418
+ * パラメータ `:id` は1セグメントにマッチ
419
+ * キャッチオール `*` は残りのパス全体にマッチ(`params['*']` で取得可能)
420
+
421
+ ### 3.3 優先順位(最長マッチの定義)
422
+
423
+ 候補が複数ある場合、次の順で高いものを採用:
424
+
425
+ 1. **セグメント数が多い**
426
+ 2. 同セグメント数なら **静的セグメントが多い**(`"users"` > `":id"` > `"*"`)
427
+ 3. それでも同じなら **定義順**
428
+
429
+ > キャッチオール `*` は優先度が最も低いため、より具体的なルートが常に優先されます。
430
+
431
+ 例:
432
+
433
+ * `/admin/users/:id`(静的2 + param1)
434
+ * `/admin/users/profile`(静的3)
435
+ → 後者が勝つ
436
+
437
+ ### 3.4 トレーリングスラッシュ
438
+
439
+ * マッチングは **内部正規化後**に行うため、
440
+
441
+ * `/products` と `/products/` は同一扱い(URL表現はどちらでもOK)
442
+
443
+ ### 3.5 キャッチオール(`*`)
444
+
445
+ パス末尾に `*` を指定すると、残りのパス全体にマッチします。
446
+
447
+ ```html
448
+ <wcs-route path="/admin/profile"></wcs-route> <!-- 優先 -->
449
+ <wcs-route path="/admin/*"></wcs-route> <!-- /admin/xxx でフォールバック -->
450
+ <wcs-route path="/*"></wcs-route> <!-- 最後の砦 -->
451
+ ```
452
+
453
+ | パス | マッチ | 理由 |
454
+ |------|--------|------|
455
+ | `/admin/profile` | `/admin/profile` | セグメント数多い |
456
+ | `/admin/setting` | `/admin/*` | `*` が `setting` にマッチ |
457
+ | `/admin/a/b/c` | `/admin/*` | `*` が `a/b/c` にマッチ |
458
+ | `/other` | `/*` | トップレベルcatch-all |
459
+
460
+ マッチした残りパスは `params['*']` で取得できます。
461
+
462
+ ---
463
+
464
+ ## 4) `<wcs-link to="...">` の仕様
465
+
466
+ ### 4.1 to が `/` で始まる場合
467
+
468
+ `to` は **internalPath** とみなす。
469
+
470
+ * 実際の `href` は `basename + internalPath` を join して生成
471
+ * join は `"/app" + "/products"` → `"/app/products"`(`//`を作らない)
472
+
473
+ ### 4.2 to が `/` で始まらない場合
474
+
475
+ 外部リンクとして扱う(`new URL(to)` が成立する想定)。
476
+
477
+ * 例: `https://example.com/`
478
+
479
+ ---
480
+
481
+ ## 5) “HTMLファイルを落とす” ルールは限定的に
482
+
483
+ `.html` を落とすのは **pathname の末尾が本当にファイルっぽい場合だけ**。
484
+
485
+ * `"/app/index.html"` → `"/app"`(OK)
486
+ * `"/products"` を `"/"` にするのは **NG**(セグメントを落とさない)
487
+
488
+