@uxf/router 11.72.2 → 11.72.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxf/router",
3
- "version": "11.72.2",
3
+ "version": "11.72.4",
4
4
  "description": "UXF Router",
5
5
  "author": "UXFans <dev@uxf.cz>",
6
6
  "homepage": "https://gitlab.com/uxf-npm/router#readme",
package/router.js CHANGED
@@ -108,7 +108,7 @@ function createRouter(routes, routerOptions) {
108
108
  if (!schema) {
109
109
  throw new Error(`Route '${String(routeName)}' has no schema.`);
110
110
  }
111
- if (router.isReady) {
111
+ if (!router.isReady) {
112
112
  throw new Error("Router is not ready. Use useQueryParamsStatic instead of useQueryParams.");
113
113
  }
114
114
  return [
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env ts-node-script
2
- import { RoutesDefinition } from "./types";
3
- export declare function routesCheck(routes: RoutesDefinition<any>, options?: {
2
+ import { RoutesDefinition } from "../types";
3
+ interface Options {
4
+ appDir: string;
5
+ pagesDir: string;
4
6
  shouldProcessExit?: boolean;
5
7
  defaultLocale?: string;
6
- }): void;
8
+ }
9
+ export declare function routesCheck(routes: RoutesDefinition<any>, options: Options): void;
10
+ export {};
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ts-node-script
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.routesCheck = routesCheck;
5
+ const throw_error_1 = require("@uxf/core/utils/throw-error");
6
+ const path_1 = require("path");
7
+ const process_1 = require("process");
8
+ const true_case_path_1 = require("true-case-path");
9
+ function fileExists(path) {
10
+ try {
11
+ return Boolean((0, true_case_path_1.trueCasePathSync)(path));
12
+ }
13
+ catch {
14
+ try {
15
+ return Boolean((0, true_case_path_1.trueCasePathSync)(path + "x"));
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ }
22
+ let hasError = false;
23
+ function routesCheck(routes, options) {
24
+ Object.entries(routes).forEach(([routeName, routeDefinition]) => {
25
+ var _a;
26
+ const pathname = typeof routeDefinition.path === "string"
27
+ ? routeDefinition.path
28
+ : routeDefinition.path[(_a = options.defaultLocale) !== null && _a !== void 0 ? _a : (0, throw_error_1.throwError)("Default locale must be provided")];
29
+ const pathParts = pathname.substr(1).split("/");
30
+ try {
31
+ if (fileExists((0, path_1.join)(options.pagesDir, ...pathParts, "index.ts"))) {
32
+ return;
33
+ }
34
+ // TODO @vejvis - proč tu je tahle podmínka?
35
+ const lastPart = pathParts.splice(-1);
36
+ const pagePath = pathParts.length > 0
37
+ ? (0, path_1.join)(options.pagesDir, ...pathParts, `${lastPart[0] || "index"}.ts`)
38
+ : (0, path_1.join)(options.pagesDir, `${lastPart[0] || "index"}.tsx`);
39
+ if (fileExists(pagePath)) {
40
+ return;
41
+ }
42
+ if (fileExists((0, path_1.join)(options.appDir, ...pathname.substr(1).split("/"), "page.ts"))) {
43
+ // app directory
44
+ return;
45
+ }
46
+ throw new Error("Invalid");
47
+ }
48
+ catch {
49
+ process_1.stdout.write(`❌ ${routeName}\n`);
50
+ hasError = true;
51
+ }
52
+ });
53
+ if (hasError) {
54
+ if (options.shouldProcessExit) {
55
+ process.exit(1);
56
+ }
57
+ throw new Error("At least one invalid route found");
58
+ }
59
+ else {
60
+ if (options.shouldProcessExit) {
61
+ process.exit(0);
62
+ }
63
+ process_1.stdout.write("All routes checked.\n");
64
+ }
65
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path_1 = require("path");
4
+ const routes_check_1 = require("./routes-check");
5
+ describe("routesCheck", () => {
6
+ it("should be valid", () => {
7
+ expect(() => (0, routes_check_1.routesCheck)({
8
+ route1: { path: "/" }, // index
9
+ route2: { path: "/product" }, // index file
10
+ route3: { path: "/product/edit" }, // file with specific name
11
+ route4: { path: "/product/[id]" }, // path parameter, file with specific name
12
+ route5: { path: "/product-2/[id]" }, // path parameter, index file
13
+ app1: { path: "/app-directory" },
14
+ app2: { path: "/app-directory/[param]" },
15
+ }, {
16
+ shouldProcessExit: false,
17
+ appDir: (0, path_1.join)(__dirname, "__test__", "app"),
18
+ pagesDir: (0, path_1.join)(__dirname, "__test__", "pages"),
19
+ })).not.toThrow();
20
+ });
21
+ it("should fail", () => {
22
+ expect(() => (0, routes_check_1.routesCheck)({
23
+ unknown: { path: "/unknown-route" }, // index
24
+ }, {
25
+ shouldProcessExit: false,
26
+ appDir: (0, path_1.join)(__dirname, "__test__", "app"),
27
+ pagesDir: (0, path_1.join)(__dirname, "__test__", "pages"),
28
+ })).toThrow();
29
+ });
30
+ });
package/routes-check.js DELETED
@@ -1,121 +0,0 @@
1
- #!/usr/bin/env ts-node-script
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.routesCheck = routesCheck;
8
- const is_not_empty_1 = require("@uxf/core/utils/is-not-empty");
9
- const throw_error_1 = require("@uxf/core/utils/throw-error");
10
- const fs_1 = __importDefault(require("fs"));
11
- const path_1 = require("path");
12
- const true_case_path_1 = require("true-case-path");
13
- let error = false;
14
- function fileExists(path) {
15
- try {
16
- return Boolean((0, true_case_path_1.trueCasePathSync)(path));
17
- }
18
- catch {
19
- return false;
20
- }
21
- }
22
- function directoryExists(path) {
23
- try {
24
- const stats = fs_1.default.lstatSync(path);
25
- return stats.isDirectory();
26
- }
27
- catch {
28
- return false;
29
- }
30
- }
31
- /**
32
- * Filters out folders used for organizational purposes in the app folder
33
- * such as parallel routes (e.g., (group)) and dynamic routes ([param]).
34
- */
35
- function cleanPathSegments(pathSegments) {
36
- return pathSegments.filter((segment) => !segment.startsWith("(") && !segment.endsWith(")"));
37
- }
38
- function normalizeDynamicSegment(segment) {
39
- return (segment === null || segment === void 0 ? void 0 : segment.startsWith("[")) ? "index" : segment || "";
40
- }
41
- /**
42
- * Checks for collisions in the path (in `baseDir`) * between the `segment.tsx` file and the `segment/` folder within one level.
43
- *
44
- * Example:
45
- * - route `/payments/export` gives segments ["payments", "export"]
46
- * - check if it exists at the same time `pages/payments.tsx` and `pages/payments/`.
47
- */
48
- function checkFolderFileCollision(baseDir, pathSegments) {
49
- for (let i = 0; i < pathSegments.length - 1; i++) {
50
- const segment = pathSegments[i];
51
- const parentDir = (0, path_1.join)(baseDir, ...pathSegments.slice(0, i));
52
- const filePath = (0, path_1.join)(parentDir, `${segment}.tsx`);
53
- const folderPath = (0, path_1.join)(parentDir, segment);
54
- if (fileExists(filePath) && directoryExists(folderPath)) {
55
- // eslint-disable-next-line no-console
56
- console.log(`Colission: For segment '${segment}' there is file and folder at the same time.\n` +
57
- `Replace file ${segment}.tsx with ${segment}/index.tsx.`);
58
- throw new Error("Route colission");
59
- }
60
- }
61
- }
62
- function routesCheck(routes, options = {}) {
63
- Object.keys(routes).forEach((route) => {
64
- var _a;
65
- const pathname = typeof routes[route].path === "string"
66
- ? routes[route].path
67
- : routes[route].path[(_a = options.defaultLocale) !== null && _a !== void 0 ? _a : (0, throw_error_1.throwError)("Default locale must be provided")];
68
- try {
69
- const basePath = (0, path_1.join)(__dirname, "..", "..", "src", "pages");
70
- const appBasePath = (0, path_1.join)(__dirname, "..", "..", "src", "app");
71
- const rawPathSegments = pathname.substring(1).split("/");
72
- const pathSegments = cleanPathSegments(rawPathSegments);
73
- checkFolderFileCollision(basePath, pathSegments);
74
- checkFolderFileCollision(appBasePath, pathSegments);
75
- // Find the last segment (e.g. for /payments/export it will be "export")
76
- const lastPart = pathSegments.splice(-1);
77
- const normalizedLastPart = normalizeDynamicSegment(lastPart.at(0));
78
- // --- Checks in `pages` folder ---
79
- // 1) path like pages/product.tsx (if it's at "first level")
80
- const firstLevelPagePath = (0, path_1.join)(basePath, `${normalizedLastPart}.tsx`);
81
- /// 2) path type pages/product/index.tsx
82
- const firstLevelFolderPath = (0, path_1.join)(basePath, normalizedLastPart, "index.tsx");
83
- // 3) paths for nested pages (pages/payments/export.tsx etc.)
84
- const nestedPaths = (0, path_1.join)(basePath, ...pathSegments, `${normalizedLastPart || "index"}.tsx`);
85
- const pagePath = (0, is_not_empty_1.isNotEmpty)(pathSegments)
86
- ? nestedPaths
87
- : fileExists(firstLevelFolderPath)
88
- ? firstLevelFolderPath
89
- : firstLevelPagePath;
90
- // --- Checks in `app folder ` ---
91
- const appPath = (0, path_1.join)(appBasePath, ...pathSegments, normalizedLastPart, "page.tsx");
92
- // Check if the path exists in either `pages` or `app`
93
- if (fileExists(pagePath) || fileExists(appPath)) {
94
- return; // Valid route
95
- }
96
- throw new Error("Missing file for route!");
97
- }
98
- catch (err) {
99
- // eslint-disable-next-line no-console
100
- console.log("❌ Invalid route: " + pathname);
101
- if (err instanceof Error) {
102
- // eslint-disable-next-line no-console
103
- console.error(err.message);
104
- }
105
- error = true;
106
- }
107
- });
108
- if (error) {
109
- if (options.shouldProcessExit) {
110
- process.exit(1);
111
- }
112
- throw new Error("At least one invalid route found");
113
- }
114
- else {
115
- if (options.shouldProcessExit) {
116
- process.exit(0);
117
- }
118
- // eslint-disable-next-line no-console
119
- console.log("All routes checked.");
120
- }
121
- }
@@ -1,130 +0,0 @@
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
- const fs_1 = __importDefault(require("fs"));
7
- const path_1 = require("path");
8
- const true_case_path_1 = require("true-case-path");
9
- const routes_check_1 = require("./routes-check");
10
- jest.mock("fs", () => {
11
- // Access the actual fs module so we can spread it
12
- const actualFs = jest.requireActual("fs");
13
- return {
14
- ...actualFs,
15
- // We'll mock only the function(s) we need
16
- lstatSync: jest.fn(),
17
- };
18
- });
19
- jest.mock("true-case-path", () => ({
20
- trueCasePathSync: jest.fn(),
21
- }));
22
- const mockTrueCasePathSync = true_case_path_1.trueCasePathSync;
23
- /**
24
- * Helper function to mock:
25
- * 1) Which file paths "exist"
26
- * 2) Which directory paths "exist"
27
- */
28
- function mockFileExists(knownPaths, knownDirs = []) {
29
- // Mock the trueCasePathSync behavior (our fileExists check)
30
- mockTrueCasePathSync.mockImplementation((inputPath) => {
31
- if (knownPaths.includes(inputPath)) {
32
- return inputPath; // indicates file exists
33
- }
34
- throw new Error(`Path not found: ${inputPath}`);
35
- });
36
- // Mock lstatSync for directory checks
37
- fs_1.default.lstatSync.mockImplementation((inputPath) => {
38
- if (knownDirs.includes(inputPath)) {
39
- return { isDirectory: () => true };
40
- }
41
- // if it's in knownPaths but not in knownDirs => treat it as a file
42
- if (knownPaths.includes(inputPath)) {
43
- return { isDirectory: () => false };
44
- }
45
- // otherwise, it's not found => throw
46
- throw new Error(`No such file or directory: ${inputPath}`);
47
- });
48
- }
49
- const rootDirPages = (0, path_1.join)(__dirname, "..", "..", "src", "pages");
50
- const rootDirApp = (0, path_1.join)(__dirname, "..", "..", "src", "app");
51
- describe("routesCheck", () => {
52
- beforeEach(() => {
53
- jest.clearAllMocks();
54
- });
55
- it("should handle multiple valid routes at once (pages + app)", () => {
56
- /**
57
- * 1) /product => pages/product/index.tsx
58
- * 2) /product/edit => pages/product/edit.tsx
59
- * 3) /product/[id] => pages/product/[id].tsx
60
- * 4) /dashboard => app/dashboard/page.tsx
61
- * 5) /settings/privacy => app/settings/privacy/page.tsx
62
- */
63
- mockFileExists([
64
- // pages
65
- (0, path_1.join)(rootDirPages, "product", "index.tsx"),
66
- (0, path_1.join)(rootDirPages, "product", "edit.tsx"),
67
- (0, path_1.join)(rootDirPages, "product", "[id].tsx"),
68
- // app
69
- (0, path_1.join)(rootDirApp, "dashboard", "page.tsx"),
70
- (0, path_1.join)(rootDirApp, "settings", "privacy", "page.tsx"),
71
- ], [
72
- // directories
73
- (0, path_1.join)(rootDirPages, "product"),
74
- (0, path_1.join)(rootDirApp, "dashboard"),
75
- (0, path_1.join)(rootDirApp, "settings"),
76
- (0, path_1.join)(rootDirApp, "settings", "privacy"),
77
- ]);
78
- expect(() => (0, routes_check_1.routesCheck)({
79
- product: { path: "/product" },
80
- productEdit: { path: "/product/edit" },
81
- productDetail: { path: "/product/[id]" },
82
- dashboard: { path: "/dashboard" },
83
- privacy: { path: "/settings/privacy" },
84
- }, { shouldProcessExit: false })).not.toThrow();
85
- });
86
- it("should fail when invalid routes are provided", () => {
87
- /**
88
- * We'll test multiple routes that do not exist in either pages or app:
89
- * 1) /unknown
90
- * 2) /product/unknown
91
- */
92
- // Intentionally not mocking any matching paths here
93
- mockFileExists([]);
94
- expect(() => (0, routes_check_1.routesCheck)({
95
- unknown: { path: "/unknown" },
96
- productUnknown: { path: "/product/unknown" },
97
- }, { shouldProcessExit: false })).toThrow();
98
- });
99
- it("Pages folder: should detect collisions (file + folder) in multiple segments", () => {
100
- /**
101
- * Testing collision scenarios together:
102
- * 1) /payments/export in pages => collision with payments.tsx + payments/ folder
103
- */
104
- mockFileExists([
105
- // pages collisions
106
- (0, path_1.join)(rootDirPages, "payments.tsx"),
107
- (0, path_1.join)(rootDirPages, "payments", "export.tsx"),
108
- ], [(0, path_1.join)(rootDirPages, "payments")]);
109
- expect(() => (0, routes_check_1.routesCheck)({
110
- paymentsExport: { path: "/payments/export" },
111
- }, { shouldProcessExit: false })).toThrow();
112
- });
113
- it("App folder: should detect that page.tsx is not created for the route", () => {
114
- /**
115
- * Testing collision scenarios together:
116
- * 1) /admin/analytics in app => collision with analytics.tsx + analytics/ folder
117
- */
118
- mockFileExists([
119
- // app collisions
120
- (0, path_1.join)(rootDirApp, "admin", "analytics.tsx"),
121
- ], [(0, path_1.join)(rootDirApp, "admin"), (0, path_1.join)(rootDirApp, "admin", "analytics")]);
122
- expect(() => (0, routes_check_1.routesCheck)({
123
- adminAnalytics: {
124
- path: "/admin/analytics",
125
- },
126
- }, {
127
- shouldProcessExit: false,
128
- })).toThrow();
129
- });
130
- });