@real-router/hash-plugin 0.2.1 → 0.2.3
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/README.md +56 -137
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/esm/index.d.mts +3 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
# @real-router/hash-plugin
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@real-router/hash-plugin)
|
|
4
|
+
[](https://www.npmjs.com/package/@real-router/hash-plugin)
|
|
5
|
+
[](https://bundlejs.com/?q=@real-router/hash-plugin&treeshake=[*])
|
|
6
|
+
[](../../LICENSE)
|
|
5
7
|
|
|
6
|
-
Hash-based routing plugin for Real-Router. Uses URL hash fragment for navigation — no server configuration needed.
|
|
8
|
+
> Hash-based routing plugin for [Real-Router](https://github.com/greydragon888/real-router). Uses URL hash fragment (`#/path`) for navigation — no server configuration needed.
|
|
9
|
+
|
|
10
|
+
Works on static hosting (GitHub Pages, S3, Netlify) without redirect rules. Tradeoff: URLs include `#` (`example.com/#!/users` vs `example.com/users`).
|
|
11
|
+
|
|
12
|
+
> **Looking for clean URLs?** Use [`@real-router/browser-plugin`](https://www.npmjs.com/package/@real-router/browser-plugin) (History API).
|
|
7
13
|
|
|
8
14
|
## Installation
|
|
9
15
|
|
|
10
16
|
```bash
|
|
11
17
|
npm install @real-router/hash-plugin
|
|
12
|
-
# or
|
|
13
|
-
pnpm add @real-router/hash-plugin
|
|
14
|
-
# or
|
|
15
|
-
yarn add @real-router/hash-plugin
|
|
16
|
-
# or
|
|
17
|
-
bun add @real-router/hash-plugin
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
**Peer dependency:** `@real-router/core`
|
|
21
|
+
|
|
20
22
|
## Quick Start
|
|
21
23
|
|
|
22
24
|
```typescript
|
|
@@ -25,186 +27,103 @@ import { hashPluginFactory } from "@real-router/hash-plugin";
|
|
|
25
27
|
|
|
26
28
|
const router = createRouter([
|
|
27
29
|
{ name: "home", path: "/" },
|
|
28
|
-
{ name: "
|
|
29
|
-
{ name: "cart", path: "/cart" },
|
|
30
|
+
{ name: "users", path: "/users/:id" },
|
|
30
31
|
]);
|
|
31
32
|
|
|
32
|
-
// Basic usage
|
|
33
33
|
router.usePlugin(hashPluginFactory());
|
|
34
|
-
|
|
35
|
-
// With options
|
|
36
|
-
router.usePlugin(
|
|
37
|
-
hashPluginFactory({
|
|
38
|
-
hashPrefix: "!",
|
|
39
|
-
}),
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
await router.start();
|
|
34
|
+
await router.start(); // reads hash from browser location
|
|
43
35
|
```
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
## Configuration
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
router.usePlugin(
|
|
51
|
-
hashPluginFactory({
|
|
52
|
-
hashPrefix: "!",
|
|
53
|
-
forceDeactivate: true,
|
|
54
|
-
}),
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
router.navigate("products", { id: "123" });
|
|
58
|
-
// URL: http://example.com/#!/products/123
|
|
59
|
-
```
|
|
37
|
+
## Options
|
|
60
38
|
|
|
61
39
|
| Option | Type | Default | Description |
|
|
62
40
|
| ----------------- | --------- | ------- | ----------------------------------------------------- |
|
|
63
41
|
| `hashPrefix` | `string` | `""` | Prefix after `#` (e.g., `"!"` → `#!/path`) |
|
|
64
42
|
| `base` | `string` | `""` | Base path before hash (e.g., `"/app"` → `/app#/path`) |
|
|
65
|
-
| `forceDeactivate` | `boolean` | `true` | Bypass `canDeactivate` guards on
|
|
66
|
-
|
|
67
|
-
> **Looking for History API routing?** Use [`@real-router/browser-plugin`](https://www.npmjs.com/package/@real-router/browser-plugin) instead.
|
|
43
|
+
| `forceDeactivate` | `boolean` | `true` | Bypass `canDeactivate` guards on back/forward |
|
|
68
44
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
---
|
|
45
|
+
```typescript
|
|
46
|
+
router.usePlugin(hashPluginFactory({ hashPrefix: "!", base: "/app" }));
|
|
72
47
|
|
|
73
|
-
|
|
48
|
+
router.navigate("users", { id: "123" });
|
|
49
|
+
// URL: /app#!/users/123
|
|
50
|
+
```
|
|
74
51
|
|
|
75
|
-
|
|
52
|
+
## Router Extensions
|
|
76
53
|
|
|
77
|
-
|
|
54
|
+
The plugin extends the router instance with three methods via [`extendRouter()`](https://github.com/greydragon888/real-router/wiki/plugin-architecture):
|
|
78
55
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
`params
|
|
82
|
-
|
|
83
|
-
|
|
56
|
+
| Method | Returns | Description |
|
|
57
|
+
| -------------------------------------------- | -------------------- | ------------------------------------- |
|
|
58
|
+
| `buildUrl(name, params?)` | `string` | Build full URL with hash and prefix |
|
|
59
|
+
| `matchUrl(url)` | `State \| undefined` | Parse hash URL to router state |
|
|
60
|
+
| `replaceHistoryState(name, params?, title?)` | `void` | Update browser URL without navigation |
|
|
84
61
|
|
|
85
62
|
```typescript
|
|
86
63
|
router.buildUrl("users", { id: "123" });
|
|
87
64
|
// => "#!/users/123" (with hashPrefix "!")
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
#### `router.matchUrl(url: string): State | undefined`
|
|
91
|
-
|
|
92
|
-
Parse URL to router state.\
|
|
93
|
-
`url: string` — URL to parse\
|
|
94
|
-
Returns: `State | undefined`\
|
|
95
|
-
[Wiki](https://github.com/greydragon888/real-router/wiki/hash-plugin#5-router-interaction)
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
const state = router.matchUrl("https://example.com/#!/users/123");
|
|
99
|
-
// => { name: "users", params: { id: "123" }, ... }
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
#### `router.replaceHistoryState(name: string, params?: Params, title?: string): void`
|
|
103
65
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
`params?: Params` — route parameters\
|
|
107
|
-
`title?: string` — page title\
|
|
108
|
-
Returns: `void`\
|
|
109
|
-
[Wiki](https://github.com/greydragon888/real-router/wiki/hash-plugin#5-router-interaction)
|
|
66
|
+
router.matchUrl("https://example.com/#!/users/123");
|
|
67
|
+
// => { name: "users", params: { id: "123" }, path: "/users/123" }
|
|
110
68
|
|
|
111
|
-
|
|
69
|
+
// Update URL silently (no transition, no guards)
|
|
112
70
|
router.replaceHistoryState("users", { id: "456" });
|
|
113
71
|
```
|
|
114
72
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
## Usage Examples
|
|
118
|
-
|
|
119
|
-
### Hashbang Routing
|
|
73
|
+
### `buildUrl` vs `buildPath`
|
|
120
74
|
|
|
121
75
|
```typescript
|
|
122
|
-
router.
|
|
123
|
-
|
|
124
|
-
hashPrefix: "!",
|
|
125
|
-
}),
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
router.navigate("users", { id: "123" });
|
|
129
|
-
// URL: #!/users/123
|
|
76
|
+
router.buildPath("users", { id: 1 }); // "/users/1" — core, no hash
|
|
77
|
+
router.buildUrl("users", { id: 1 }); // "#!/users/1" — plugin, with hash prefix
|
|
130
78
|
```
|
|
131
79
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
router.usePlugin(
|
|
136
|
-
hashPluginFactory({
|
|
137
|
-
hashPrefix: "!",
|
|
138
|
-
base: "/app",
|
|
139
|
-
}),
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
router.navigate("users", { id: "123" });
|
|
143
|
-
// URL: /app#!/users/123
|
|
144
|
-
```
|
|
80
|
+
## Form Protection
|
|
145
81
|
|
|
146
|
-
|
|
82
|
+
Set `forceDeactivate: false` to respect `canDeactivate` guards on back/forward:
|
|
147
83
|
|
|
148
84
|
```typescript
|
|
149
|
-
router.usePlugin(
|
|
150
|
-
hashPluginFactory({
|
|
151
|
-
forceDeactivate: false,
|
|
152
|
-
}),
|
|
153
|
-
);
|
|
85
|
+
router.usePlugin(hashPluginFactory({ forceDeactivate: false }));
|
|
154
86
|
|
|
155
87
|
import { getLifecycleApi } from "@real-router/core/api";
|
|
156
88
|
|
|
157
89
|
const lifecycle = getLifecycleApi(router);
|
|
158
|
-
lifecycle.addDeactivateGuard(
|
|
159
|
-
|
|
160
|
-
|
|
90
|
+
lifecycle.addDeactivateGuard(
|
|
91
|
+
"checkout",
|
|
92
|
+
(router, getDep) => (toState, fromState) => {
|
|
93
|
+
return !hasUnsavedChanges(); // false blocks back/forward
|
|
94
|
+
},
|
|
95
|
+
);
|
|
161
96
|
```
|
|
162
97
|
|
|
163
|
-
---
|
|
164
|
-
|
|
165
98
|
## SSR Support
|
|
166
99
|
|
|
167
|
-
|
|
100
|
+
SSR-safe — automatically detects the environment and falls back to no-ops:
|
|
168
101
|
|
|
169
102
|
```typescript
|
|
170
|
-
// Server-side — no errors, methods return safe defaults
|
|
171
103
|
router.usePlugin(hashPluginFactory());
|
|
172
|
-
router.buildUrl("home"); //
|
|
173
|
-
router.matchUrl("/path"); //
|
|
104
|
+
router.buildUrl("home"); // returns hash path
|
|
105
|
+
router.matchUrl("/path"); // returns undefined
|
|
174
106
|
```
|
|
175
107
|
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## Why Hash Routing?
|
|
179
|
-
|
|
180
|
-
Hash-based routing stores the entire route in the URL hash fragment (`#/path`). This means:
|
|
181
|
-
|
|
182
|
-
- **No server configuration** — the server always serves the same `index.html` regardless of the URL
|
|
183
|
-
- **Works on static hosting** — GitHub Pages, S3, Netlify (without redirect rules)
|
|
184
|
-
- **Legacy browser support** — works everywhere that supports `hashchange` events
|
|
185
|
-
|
|
186
|
-
The tradeoff is less clean URLs (`example.com/#!/users` vs `example.com/users`).
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
108
|
## Documentation
|
|
191
109
|
|
|
192
|
-
Full documentation
|
|
110
|
+
Full documentation: [Wiki — hash-plugin](https://github.com/greydragon888/real-router/wiki/hash-plugin)
|
|
193
111
|
|
|
194
112
|
- [Configuration Options](https://github.com/greydragon888/real-router/wiki/hash-plugin#3-configuration-options)
|
|
195
|
-
- [Lifecycle Hooks](https://github.com/greydragon888/real-router/wiki/hash-plugin#4-lifecycle-hooks)
|
|
196
|
-
- [Router Methods](https://github.com/greydragon888/real-router/wiki/hash-plugin#5-router-interaction)
|
|
197
113
|
- [Behavior & Edge Cases](https://github.com/greydragon888/real-router/wiki/hash-plugin#8-behavior)
|
|
198
114
|
|
|
199
|
-
---
|
|
200
|
-
|
|
201
115
|
## Related Packages
|
|
202
116
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
117
|
+
| Package | Description |
|
|
118
|
+
| ---------------------------------------------------------------------------------------- | -------------------------------------- |
|
|
119
|
+
| [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router (required peer dependency) |
|
|
120
|
+
| [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) | History API routing (clean URLs) |
|
|
121
|
+
| [@real-router/react](https://www.npmjs.com/package/@real-router/react) | React integration |
|
|
122
|
+
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
See [contributing guidelines](../../CONTRIBUTING.md) for development setup and PR process.
|
|
207
126
|
|
|
208
127
|
## License
|
|
209
128
|
|
|
210
|
-
MIT © [Oleg Ivanov](https://github.com/greydragon888)
|
|
129
|
+
[MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -40,11 +40,11 @@ declare function hashPluginFactory(opts?: Partial<HashPluginOptions>, browser?:
|
|
|
40
40
|
type TransitionPhase = "deactivating" | "activating";
|
|
41
41
|
type TransitionReason = "success" | "blocked" | "cancelled" | "error";
|
|
42
42
|
interface TransitionMeta {
|
|
43
|
-
readonly reload?: boolean;
|
|
44
|
-
readonly redirected?: boolean;
|
|
45
43
|
phase: TransitionPhase;
|
|
46
|
-
from?: string;
|
|
47
44
|
reason: TransitionReason;
|
|
45
|
+
reload?: boolean;
|
|
46
|
+
redirected?: boolean;
|
|
47
|
+
from?: string;
|
|
48
48
|
blocker?: string;
|
|
49
49
|
segments: {
|
|
50
50
|
deactivated: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.
|
|
1
|
+
{"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"../browser-env/dist/esm/index.mjs":{"bytes":4153,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/constants.ts":{"bytes":367,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hash-utils.ts":{"bytes":1119,"imports":[{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/plugin.ts":{"bytes":2769,"imports":[{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/hash-utils.ts","kind":"import-statement","original":"./hash-utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/validation.ts":{"bytes":282,"imports":[{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/factory.ts":{"bytes":1582,"imports":[{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/hash-utils.ts","kind":"import-statement","original":"./hash-utils"},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":1283,"imports":[{"path":"src/factory.ts","kind":"import-statement","original":"./factory"},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.19.4_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":9143},"dist/cjs/index.js":{"imports":[{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["hashPluginFactory","isState"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":880},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2337},"../browser-env/dist/esm/index.mjs":{"bytesInOutput":4761},"src/constants.ts":{"bytesInOutput":141},"src/hash-utils.ts":{"bytesInOutput":568},"src/plugin.ts":{"bytesInOutput":1634},"src/validation.ts":{"bytesInOutput":63},"src/index.ts":{"bytesInOutput":0}},"bytes":10628}}}
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -40,11 +40,11 @@ declare function hashPluginFactory(opts?: Partial<HashPluginOptions>, browser?:
|
|
|
40
40
|
type TransitionPhase = "deactivating" | "activating";
|
|
41
41
|
type TransitionReason = "success" | "blocked" | "cancelled" | "error";
|
|
42
42
|
interface TransitionMeta {
|
|
43
|
-
readonly reload?: boolean;
|
|
44
|
-
readonly redirected?: boolean;
|
|
45
43
|
phase: TransitionPhase;
|
|
46
|
-
from?: string;
|
|
47
44
|
reason: TransitionReason;
|
|
45
|
+
reload?: boolean;
|
|
46
|
+
redirected?: boolean;
|
|
47
|
+
from?: string;
|
|
48
48
|
blocker?: string;
|
|
49
49
|
segments: {
|
|
50
50
|
deactivated: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/hash-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "Hash-based routing plugin for Real-Router",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
},
|
|
44
44
|
"sideEffects": false,
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@real-router/core": "^0.
|
|
46
|
+
"@real-router/core": "^0.38.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@testing-library/jest-dom": "6.9.1",
|
|
50
50
|
"jsdom": "28.1.0",
|
|
51
|
-
"browser-env": "^0.1.
|
|
52
|
-
"type-guards": "^0.3.
|
|
51
|
+
"browser-env": "^0.1.5",
|
|
52
|
+
"type-guards": "^0.3.7"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"test": "vitest",
|