@hyeonqyu/typed-router-next 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/dist/index.d.mts CHANGED
@@ -3,7 +3,10 @@ import * as react from 'react';
3
3
  import { ComponentProps } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { BaseMetadata, PartialRouteTree, RouteTree, RoutePathname } from '@hyeonqyu/typed-router-core';
6
- export * from '@hyeonqyu/typed-router-core';
6
+ export { BaseMetadata, PartialRouteTree, RouteNode, RouteNodeMetadata, RoutePathname, RouteTree } from '@hyeonqyu/typed-router-core';
7
+
8
+ type QueryValue = string | number | boolean | readonly (string | number | boolean)[];
9
+ type QueryParams = Record<string, QueryValue>;
7
10
 
8
11
  type NextLinkProps = ComponentProps<typeof Link>;
9
12
  type TypedLinkProps<TPathname extends string = string> = Omit<NextLinkProps, 'href'> & {
@@ -22,11 +25,15 @@ declare const createAppRoutes: <TMetadata extends BaseMetadata, TContext>() => <
22
25
  refresh: () => void;
23
26
  push: (href: RoutePathname<TMetadata, TContext, TRouteTree>, options?: {
24
27
  scroll?: boolean;
25
- } | undefined) => void;
28
+ query?: QueryParams;
29
+ }) => void;
26
30
  replace: (href: RoutePathname<TMetadata, TContext, TRouteTree>, options?: {
27
31
  scroll?: boolean;
28
- } | undefined) => void;
29
- prefetch: (href: RoutePathname<TMetadata, TContext, TRouteTree>) => void;
32
+ query?: QueryParams;
33
+ }) => void;
34
+ prefetch: (href: RoutePathname<TMetadata, TContext, TRouteTree>, options?: {
35
+ query?: QueryParams | undefined;
36
+ }) => void;
30
37
  };
31
38
  useTypedPathname: () => RoutePathname<TMetadata, TContext, TRouteTree>;
32
39
  _types: {
package/dist/index.d.ts CHANGED
@@ -3,7 +3,10 @@ import * as react from 'react';
3
3
  import { ComponentProps } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { BaseMetadata, PartialRouteTree, RouteTree, RoutePathname } from '@hyeonqyu/typed-router-core';
6
- export * from '@hyeonqyu/typed-router-core';
6
+ export { BaseMetadata, PartialRouteTree, RouteNode, RouteNodeMetadata, RoutePathname, RouteTree } from '@hyeonqyu/typed-router-core';
7
+
8
+ type QueryValue = string | number | boolean | readonly (string | number | boolean)[];
9
+ type QueryParams = Record<string, QueryValue>;
7
10
 
8
11
  type NextLinkProps = ComponentProps<typeof Link>;
9
12
  type TypedLinkProps<TPathname extends string = string> = Omit<NextLinkProps, 'href'> & {
@@ -22,11 +25,15 @@ declare const createAppRoutes: <TMetadata extends BaseMetadata, TContext>() => <
22
25
  refresh: () => void;
23
26
  push: (href: RoutePathname<TMetadata, TContext, TRouteTree>, options?: {
24
27
  scroll?: boolean;
25
- } | undefined) => void;
28
+ query?: QueryParams;
29
+ }) => void;
26
30
  replace: (href: RoutePathname<TMetadata, TContext, TRouteTree>, options?: {
27
31
  scroll?: boolean;
28
- } | undefined) => void;
29
- prefetch: (href: RoutePathname<TMetadata, TContext, TRouteTree>) => void;
32
+ query?: QueryParams;
33
+ }) => void;
34
+ prefetch: (href: RoutePathname<TMetadata, TContext, TRouteTree>, options?: {
35
+ query?: QueryParams | undefined;
36
+ }) => void;
30
37
  };
31
38
  useTypedPathname: () => RoutePathname<TMetadata, TContext, TRouteTree>;
32
39
  _types: {
package/dist/index.js CHANGED
@@ -17,7 +17,37 @@ var createTypedPathname = () => {
17
17
  return pathname;
18
18
  };
19
19
  };
20
+
21
+ // ../core/src/object.utils.ts
22
+ var toQueryString = (query, options = { includeQuestionMark: true }) => {
23
+ const params = [];
24
+ Object.entries(query).forEach(([key, value]) => {
25
+ if (value === void 0 || value === null) {
26
+ return;
27
+ }
28
+ const encodedKey = encodeURIComponent(key);
29
+ if (Array.isArray(value)) {
30
+ value.forEach((item) => {
31
+ if (item !== void 0 && item !== null) {
32
+ params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);
33
+ }
34
+ });
35
+ } else {
36
+ params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);
37
+ }
38
+ });
39
+ if (params.length === 0) {
40
+ return "";
41
+ }
42
+ const queryString = params.join("&");
43
+ return options.includeQuestionMark ? `?${queryString}` : queryString;
44
+ };
45
+
46
+ // src/router.hooks.ts
20
47
  var createTypedRouter = () => {
48
+ const getHrefWithQuery = (href, query) => {
49
+ return href + toQueryString(query || {}, { includeQuestionMark: true });
50
+ };
21
51
  return () => {
22
52
  const router = navigation.useRouter();
23
53
  return {
@@ -25,13 +55,13 @@ var createTypedRouter = () => {
25
55
  forward: router.forward,
26
56
  refresh: router.refresh,
27
57
  push: (href, options) => {
28
- return router.push(href, options);
58
+ return router.push(getHrefWithQuery(href, options?.query), options);
29
59
  },
30
60
  replace: (href, options) => {
31
- return router.replace(href, options);
61
+ return router.replace(getHrefWithQuery(href, options?.query), options);
32
62
  },
33
- prefetch: (href) => {
34
- return router.prefetch(href);
63
+ prefetch: (href, options) => {
64
+ return router.prefetch(getHrefWithQuery(href, options?.query));
35
65
  }
36
66
  };
37
67
  };
@@ -43,6 +73,8 @@ var createTypedLink = () => {
43
73
  TypedLink.displayName = "TypedLink";
44
74
  return TypedLink;
45
75
  };
76
+
77
+ // src/index.tsx
46
78
  var createAppRoutes = () => {
47
79
  return (appRoutes) => {
48
80
  const { _types, ...rest } = typedRouterCore.createAppRoutes()(appRoutes);
@@ -60,11 +92,5 @@ var createAppRoutes = () => {
60
92
  };
61
93
 
62
94
  exports.createAppRoutes = createAppRoutes;
63
- Object.keys(typedRouterCore).forEach(function (k) {
64
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
65
- enumerable: true,
66
- get: function () { return typedRouterCore[k]; }
67
- });
68
- });
69
95
  //# sourceMappingURL=index.js.map
70
96
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pathname.hooks.ts","../src/router.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["usePathname","useRouter","forwardRef","jsx","Link","createAppRoutesCore"],"mappings":";;;;;;;;;;;;;AAEO,IAAM,sBAAsB,MAAyC;AAC1E,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,WAAWA,sBAAA,EAAY;AAC7B,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACF,CAAA;ACLO,IAAM,oBAAoB,MAAyC;AACxE,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,SAASC,oBAAA,EAAU;AAEzB,IAAA,OAAO;AAAA,MACL,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,IAAA,EAAM,CAAC,IAAA,EAAiB,OAAA,KAAmC;AACzD,QAAA,OAAO,MAAA,CAAO,IAAA,CAAK,IAAA,EAAM,OAAO,CAAA;AAAA,MAClC,CAAA;AAAA,MACA,OAAA,EAAS,CAAC,IAAA,EAAiB,OAAA,KAAmC;AAC5D,QAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA;AAAA,MACrC,CAAA;AAAA,MACA,QAAA,EAAU,CAAC,IAAA,KAAoB;AAC7B,QAAA,OAAO,MAAA,CAAO,SAAS,IAAI,CAAA;AAAA,MAC7B;AAAA,KACF;AAAA,EACF,CAAA;AACF,CAAA;ACLO,IAAM,kBAAkB,MAAyC;AACtE,EAAA,MAAM,SAAA,GAAYC,gBAAA,CAAiE,CAAC,KAAA,EAAO,GAAA,KAAQ;AACjG,IAAA,uBAAOC,cAAA,CAACC,qBAAA,EAAA,EAAK,GAAA,EAAW,GAAI,KAAA,EAAyB,CAAA;AAAA,EACvD,CAAC,CAAA;AAED,EAAA,SAAA,CAAU,WAAA,GAAc,WAAA;AAExB,EAAA,OAAO,SAAA;AACT,CAAA;ACbO,IAAM,kBAAkB,MAAgD;AAC7E,EAAA,OAAO,CAA2D,SAAA,KAAuE;AACvI,IAAA,MAAM,EAAE,MAAA,EAAQ,GAAG,MAAK,GAAIC,+BAAA,GAA2C,SAAS,CAAA;AAGhF,IAAA,MAAM,YAAY,eAAA,EAA0B;AAC5C,IAAA,MAAM,iBAAiB,iBAAA,EAA4B;AACnD,IAAA,MAAM,mBAAmB,mBAAA,EAA8B;AAEvD,IAAA,OAAO;AAAA,MACL,GAAG,IAAA;AAAA,MACH,SAAA;AAAA,MACA,cAAA;AAAA,MACA,gBAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA;AACF","file":"index.js","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { useRouter } from 'next/navigation';\n\nexport const createTypedRouter = <TPathname extends string = string>() => {\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: (href: TPathname, options?: { scroll?: boolean }) => {\n return router.push(href, options);\n },\n replace: (href: TPathname, options?: { scroll?: boolean }) => {\n return router.replace(href, options);\n },\n prefetch: (href: TPathname) => {\n return router.prefetch(href);\n },\n };\n };\n};\n","import Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | {\n pathname: TPathname;\n query?: Record<string, string | number | boolean | readonly (string | number | boolean)[]>;\n hash?: string;\n };\n};\n\nexport const createTypedLink = <TPathname extends string = string>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname>>((props, ref) => {\n return <Link ref={ref} {...(props as NextLinkProps)} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n createAppRoutes as createAppRoutesCore,\n type BaseMetadata,\n type PartialRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createTypedPathname } from 'packages/next/src/pathname.hooks';\nimport { createTypedRouter } from 'packages/next/src/router.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { _types, ...rest } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n const TypedLink = createTypedLink<Pathname>();\n const useTypedRouter = createTypedRouter<Pathname>();\n const useTypedPathname = createTypedPathname<Pathname>();\n\n return {\n ...rest,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n _types,\n };\n };\n};\n\nexport * from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n"]}
1
+ {"version":3,"sources":["../src/pathname.hooks.ts","../../core/src/object.utils.ts","../src/router.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["usePathname","useRouter","forwardRef","jsx","Link","createAppRoutesCore"],"mappings":"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","file":"index.js","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { Paths, PathValue } from './path.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type QueryValue = string | number | boolean | readonly (string | number | boolean)[];\nexport type QueryParams = Record<string, QueryValue>;\nexport type ToQueryStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toQueryString = (query: QueryParams, options: ToQueryStringOptions = { includeQuestionMark: true }): string => {\n const params: string[] = [];\n\n Object.entries(query).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const queryString = params.join('&');\n return options.includeQuestionMark ? `?${queryString}` : queryString;\n};\n","import { useRouter } from 'next/navigation';\nimport { QueryParams, toQueryString } from 'packages/core/src/object.utils';\n\ntype NavigateOptions = {\n scroll?: boolean;\n query?: QueryParams;\n};\n\ntype PrefetchOptions = Pick<NavigateOptions, 'query'>;\n\nexport const createTypedRouter = <TPathname extends string = string>() => {\n const getHrefWithQuery = (href: TPathname, query?: QueryParams) => {\n return href + toQueryString(query || {}, { includeQuestionMark: true });\n };\n\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: (href: TPathname, options?: NavigateOptions) => {\n return router.push(getHrefWithQuery(href, options?.query), options);\n },\n replace: (href: TPathname, options?: NavigateOptions) => {\n return router.replace(getHrefWithQuery(href, options?.query), options);\n },\n prefetch: (href: TPathname, options?: PrefetchOptions) => {\n return router.prefetch(getHrefWithQuery(href, options?.query));\n },\n };\n };\n};\n","import Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | {\n pathname: TPathname;\n query?: Record<string, string | number | boolean | readonly (string | number | boolean)[]>;\n hash?: string;\n };\n};\n\nexport const createTypedLink = <TPathname extends string = string>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname>>((props, ref) => {\n return <Link ref={ref} {...(props as NextLinkProps)} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n createAppRoutes as createAppRoutesCore,\n type BaseMetadata,\n type PartialRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createTypedPathname } from 'packages/next/src/pathname.hooks';\nimport { createTypedRouter } from 'packages/next/src/router.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { _types, ...rest } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n const TypedLink = createTypedLink<Pathname>();\n const useTypedRouter = createTypedRouter<Pathname>();\n const useTypedPathname = createTypedPathname<Pathname>();\n\n return {\n ...rest,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n _types,\n };\n };\n};\n\nexport type { BaseMetadata, PartialRouteTree, RouteNode, RouteNodeMetadata, RoutePathname, RouteTree } from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { createAppRoutes as createAppRoutes$1 } from '@hyeonqyu/typed-router-core';
2
- export * from '@hyeonqyu/typed-router-core';
3
2
  import { usePathname, useRouter } from 'next/navigation';
4
3
  import Link from 'next/link';
5
4
  import { forwardRef } from 'react';
@@ -12,7 +11,37 @@ var createTypedPathname = () => {
12
11
  return pathname;
13
12
  };
14
13
  };
14
+
15
+ // ../core/src/object.utils.ts
16
+ var toQueryString = (query, options = { includeQuestionMark: true }) => {
17
+ const params = [];
18
+ Object.entries(query).forEach(([key, value]) => {
19
+ if (value === void 0 || value === null) {
20
+ return;
21
+ }
22
+ const encodedKey = encodeURIComponent(key);
23
+ if (Array.isArray(value)) {
24
+ value.forEach((item) => {
25
+ if (item !== void 0 && item !== null) {
26
+ params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);
27
+ }
28
+ });
29
+ } else {
30
+ params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);
31
+ }
32
+ });
33
+ if (params.length === 0) {
34
+ return "";
35
+ }
36
+ const queryString = params.join("&");
37
+ return options.includeQuestionMark ? `?${queryString}` : queryString;
38
+ };
39
+
40
+ // src/router.hooks.ts
15
41
  var createTypedRouter = () => {
42
+ const getHrefWithQuery = (href, query) => {
43
+ return href + toQueryString(query || {}, { includeQuestionMark: true });
44
+ };
16
45
  return () => {
17
46
  const router = useRouter();
18
47
  return {
@@ -20,13 +49,13 @@ var createTypedRouter = () => {
20
49
  forward: router.forward,
21
50
  refresh: router.refresh,
22
51
  push: (href, options) => {
23
- return router.push(href, options);
52
+ return router.push(getHrefWithQuery(href, options?.query), options);
24
53
  },
25
54
  replace: (href, options) => {
26
- return router.replace(href, options);
55
+ return router.replace(getHrefWithQuery(href, options?.query), options);
27
56
  },
28
- prefetch: (href) => {
29
- return router.prefetch(href);
57
+ prefetch: (href, options) => {
58
+ return router.prefetch(getHrefWithQuery(href, options?.query));
30
59
  }
31
60
  };
32
61
  };
@@ -38,6 +67,8 @@ var createTypedLink = () => {
38
67
  TypedLink.displayName = "TypedLink";
39
68
  return TypedLink;
40
69
  };
70
+
71
+ // src/index.tsx
41
72
  var createAppRoutes = () => {
42
73
  return (appRoutes) => {
43
74
  const { _types, ...rest } = createAppRoutes$1()(appRoutes);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pathname.hooks.ts","../src/router.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["createAppRoutesCore"],"mappings":";;;;;;;;AAEO,IAAM,sBAAsB,MAAyC;AAC1E,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACF,CAAA;ACLO,IAAM,oBAAoB,MAAyC;AACxE,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,SAAS,SAAA,EAAU;AAEzB,IAAA,OAAO;AAAA,MACL,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,IAAA,EAAM,CAAC,IAAA,EAAiB,OAAA,KAAmC;AACzD,QAAA,OAAO,MAAA,CAAO,IAAA,CAAK,IAAA,EAAM,OAAO,CAAA;AAAA,MAClC,CAAA;AAAA,MACA,OAAA,EAAS,CAAC,IAAA,EAAiB,OAAA,KAAmC;AAC5D,QAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA;AAAA,MACrC,CAAA;AAAA,MACA,QAAA,EAAU,CAAC,IAAA,KAAoB;AAC7B,QAAA,OAAO,MAAA,CAAO,SAAS,IAAI,CAAA;AAAA,MAC7B;AAAA,KACF;AAAA,EACF,CAAA;AACF,CAAA;ACLO,IAAM,kBAAkB,MAAyC;AACtE,EAAA,MAAM,SAAA,GAAY,UAAA,CAAiE,CAAC,KAAA,EAAO,GAAA,KAAQ;AACjG,IAAA,uBAAO,GAAA,CAAC,IAAA,EAAA,EAAK,GAAA,EAAW,GAAI,KAAA,EAAyB,CAAA;AAAA,EACvD,CAAC,CAAA;AAED,EAAA,SAAA,CAAU,WAAA,GAAc,WAAA;AAExB,EAAA,OAAO,SAAA;AACT,CAAA;ACbO,IAAM,kBAAkB,MAAgD;AAC7E,EAAA,OAAO,CAA2D,SAAA,KAAuE;AACvI,IAAA,MAAM,EAAE,MAAA,EAAQ,GAAG,MAAK,GAAIA,iBAAA,GAA2C,SAAS,CAAA;AAGhF,IAAA,MAAM,YAAY,eAAA,EAA0B;AAC5C,IAAA,MAAM,iBAAiB,iBAAA,EAA4B;AACnD,IAAA,MAAM,mBAAmB,mBAAA,EAA8B;AAEvD,IAAA,OAAO;AAAA,MACL,GAAG,IAAA;AAAA,MACH,SAAA;AAAA,MACA,cAAA;AAAA,MACA,gBAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA;AACF","file":"index.mjs","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { useRouter } from 'next/navigation';\n\nexport const createTypedRouter = <TPathname extends string = string>() => {\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: (href: TPathname, options?: { scroll?: boolean }) => {\n return router.push(href, options);\n },\n replace: (href: TPathname, options?: { scroll?: boolean }) => {\n return router.replace(href, options);\n },\n prefetch: (href: TPathname) => {\n return router.prefetch(href);\n },\n };\n };\n};\n","import Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | {\n pathname: TPathname;\n query?: Record<string, string | number | boolean | readonly (string | number | boolean)[]>;\n hash?: string;\n };\n};\n\nexport const createTypedLink = <TPathname extends string = string>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname>>((props, ref) => {\n return <Link ref={ref} {...(props as NextLinkProps)} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n createAppRoutes as createAppRoutesCore,\n type BaseMetadata,\n type PartialRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createTypedPathname } from 'packages/next/src/pathname.hooks';\nimport { createTypedRouter } from 'packages/next/src/router.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { _types, ...rest } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n const TypedLink = createTypedLink<Pathname>();\n const useTypedRouter = createTypedRouter<Pathname>();\n const useTypedPathname = createTypedPathname<Pathname>();\n\n return {\n ...rest,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n _types,\n };\n };\n};\n\nexport * from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n"]}
1
+ {"version":3,"sources":["../src/pathname.hooks.ts","../../core/src/object.utils.ts","../src/router.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["createAppRoutesCore"],"mappings":"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","file":"index.mjs","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { Paths, PathValue } from './path.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type QueryValue = string | number | boolean | readonly (string | number | boolean)[];\nexport type QueryParams = Record<string, QueryValue>;\nexport type ToQueryStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toQueryString = (query: QueryParams, options: ToQueryStringOptions = { includeQuestionMark: true }): string => {\n const params: string[] = [];\n\n Object.entries(query).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const queryString = params.join('&');\n return options.includeQuestionMark ? `?${queryString}` : queryString;\n};\n","import { useRouter } from 'next/navigation';\nimport { QueryParams, toQueryString } from 'packages/core/src/object.utils';\n\ntype NavigateOptions = {\n scroll?: boolean;\n query?: QueryParams;\n};\n\ntype PrefetchOptions = Pick<NavigateOptions, 'query'>;\n\nexport const createTypedRouter = <TPathname extends string = string>() => {\n const getHrefWithQuery = (href: TPathname, query?: QueryParams) => {\n return href + toQueryString(query || {}, { includeQuestionMark: true });\n };\n\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: (href: TPathname, options?: NavigateOptions) => {\n return router.push(getHrefWithQuery(href, options?.query), options);\n },\n replace: (href: TPathname, options?: NavigateOptions) => {\n return router.replace(getHrefWithQuery(href, options?.query), options);\n },\n prefetch: (href: TPathname, options?: PrefetchOptions) => {\n return router.prefetch(getHrefWithQuery(href, options?.query));\n },\n };\n };\n};\n","import Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | {\n pathname: TPathname;\n query?: Record<string, string | number | boolean | readonly (string | number | boolean)[]>;\n hash?: string;\n };\n};\n\nexport const createTypedLink = <TPathname extends string = string>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname>>((props, ref) => {\n return <Link ref={ref} {...(props as NextLinkProps)} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n createAppRoutes as createAppRoutesCore,\n type BaseMetadata,\n type PartialRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createTypedPathname } from 'packages/next/src/pathname.hooks';\nimport { createTypedRouter } from 'packages/next/src/router.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { _types, ...rest } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n const TypedLink = createTypedLink<Pathname>();\n const useTypedRouter = createTypedRouter<Pathname>();\n const useTypedPathname = createTypedPathname<Pathname>();\n\n return {\n ...rest,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n _types,\n };\n };\n};\n\nexport type { BaseMetadata, PartialRouteTree, RouteNode, RouteNodeMetadata, RoutePathname, RouteTree } from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyeonqyu/typed-router-next",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Type-safe IA-first routing for Next.js",
5
5
  "author": "hyeonQyu <dhk0561@naver.com>",
6
6
  "license": "MIT",