@nimpl/getters 0.0.0-experimental-b3fa49d
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/LICENSE +9 -0
- package/README.md +48 -0
- package/get-app-params.d.ts +16 -0
- package/get-app-params.js +57 -0
- package/get-app-pathname.d.ts +1 -0
- package/get-app-pathname.js +16 -0
- package/get-page-config.d.ts +9 -0
- package/get-page-config.js +25 -0
- package/get-params.d.ts +16 -0
- package/get-params.js +59 -0
- package/get-pathname.d.ts +1 -0
- package/get-pathname.js +27 -0
- package/package.json +47 -0
- package/server-getter-in-client-component-error.d.ts +1 -0
- package/server-getter-in-client-component-error.js +15 -0
- package/utils.d.ts +10 -0
- package/utils.js +106 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Alex Savelyev <dev@alexdln.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @nimpl/getters
|
|
2
|
+
|
|
3
|
+
(Former [next-impl-getters](https://www.npmjs.com/package/next-impl-getters))
|
|
4
|
+
|
|
5
|
+
Implementation of server getters in React Server Components without switching to SSR in Next.js
|
|
6
|
+
|
|
7
|
+
Before using the library, read the [Possible Issues](https://nimpl.dev/docs/server-getters#possible-issues)
|
|
8
|
+
|
|
9
|
+
Visit https://nimpl.dev/docs/server-getters to view the full documentation.
|
|
10
|
+
|
|
11
|
+
Use `@nimpl/getters 2.x` for Next.js v15. For earlier versions of Next.js use `@nimpl/getters 1.x`.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
**Using npm:**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm i @nimpl/getters
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Using yarn:**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
yarn add @nimpl/getters
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Current Getters
|
|
28
|
+
|
|
29
|
+
- [get-pathname](https://nimpl.dev/docs/server-getters#get-pathname)
|
|
30
|
+
- [get-app-pathname](https://nimpl.dev/docs/server-getters#get-app-pathname)
|
|
31
|
+
- [get-params](https://nimpl.dev/docs/server-getters#get-params)
|
|
32
|
+
- [get-app-params](https://nimpl.dev/docs/server-getters#get-app-params)
|
|
33
|
+
- [get-page-config](https://nimpl.dev/docs/server-getters#get-page-config)
|
|
34
|
+
- [get-search-params](https://nimpl.dev/docs/server-getters#get-search-params) (_deprecated_)
|
|
35
|
+
|
|
36
|
+
> _`create-server-context` and `get-server-context` were moved to a separate package - [@nimpl/context](https://nimpl.dev/docs/context)_
|
|
37
|
+
|
|
38
|
+
## Stability
|
|
39
|
+
|
|
40
|
+
All getters are covered with tests. Tests are run on every release and every 6 hours on the latest **Canary** version of `Next.js`.
|
|
41
|
+
|
|
42
|
+
In this way, you can be sure not only of the stability of the code, but also that if there is a breaking change in `Next.js`, this will immediately become known. _Even before the release of a stable version of `Next.js`._
|
|
43
|
+
|
|
44
|
+
## Additional
|
|
45
|
+
|
|
46
|
+
Please consider giving a star if you like it, it will help promote the implementation in the eyes of the Next.js team. This also motivates the author to continue working on this and other solutions ❤️
|
|
47
|
+
|
|
48
|
+
Create issues for identified problems, desired getters, or various improvements.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type GetParamsOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* In case of error or segments difference
|
|
4
|
+
* getter will return null
|
|
5
|
+
* (_f.e. `/_not-found` could be at `/it/removed-page` and it would not have any params_)
|
|
6
|
+
*/
|
|
7
|
+
ignoreDifferenceError?: true;
|
|
8
|
+
/** Custom pathname (f.e. `["/example/custom"]`). Usable for rewritten pages in SSR or custom functions */
|
|
9
|
+
pathname?: string;
|
|
10
|
+
/** Custom pagePaths list (f.e. `["/example/[slug]/page"]`). Usable for rewritten pages in SSR or custom functions */
|
|
11
|
+
pagePaths?: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare const getAppParams: (options?: GetParamsOptions) => {
|
|
14
|
+
[key: string]: string | string[];
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAppParams = void 0;
|
|
4
|
+
const work_async_storage_external_1 = require("next/dist/server/app-render/work-async-storage.external");
|
|
5
|
+
const server_getter_in_client_component_error_1 = require("./server-getter-in-client-component-error");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
const get_app_pathname_1 = require("./get-app-pathname");
|
|
8
|
+
const getAppParams = (options = {}) => {
|
|
9
|
+
(0, server_getter_in_client_component_error_1.serverGetterInClientComponentError)("getAppParams");
|
|
10
|
+
const store = work_async_storage_external_1.workAsyncStorage.getStore();
|
|
11
|
+
if (!store)
|
|
12
|
+
return {};
|
|
13
|
+
const { ignoreDifferenceError, pagePaths, pathname } = options;
|
|
14
|
+
const pageConfigurationStore = work_async_storage_external_1.workAsyncStorage.getStore();
|
|
15
|
+
if (!pageConfigurationStore)
|
|
16
|
+
return {};
|
|
17
|
+
const { route } = pageConfigurationStore;
|
|
18
|
+
const pagePath = route;
|
|
19
|
+
const urlPathname = (0, get_app_pathname_1.getAppPathname)();
|
|
20
|
+
const targetUrlPathname = pathname || urlPathname;
|
|
21
|
+
if (!targetUrlPathname)
|
|
22
|
+
return {};
|
|
23
|
+
let isInvalid = false;
|
|
24
|
+
try {
|
|
25
|
+
if (pagePaths) {
|
|
26
|
+
for (const userPagePath of pagePaths) {
|
|
27
|
+
const params = (0, utils_1.parseParams)(targetUrlPathname, userPagePath);
|
|
28
|
+
if (params === utils_1.INVALID_PARSE) {
|
|
29
|
+
isInvalid ||= true;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
return params || {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const params = (0, utils_1.parseParams)(targetUrlPathname, pagePath);
|
|
37
|
+
if (params === utils_1.INVALID_PARSE) {
|
|
38
|
+
isInvalid ||= true;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return params || {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
isInvalid = true;
|
|
47
|
+
}
|
|
48
|
+
if (isInvalid && !ignoreDifferenceError) {
|
|
49
|
+
const createIssueUrl = new URL("https://github.com/alexdln/nimpl-getters/issues/new");
|
|
50
|
+
createIssueUrl.searchParams.set("title", "Error parsing segments in get-params");
|
|
51
|
+
createIssueUrl.searchParams.set("body", `urlPathname: \`${urlPathname}\`;\n\npagePath(s): \`${pagePaths || pagePath}\`;`);
|
|
52
|
+
createIssueUrl.searchParams.append("labels", "bug");
|
|
53
|
+
throw new Error(`Something went wrong. Please create an issue on Github: ${createIssueUrl}`);
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
};
|
|
57
|
+
exports.getAppParams = getAppParams;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getAppPathname(): any;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAppPathname = getAppPathname;
|
|
4
|
+
const work_unit_async_storage_external_1 = require("next/dist/server/app-render/work-unit-async-storage.external");
|
|
5
|
+
const server_getter_in_client_component_error_1 = require("./server-getter-in-client-component-error");
|
|
6
|
+
function getAppPathname() {
|
|
7
|
+
(0, server_getter_in_client_component_error_1.serverGetterInClientComponentError)("getAppPathname");
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
const pageStore = work_unit_async_storage_external_1.workUnitAsyncStorage.getStore();
|
|
10
|
+
const tags = (Array.isArray(pageStore.implicitTags.tags) && pageStore.implicitTags.tags) ||
|
|
11
|
+
(Array.isArray(pageStore.implicitTags) && pageStore.implicitTags) ||
|
|
12
|
+
[];
|
|
13
|
+
const pageTags = tags.filter((tag) => tag.startsWith("_N_T_/"));
|
|
14
|
+
const pathnameTag = pageTags[pageTags.length - 1];
|
|
15
|
+
return pathnameTag.substring(5);
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPageConfig = void 0;
|
|
4
|
+
const work_async_storage_external_1 = require("next/dist/server/app-render/work-async-storage.external");
|
|
5
|
+
const server_getter_in_client_component_error_1 = require("./server-getter-in-client-component-error");
|
|
6
|
+
const getPageConfig = () => {
|
|
7
|
+
(0, server_getter_in_client_component_error_1.serverGetterInClientComponentError)("getPageConfig");
|
|
8
|
+
const store = work_async_storage_external_1.workAsyncStorage.getStore();
|
|
9
|
+
if (!store)
|
|
10
|
+
return {};
|
|
11
|
+
const basePath = process.env.__NEXT_ROUTER_BASEPATH || "";
|
|
12
|
+
const { page, forceDynamic, forceStatic, dynamicShouldError } = store || {};
|
|
13
|
+
let dynamic = "auto";
|
|
14
|
+
if (forceDynamic) {
|
|
15
|
+
dynamic = "force-dynamic";
|
|
16
|
+
}
|
|
17
|
+
else if (forceStatic) {
|
|
18
|
+
dynamic = "force-static";
|
|
19
|
+
}
|
|
20
|
+
else if (dynamicShouldError) {
|
|
21
|
+
dynamic = "error";
|
|
22
|
+
}
|
|
23
|
+
return { pagePath: page, dynamic, basePath };
|
|
24
|
+
};
|
|
25
|
+
exports.getPageConfig = getPageConfig;
|
package/get-params.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type GetParamsOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* In case of error or segments difference
|
|
4
|
+
* getter will return null
|
|
5
|
+
* (_f.e. `/_not-found` could be at `/it/removed-page` and it would not have any params_)
|
|
6
|
+
*/
|
|
7
|
+
ignoreDifferenceError?: true;
|
|
8
|
+
/** Custom pathname (f.e. `["/example/custom"]`). Usable for rewritten pages in SSR or custom functions */
|
|
9
|
+
pathname?: string;
|
|
10
|
+
/** Custom pagePaths list (f.e. `["/example/[slug]/page"]`). Usable for rewritten pages in SSR or custom functions */
|
|
11
|
+
pagePaths?: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare const getParams: (options?: GetParamsOptions) => {
|
|
14
|
+
[key: string]: string | string[];
|
|
15
|
+
};
|
|
16
|
+
export {};
|
package/get-params.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getParams = void 0;
|
|
4
|
+
const work_async_storage_external_1 = require("next/dist/server/app-render/work-async-storage.external");
|
|
5
|
+
const work_unit_async_storage_external_1 = require("next/dist/server/app-render/work-unit-async-storage.external");
|
|
6
|
+
const server_getter_in_client_component_error_1 = require("./server-getter-in-client-component-error");
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
const getParams = (options = {}) => {
|
|
9
|
+
(0, server_getter_in_client_component_error_1.serverGetterInClientComponentError)("getParams");
|
|
10
|
+
const store = work_async_storage_external_1.workAsyncStorage.getStore();
|
|
11
|
+
if (!store)
|
|
12
|
+
return {};
|
|
13
|
+
const { ignoreDifferenceError, pagePaths, pathname } = options;
|
|
14
|
+
const pageConfigurationStore = work_async_storage_external_1.workAsyncStorage.getStore();
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
const pageStore = work_unit_async_storage_external_1.workUnitAsyncStorage.getStore();
|
|
17
|
+
if (!pageConfigurationStore)
|
|
18
|
+
return {};
|
|
19
|
+
const { route } = pageConfigurationStore;
|
|
20
|
+
const pagePath = route;
|
|
21
|
+
const urlPathname = pageStore?.url?.pathname;
|
|
22
|
+
const targetUrlPathname = pathname || urlPathname;
|
|
23
|
+
if (!targetUrlPathname)
|
|
24
|
+
return {};
|
|
25
|
+
let isInvalid = false;
|
|
26
|
+
try {
|
|
27
|
+
if (pagePaths) {
|
|
28
|
+
for (const userPagePath of pagePaths) {
|
|
29
|
+
const params = (0, utils_1.parseParams)(targetUrlPathname, userPagePath);
|
|
30
|
+
if (params === utils_1.INVALID_PARSE) {
|
|
31
|
+
isInvalid ||= true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
return params || {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const params = (0, utils_1.parseParams)(targetUrlPathname, pagePath);
|
|
39
|
+
if (params === utils_1.INVALID_PARSE) {
|
|
40
|
+
isInvalid ||= true;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return params || {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
isInvalid = true;
|
|
49
|
+
}
|
|
50
|
+
if (isInvalid && !ignoreDifferenceError) {
|
|
51
|
+
const createIssueUrl = new URL("https://github.com/alexdln/nimpl-getters/issues/new");
|
|
52
|
+
createIssueUrl.searchParams.set("title", "Error parsing segments in get-params");
|
|
53
|
+
createIssueUrl.searchParams.set("body", `urlPathname: \`${urlPathname}\`;\n\npagePath(s): \`${pagePaths || pagePath}\`;`);
|
|
54
|
+
createIssueUrl.searchParams.append("labels", "bug");
|
|
55
|
+
throw new Error(`Something went wrong. Please create an issue on Github: ${createIssueUrl}`);
|
|
56
|
+
}
|
|
57
|
+
return {};
|
|
58
|
+
};
|
|
59
|
+
exports.getParams = getParams;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getPathname(): string | null;
|
package/get-pathname.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPathname = getPathname;
|
|
4
|
+
const work_async_storage_external_1 = require("next/dist/server/app-render/work-async-storage.external");
|
|
5
|
+
const work_unit_async_storage_external_1 = require("next/dist/server/app-render/work-unit-async-storage.external");
|
|
6
|
+
const has_base_path_1 = require("next/dist/client/has-base-path");
|
|
7
|
+
const remove_base_path_1 = require("next/dist/client/remove-base-path");
|
|
8
|
+
const server_getter_in_client_component_error_1 = require("./server-getter-in-client-component-error");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
function getPathname() {
|
|
11
|
+
(0, server_getter_in_client_component_error_1.serverGetterInClientComponentError)("getPathname");
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
const pageStore = work_unit_async_storage_external_1.workUnitAsyncStorage.getStore();
|
|
14
|
+
let originalPathname = pageStore?.url?.pathname;
|
|
15
|
+
if (!originalPathname) {
|
|
16
|
+
const pageConfigurationStore = work_async_storage_external_1.workAsyncStorage.getStore();
|
|
17
|
+
originalPathname =
|
|
18
|
+
pageConfigurationStore?.route &&
|
|
19
|
+
(0, utils_1.normalizeInterceptingRoutes)(pageConfigurationStore.route.split("/")).join("/");
|
|
20
|
+
}
|
|
21
|
+
if (!originalPathname) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const url = new URL(originalPathname, "http://n");
|
|
25
|
+
const pathname = (0, has_base_path_1.hasBasePath)(url.pathname) ? (0, remove_base_path_1.removeBasePath)(url.pathname) : url.pathname;
|
|
26
|
+
return pathname;
|
|
27
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nimpl/getters",
|
|
3
|
+
"version": "0.0.0-experimental-b3fa49d",
|
|
4
|
+
"description": "Implementation of server getters in React Server Components without switching to SSR in next.js",
|
|
5
|
+
"files": [
|
|
6
|
+
"**/*.js",
|
|
7
|
+
"**/*.d.ts"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"next",
|
|
14
|
+
"next.js",
|
|
15
|
+
"server components",
|
|
16
|
+
"react.js",
|
|
17
|
+
"get-pathname",
|
|
18
|
+
"getters",
|
|
19
|
+
"implementation",
|
|
20
|
+
"get-params",
|
|
21
|
+
"app router",
|
|
22
|
+
"rsc"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git://github.com/alexdln/nimpl-getters.git"
|
|
27
|
+
},
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "Alex Savelyev",
|
|
30
|
+
"email": "dev@alexdln.com",
|
|
31
|
+
"url": "https://github.com/alexdln/"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "25.0.3",
|
|
36
|
+
"@types/react": "19.2.7",
|
|
37
|
+
"next": "16.1.1",
|
|
38
|
+
"react": "19.2.3",
|
|
39
|
+
"react-dom": "19.2.3",
|
|
40
|
+
"typescript": "5.9.3"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"next": ">= 14.0.0",
|
|
44
|
+
"react": ">= 18.2.0",
|
|
45
|
+
"react-dom": ">= 18.2.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function serverGetterInClientComponentError(getterName: string): void | never;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.serverGetterInClientComponentError = serverGetterInClientComponentError;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function serverGetterInClientComponentError(getterName) {
|
|
9
|
+
if (process.env.NODE_ENV !== "production") {
|
|
10
|
+
// If useState is defined we're in a client component
|
|
11
|
+
if (Boolean(react_1.default.useState)) {
|
|
12
|
+
throw new Error(`${getterName} only works in Server Components`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
package/utils.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const normalizePathname: (pathname: string) => string;
|
|
2
|
+
export declare const normalizePagePath: (pagePath: string) => string;
|
|
3
|
+
export declare const parseSegments: (pagePathParts: string[], pathnameParts: string[]) => {
|
|
4
|
+
[key: string]: string | string[];
|
|
5
|
+
};
|
|
6
|
+
export declare const normalizeInterceptingRoutes: (pageParts: string[]) => string[];
|
|
7
|
+
export declare const INVALID_PARSE: unique symbol;
|
|
8
|
+
export declare const parseParams: (urlPathname: string, pagePath: string) => typeof INVALID_PARSE | {
|
|
9
|
+
[key: string]: string | string[];
|
|
10
|
+
};
|
package/utils.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseParams = exports.INVALID_PARSE = exports.normalizeInterceptingRoutes = exports.parseSegments = exports.normalizePagePath = exports.normalizePathname = void 0;
|
|
4
|
+
const normalizePathname = (pathname) => {
|
|
5
|
+
const cleanPathname = pathname && new URL(pathname, "http://n").pathname;
|
|
6
|
+
const pathnameWithoutTrailingSlash = cleanPathname?.replace(/^(\/.*)\/$/, "$1");
|
|
7
|
+
const pathnameWithoutFileType = pathnameWithoutTrailingSlash?.replace(/\/_not-found$/, "");
|
|
8
|
+
return pathnameWithoutFileType.endsWith("/") ? pathnameWithoutFileType : pathnameWithoutFileType + "/";
|
|
9
|
+
};
|
|
10
|
+
exports.normalizePathname = normalizePathname;
|
|
11
|
+
const normalizePagePath = (pagePath) => {
|
|
12
|
+
const cleanPagePath = pagePath && new URL(pagePath, "http://n").pathname;
|
|
13
|
+
const pagePathWithoutFileType = cleanPagePath?.replace(/(\/page|\/_not-found)$/, "/");
|
|
14
|
+
const pagePathWithoutGroups = pagePathWithoutFileType
|
|
15
|
+
.split("/")
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.filter((segment) => !segment.match(/^(\([^)]+\)$|^\@.+$)/g));
|
|
18
|
+
if (pagePathWithoutGroups.length === 0)
|
|
19
|
+
return "/";
|
|
20
|
+
return "/" + pagePathWithoutGroups.join("/") + "/";
|
|
21
|
+
};
|
|
22
|
+
exports.normalizePagePath = normalizePagePath;
|
|
23
|
+
const parseSegments = (pagePathParts, pathnameParts) => {
|
|
24
|
+
const query = pagePathParts.reduce((acc, cur, index) => {
|
|
25
|
+
const optionalCatchAllSegment = cur.match(/^\[\[\.\.\.([^\]]+)\]\]$/);
|
|
26
|
+
if (optionalCatchAllSegment) {
|
|
27
|
+
const key = optionalCatchAllSegment[1];
|
|
28
|
+
const segmentParts = pathnameParts.slice(index);
|
|
29
|
+
if (segmentParts.length) {
|
|
30
|
+
acc[key] = segmentParts;
|
|
31
|
+
}
|
|
32
|
+
return acc;
|
|
33
|
+
}
|
|
34
|
+
const catchAllSegment = cur.match(/^\[\.\.\.([^\]]+)\]$/);
|
|
35
|
+
if (catchAllSegment) {
|
|
36
|
+
const key = catchAllSegment[1];
|
|
37
|
+
acc[key] = pathnameParts.slice(index);
|
|
38
|
+
return acc;
|
|
39
|
+
}
|
|
40
|
+
const dynamicSegment = cur.match(/^\[([^\]]+)\]$/);
|
|
41
|
+
if (dynamicSegment) {
|
|
42
|
+
const key = dynamicSegment[1];
|
|
43
|
+
acc[key] = pathnameParts[index];
|
|
44
|
+
return acc;
|
|
45
|
+
}
|
|
46
|
+
return acc;
|
|
47
|
+
}, {});
|
|
48
|
+
return query;
|
|
49
|
+
};
|
|
50
|
+
exports.parseSegments = parseSegments;
|
|
51
|
+
const normalizeInterceptingRoutes = (pageParts) => {
|
|
52
|
+
let skip = 0;
|
|
53
|
+
const normilizedParts = [];
|
|
54
|
+
for (const pagepart of [...pageParts].reverse()) {
|
|
55
|
+
if (skip) {
|
|
56
|
+
skip -= 1;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (pagepart.startsWith("(...)")) {
|
|
60
|
+
normilizedParts.push(pagepart.replace(/^\(\.\.\.\)/, ""));
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
else if (pagepart.startsWith("(.)")) {
|
|
64
|
+
normilizedParts.push(pagepart.replace(/^\(\.\)/, ""));
|
|
65
|
+
}
|
|
66
|
+
else if (pagepart.startsWith("(..)")) {
|
|
67
|
+
const skipLeafs = pagepart.match(/\(\.\.\)/g);
|
|
68
|
+
skip += skipLeafs?.length || 0;
|
|
69
|
+
normilizedParts.push(pagepart.replace(/^(\(\.\.\))+/, ""));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
normilizedParts.push(pagepart);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return normilizedParts.reverse();
|
|
76
|
+
};
|
|
77
|
+
exports.normalizeInterceptingRoutes = normalizeInterceptingRoutes;
|
|
78
|
+
exports.INVALID_PARSE = Symbol("Invalid Parse");
|
|
79
|
+
const isSamePaths = (urlPathnameParts, pagePathParts) => {
|
|
80
|
+
for (let i = 0; i < pagePathParts.length; i++) {
|
|
81
|
+
const urlPathnamePart = urlPathnameParts[i];
|
|
82
|
+
const pagePathPart = pagePathParts[i];
|
|
83
|
+
if (pagePathPart.match(/\[\.\.\.[^\]]+\]/))
|
|
84
|
+
return true;
|
|
85
|
+
if (pagePathPart.match(/\[[^\]]+\]/))
|
|
86
|
+
continue;
|
|
87
|
+
if (urlPathnamePart !== pagePathPart)
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return urlPathnameParts.length === pagePathParts.length;
|
|
91
|
+
};
|
|
92
|
+
const parseParams = (urlPathname, pagePath) => {
|
|
93
|
+
const cleanUrlPathname = (0, exports.normalizePathname)(urlPathname);
|
|
94
|
+
const cleanPagePath = (0, exports.normalizePagePath)(pagePath);
|
|
95
|
+
const pagePathParts = cleanPagePath.split("/").slice(0, -1);
|
|
96
|
+
const pagePathInterceptedParts = (0, exports.normalizeInterceptingRoutes)(pagePathParts);
|
|
97
|
+
const pathnameParts = cleanUrlPathname.split("/").slice(0, -1);
|
|
98
|
+
const isNotFoundPage = pagePath.match(/\/_not-found\/?$/);
|
|
99
|
+
const isCorrectMatched = isNotFoundPage || isSamePaths(pathnameParts, pagePathInterceptedParts);
|
|
100
|
+
if (!isCorrectMatched) {
|
|
101
|
+
return exports.INVALID_PARSE;
|
|
102
|
+
}
|
|
103
|
+
const query = (0, exports.parseSegments)(pagePathInterceptedParts, pathnameParts);
|
|
104
|
+
return query;
|
|
105
|
+
};
|
|
106
|
+
exports.parseParams = parseParams;
|