@real-router/hash-plugin 0.2.2 → 0.2.4
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 +24 -21
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/metafile-cjs.json +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -36,11 +36,11 @@ await router.start(); // reads hash from browser location
|
|
|
36
36
|
|
|
37
37
|
## Options
|
|
38
38
|
|
|
39
|
-
| Option
|
|
40
|
-
|
|
41
|
-
| `hashPrefix`
|
|
42
|
-
| `base`
|
|
43
|
-
| `forceDeactivate` | `boolean` | `true`
|
|
39
|
+
| Option | Type | Default | Description |
|
|
40
|
+
| ----------------- | --------- | ------- | ----------------------------------------------------- |
|
|
41
|
+
| `hashPrefix` | `string` | `""` | Prefix after `#` (e.g., `"!"` → `#!/path`) |
|
|
42
|
+
| `base` | `string` | `""` | Base path before hash (e.g., `"/app"` → `/app#/path`) |
|
|
43
|
+
| `forceDeactivate` | `boolean` | `true` | Bypass `canDeactivate` guards on back/forward |
|
|
44
44
|
|
|
45
45
|
```typescript
|
|
46
46
|
router.usePlugin(hashPluginFactory({ hashPrefix: "!", base: "/app" }));
|
|
@@ -53,11 +53,11 @@ router.navigate("users", { id: "123" });
|
|
|
53
53
|
|
|
54
54
|
The plugin extends the router instance with three methods via [`extendRouter()`](https://github.com/greydragon888/real-router/wiki/plugin-architecture):
|
|
55
55
|
|
|
56
|
-
| Method
|
|
57
|
-
|
|
58
|
-
| `buildUrl(name, params?)`
|
|
59
|
-
| `matchUrl(url)`
|
|
60
|
-
| `replaceHistoryState(name, params?, title?)` | `void`
|
|
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 |
|
|
61
61
|
|
|
62
62
|
```typescript
|
|
63
63
|
router.buildUrl("users", { id: "123" });
|
|
@@ -74,7 +74,7 @@ router.replaceHistoryState("users", { id: "456" });
|
|
|
74
74
|
|
|
75
75
|
```typescript
|
|
76
76
|
router.buildPath("users", { id: 1 }); // "/users/1" — core, no hash
|
|
77
|
-
router.buildUrl("users", { id: 1 });
|
|
77
|
+
router.buildUrl("users", { id: 1 }); // "#!/users/1" — plugin, with hash prefix
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
## Form Protection
|
|
@@ -87,9 +87,12 @@ router.usePlugin(hashPluginFactory({ forceDeactivate: false }));
|
|
|
87
87
|
import { getLifecycleApi } from "@real-router/core/api";
|
|
88
88
|
|
|
89
89
|
const lifecycle = getLifecycleApi(router);
|
|
90
|
-
lifecycle.addDeactivateGuard(
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
lifecycle.addDeactivateGuard(
|
|
91
|
+
"checkout",
|
|
92
|
+
(router, getDep) => (toState, fromState) => {
|
|
93
|
+
return !hasUnsavedChanges(); // false blocks back/forward
|
|
94
|
+
},
|
|
95
|
+
);
|
|
93
96
|
```
|
|
94
97
|
|
|
95
98
|
## SSR Support
|
|
@@ -98,8 +101,8 @@ SSR-safe — automatically detects the environment and falls back to no-ops:
|
|
|
98
101
|
|
|
99
102
|
```typescript
|
|
100
103
|
router.usePlugin(hashPluginFactory());
|
|
101
|
-
router.buildUrl("home");
|
|
102
|
-
router.matchUrl("/path");
|
|
104
|
+
router.buildUrl("home"); // returns hash path
|
|
105
|
+
router.matchUrl("/path"); // returns undefined
|
|
103
106
|
```
|
|
104
107
|
|
|
105
108
|
## Documentation
|
|
@@ -111,11 +114,11 @@ Full documentation: [Wiki — hash-plugin](https://github.com/greydragon888/real
|
|
|
111
114
|
|
|
112
115
|
## Related Packages
|
|
113
116
|
|
|
114
|
-
| Package
|
|
115
|
-
|
|
116
|
-
| [@real-router/core](https://www.npmjs.com/package/@real-router/core)
|
|
117
|
-
| [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) | History API routing (clean URLs)
|
|
118
|
-
| [@real-router/react](https://www.npmjs.com/package/@real-router/react)
|
|
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 |
|
|
119
122
|
|
|
120
123
|
## Contributing
|
|
121
124
|
|
package/dist/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
var e=require("@real-router/core/api"),t=require("@real-router/core"),r=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function n(e,t=new WeakSet){if(null==e)return!0;const r=typeof e;if("string"===r||"boolean"===r)return!0;if("number"===r)return Number.isFinite(e);if("function"===r||"symbol"===r)return!1;if(Array.isArray(e))return!t.has(e)&&(t.add(e),e.every(e=>n(e,t)));if("object"===r){if(t.has(e))return!1;t.add(e);const r=Object.getPrototypeOf(e);return(null===r||r===Object.prototype)&&Object.values(e).every(e=>n(e,t))}return!1}function o(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}function a(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const t=Object.getPrototypeOf(e);if(null!==t&&t!==Object.prototype)return!1;let r=!1;for(const t in e){if(!Object.hasOwn(e,t))continue;const n=e[t];if(!o(n)){const e=typeof n;if("function"===e||"symbol"===e)return!1;r=!0;break}}return!r||n(e)}function i(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||("number"===t?Number.isFinite(e):!!Array.isArray(e)&&e.every(e=>{const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}))}function s(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!function(e){return function(e){return"string"==typeof e&&(""===e||!(e.length>1e4)&&(!!e.startsWith("@@")||r.test(e)))}(e.name)&&"string"==typeof e.path&&a(e.params)}(t)&&(void 0===t.meta||function(e){if("object"!=typeof e||null===e)return!1;const t=e;return!("params"in t&&!function(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;for(const t in e)if(Object.hasOwn(e,t)&&!i(e[t]))return!1;return!0}(t.params)||"id"in t&&"number"!=typeof t.id)}(t.meta))}var c=(e,t)=>{globalThis.history.pushState(e,"",t)},u=(e,t)=>{globalThis.history.replaceState(e,"",t)},l=e=>(globalThis.addEventListener("popstate",e),()=>{globalThis.removeEventListener("popstate",e)}),p=()=>globalThis.location.hash,h=()=>{},f=e=>{let t=!1;return r=>{t||(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: "${e}"). Method "${r}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),t=!0)}},d=e=>{const t=f(e);return{pushState:()=>{t("pushState")},replaceState:()=>{t("replaceState")},addPopstateListener:()=>(t("addPopstateListener"),h),getHash:()=>(t("getHash"),"")}};function m(e,t,r,n){const o={meta:e.meta,name:e.name,params:e.params,path:e.path};r?n.replaceState(o,t):n.pushState(o,t)}function b(e){let r=!1,n=null;async function o(a){if(r)return console.warn(`[${e.loggerContext}] Transition in progress, deferring popstate event`),void(n=a);r=!0;try{const t=function(e,t,r){if(s(e.state))return{name:e.state.name,params:e.state.params};const n=t.matchPath(r.getLocation());return n?{name:n.name,params:n.params}:void 0}(a,e.api,e.browser);t?await e.router.navigate(t.name,t.params,e.transitionOptions):e.allowNotFound?e.router.navigateToNotFound(e.browser.getLocation()):await e.router.navigateToDefault({...e.transitionOptions,reload:!0,replace:!0})}catch(r){r instanceof t.RouterError||function(t){console.error(`[${e.loggerContext}] Critical error in onPopState`,t);try{const t=e.router.getState();if(t){const r=e.buildUrl(t.name,t.params);e.browser.replaceState(t,r)}}catch(t){console.error(`[${e.loggerContext}] Failed to recover from critical error`,t)}}(r)}finally{r=!1,function(){if(n){const t=n;n=null,console.warn(`[${e.loggerContext}] Processing deferred popstate event`),o(t)}}()}}return e=>{o(e)}}function g(e,t,r,n){return(o,a={})=>{const i=e.buildState(o,a);if(!i)throw new Error(`[real-router] Cannot replace state: route "${o}" is not found`);m(e.makeState(i.name,i.params,t.buildPath(i.name,i.params),{params:i.meta},1),n(o,a),!0,r)}}var v={hashPrefix:"",base:"",forceDeactivate:!0},y="hash-plugin";function w(e,t){return(t?e.replace(t,""):e.slice(1))||"/"}var S,P,L=class{#e;#t;#r;#n;#o;constructor(e,t,r,n,o,a,i){var s;this.#e=e,this.#t=n,this.#r=(s=n,t.addInterceptor("start",(e,t)=>e(t??s.getLocation())));const c=`${r.base}#${r.hashPrefix}`,u=(t,r)=>c+e.buildPath(t,r);this.#n=t.extendRouter({buildUrl:u,matchUrl:e=>{const r=function(e,t){const r=function(e,t){try{const r=new URL(e,globalThis.location.origin);return["http:","https:"].includes(r.protocol)?r:(console.warn(`[${t}] Invalid URL protocol in ${e}`),null)}catch(r){return console.warn(`[${t}] Could not parse url ${e}`,r),null}}(e,y);return r?w(r.hash,t)+r.search:null}(e,o);return r?t.matchPath(r):void 0},replaceHistoryState:g(t,e,n,u)});const l=b({router:e,api:t,browser:n,allowNotFound:t.getOptions().allowNotFound,transitionOptions:a,loggerContext:"hash-plugin",buildUrl:(t,r)=>e.buildUrl(t,r)});this.#o=function(e){return{onStart:()=>{e.shared.removePopStateListener&&e.shared.removePopStateListener(),e.shared.removePopStateListener=e.browser.addPopstateListener(e.handler)},onStop:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0)},teardown:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0),e.cleanup()}}}({browser:n,shared:i,handler:l,cleanup:()=>{this.#r(),this.#n()}})}getPlugin(){return{...this.#o,onTransitionSuccess:(e,t,r)=>{const n=(a=t,i=this.#e,((o=r).replace??!a)||!!o.reload&&i.areStatesEqual(e,a,!1));var o,a,i;m(e,this.#e.buildUrl(e.name,e.params),n,this.#t)}}}},$=(S=v,P=y,e=>{if(e)for(const t of Object.keys(e))if(t in S){const r=e[t],n=typeof S[t],o=typeof r;if(void 0!==r&&o!==n)throw new Error(`[${P}] Invalid type for '${t}': expected ${n}, got ${o}`)}});exports.hashPluginFactory=function(t,r){$(t);const n={...v,...t};n.base=function(e){if(!e)return e;let t=e;return t.startsWith("/")||(t=`/${t}`),t.endsWith("/")&&(t=t.slice(0,-1)),t}(n.base);const o=(h=n.hashPrefix)?new RegExp(`^#${m=h,m.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`)}`):null,a=r??function(e,t){if(void 0!==globalThis.window&&globalThis.history)return{pushState:c,replaceState:u,addPopstateListener:l,getLocation:e,getHash:p};const r=f(t);return{...d(t),getLocation:()=>(r("getLocation"),"")}}(()=>(e=>{try{return encodeURI(decodeURI(e))}catch(t){return console.warn(`[browser-env] Could not encode path "${e}"`,t),e}})(w(globalThis.location.hash,o))+globalThis.location.search,"hash-plugin"),i={forceDeactivate:n.forceDeactivate,source:"popstate",replace:!0},s={removePopStateListener:void 0};var h,m;return function(t){return new L(t,e.getPluginApi(t),n,a,o,i,s).getPlugin()}},exports.isState=s;//# sourceMappingURL=index.js.map
|
|
@@ -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.21.0_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.21.0_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.21.0_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.21.0_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.21.0_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.21.0_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.21.0_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.21.0_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.21.0_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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/hash-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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.39.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.6",
|
|
52
|
+
"type-guards": "^0.3.8"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"test": "vitest",
|