Skip to content

Commit 46427dd

Browse files
authored
Merge pull request #14 from script-development/feat/fs-router
feat: add @script-development/fs-router package
2 parents 4d9489c + 8a8b0ce commit 46427dd

15 files changed

Lines changed: 2243 additions & 0 deletions

package-lock.json

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/router/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# @script-development/fs-router

packages/router/package.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "@script-development/fs-router",
3+
"version": "0.1.0",
4+
"description": "Type-safe router service factory with CRUD navigation, middleware pipeline, and custom components for Vue Router",
5+
"license": "UNLICENSED",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/script-development/fs-packages.git",
9+
"directory": "packages/router"
10+
},
11+
"files": [
12+
"dist"
13+
],
14+
"type": "module",
15+
"main": "./dist/index.cjs",
16+
"module": "./dist/index.mjs",
17+
"types": "./dist/index.d.mts",
18+
"exports": {
19+
".": {
20+
"import": {
21+
"types": "./dist/index.d.mts",
22+
"default": "./dist/index.mjs"
23+
},
24+
"require": {
25+
"types": "./dist/index.d.cts",
26+
"default": "./dist/index.cjs"
27+
}
28+
}
29+
},
30+
"publishConfig": {
31+
"access": "public",
32+
"registry": "https://registry.npmjs.org"
33+
},
34+
"scripts": {
35+
"build": "tsdown",
36+
"typecheck": "tsc --noEmit",
37+
"lint:pkg": "publint && attw --pack",
38+
"test": "vitest run",
39+
"test:coverage": "vitest run --coverage",
40+
"test:mutation": "stryker run"
41+
},
42+
"devDependencies": {
43+
"@vue/test-utils": "^2.4.6",
44+
"happy-dom": "^20.8.9",
45+
"vue": "^3.5.0",
46+
"vue-router": "^4.5.0"
47+
},
48+
"peerDependencies": {
49+
"vue": "^3.5.0",
50+
"vue-router": "^4.5.0"
51+
}
52+
}

packages/router/src/components.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import type { Ref } from "vue";
2+
import type { LocationQueryRaw, RouteLocationNormalizedLoaded, RouteRecordRaw } from "vue-router";
3+
4+
import { computed, defineComponent, h } from "vue";
5+
6+
import type { RouteName, RouterLinkComponent, RouterService, RouterViewComponent } from "./types";
7+
8+
const buildRouteKey = (route: RouteLocationNormalizedLoaded, depth: number): string => {
9+
let key = route.matched[depth].path;
10+
for (const [paramName, paramValue] of Object.entries(route.params)) {
11+
const value = Array.isArray(paramValue) ? paramValue[0] : paramValue;
12+
if (value) key = key.replace(`:${paramName}`, value);
13+
}
14+
15+
return key;
16+
};
17+
18+
export const createRouterView = (
19+
currentRouteRef: Ref<RouteLocationNormalizedLoaded>,
20+
): RouterViewComponent =>
21+
defineComponent<{ depth?: number }>(
22+
({ depth = 0 }) => {
23+
const component = computed(() => {
24+
const matched = currentRouteRef.value.matched[depth];
25+
return matched?.components?.default ?? null;
26+
});
27+
28+
return () => {
29+
if (!component.value) return h("p", ["404"]);
30+
31+
return h(component.value, {
32+
key: buildRouteKey(currentRouteRef.value, depth),
33+
});
34+
};
35+
},
36+
// https://vuejs.org/api/general.html#function-signature
37+
// manual runtime props declaration is currently still needed
38+
{ props: ["depth"] },
39+
);
40+
41+
export const createRouterLink = <Routes extends RouteRecordRaw[]>(
42+
getUrlForRouteName: RouterService<Routes>["getUrlForRouteName"],
43+
goToRoute: RouterService<Routes>["goToRoute"],
44+
): RouterLinkComponent<Routes> =>
45+
defineComponent<{
46+
to: {
47+
name: RouteName<Routes>;
48+
query?: LocationQueryRaw;
49+
id?: number | string;
50+
parentId?: number;
51+
};
52+
}>(
53+
(props, { slots }) =>
54+
() =>
55+
h(
56+
"a",
57+
{
58+
href: getUrlForRouteName(props.to.name, props.to.id, props.to.query, props.to.parentId),
59+
onClick: (event: MouseEvent) => {
60+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
61+
62+
event.preventDefault();
63+
goToRoute(props.to.name, props.to.id, props.to.query, props.to.parentId);
64+
},
65+
},
66+
slots.default?.(),
67+
),
68+
// https://vuejs.org/api/general.html#function-signature
69+
// manual runtime props declaration is currently still needed
70+
{ props: ["to"] },
71+
);

packages/router/src/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export { createRouterService } from "./router";
2+
export {
3+
createStandardRouteConfig,
4+
createCrudRoutes,
5+
createNestedCrudRoutes,
6+
CREATE_PAGE_NAME,
7+
EDIT_PAGE_NAME,
8+
OVERVIEW_PAGE_NAME,
9+
SHOW_PAGE_NAME,
10+
} from "./routes";
11+
export { createRouterView, createRouterLink } from "./components";
12+
export type {
13+
FilterUndefined,
14+
LazyRouteComponent,
15+
OptionalComponent,
16+
ActualRoute,
17+
RouteName,
18+
CreateRouteName,
19+
OverviewRouteName,
20+
EditRouteName,
21+
ShowRouteName,
22+
BeforeRouteMiddleware,
23+
UnregisterMiddleware,
24+
RouterViewComponent,
25+
RouterLinkComponent,
26+
RouterServiceOptions,
27+
RouterService,
28+
CrudRoute,
29+
ParentCrudRoute,
30+
NestedParentCrudRoute,
31+
} from "./types";

0 commit comments

Comments
 (0)