@korioinc/next-core 2.0.24 → 2.0.25

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.
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/auth/routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgCxD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAmB1F"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/auth/routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqCxD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAmB1F"}
@@ -14,13 +14,16 @@ function isPathMatching(path, patterns) {
14
14
  }
15
15
  // 경로에서 locale 접두사를 제거하는 함수
16
16
  function removeLocalePrefix(pathname) {
17
- // Check if pathname starts with any locale
18
- for (const locale of locales) {
19
- if (pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`) {
20
- return pathname.slice(`/${locale}`.length) || '/';
21
- }
17
+ const pathMatch = pathname.match(/^\/([^\\/]+)(?:\/|$)/);
18
+ const localeSegment = pathMatch?.[1];
19
+ if (!localeSegment) {
20
+ return pathname;
21
+ }
22
+ const canonicalLocale = locales.find((locale) => locale.toLowerCase() === localeSegment.toLowerCase());
23
+ if (!canonicalLocale) {
24
+ return pathname;
22
25
  }
23
- return pathname;
26
+ return pathname.replace(new RegExp(`^/${canonicalLocale}(?=/|$)`, 'i'), '') || '/';
24
27
  }
25
28
  export async function handleAuthRouting(request) {
26
29
  const config = getConfig();
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/i18n/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsC7D;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAyDD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,yBAyEvD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAShE;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkB9F;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBvG;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,UAY7E;AAsBD,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAY,0BAwB3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK1D;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CAMvE;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU1D;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAIpF;AAED,KAAK,aAAa,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7C,eAAe,GACf,uBAAuB,GACvB,SAAS,CAAC;AAiDd,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAErF;AAED,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIxE;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC,CAKnE"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/i18n/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsC7D;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAiGD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,yBAsEvD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAShE;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW9F;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBvG;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,UAY7E;AAsBD,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAY,0BAwB3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK1D;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CAMvE;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU1D;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAIpF;AAED,KAAK,aAAa,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7C,eAAe,GACf,uBAAuB,GACvB,SAAS,CAAC;AAiDd,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAErF;AAED,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIxE;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC,CAKnE"}
@@ -47,11 +47,25 @@ const setCookie = (response, locale) => {
47
47
  });
48
48
  return response;
49
49
  };
50
+ const getCanonicalLocale = (locale) => {
51
+ if (!locale) {
52
+ return undefined;
53
+ }
54
+ return locales.find((supportedLocale) => supportedLocale.toLowerCase() === locale.toLowerCase());
55
+ };
56
+ const getPathLocaleSegment = (pathname) => {
57
+ const pathMatch = pathname.match(/^\/([^\\/]+)(?:\/|$)/);
58
+ return pathMatch?.[1];
59
+ };
60
+ const replacePathLocale = (pathname, locale) => {
61
+ return pathname.replace(/^\/[^\\/]+(?=\/|$)/, `/${locale}`);
62
+ };
50
63
  /**
51
64
  * URL에서 locale 부분을 제거하는 헬퍼 함수
52
65
  */
53
66
  const removeLocaleFromPath = (pathname, locale) => {
54
- return pathname.replace(`/${locale}`, '') || '/';
67
+ const localePattern = new RegExp(`^/${locale}(?=/|$)`, 'i');
68
+ return pathname.replace(localePattern, '') || '/';
55
69
  };
56
70
  /**
57
71
  * URL에서 locale을 제거하고 리다이렉트하는 함수
@@ -67,6 +81,17 @@ const redirectWithoutLocale = (request, pathname, locale, shouldSetCookie = fals
67
81
  }
68
82
  return response;
69
83
  };
84
+ const redirectWithLocale = (request, pathname, locale, shouldSetCookie = false) => {
85
+ const newPathname = replacePathLocale(pathname, locale);
86
+ const url = new URL(newPathname, request.url);
87
+ url.search = request.nextUrl.search;
88
+ url.hash = request.nextUrl.hash;
89
+ const response = setRoutingHeaders(NextResponse.redirect(url, 302), newPathname, request.nextUrl.search);
90
+ if (shouldSetCookie) {
91
+ setCookie(response, locale);
92
+ }
93
+ return response;
94
+ };
70
95
  /**
71
96
  * 내부적으로 locale을 처리하는 rewrite 함수
72
97
  */
@@ -79,24 +104,22 @@ const rewriteWithLocale = (request, pathname, locale) => {
79
104
  };
80
105
  export function handleLocaleRouting(request) {
81
106
  const { pathname } = request.nextUrl;
82
- const cookies = request.cookies;
83
- const localeCookie = cookies.get(LOCALE_COOKIE_NAME);
107
+ const localeCookie = request.cookies.get(LOCALE_COOKIE_NAME);
108
+ const cookieLocale = getCanonicalLocale(localeCookie?.value);
84
109
  const defaultFallback = DEFAULT_FALLBACK();
85
110
  // 1. URL에서 locale 감지
86
- let pathLocale;
87
- const pathnameHasLocale = locales.some((locale) => {
88
- if (pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`) {
89
- pathLocale = locale;
90
- return true;
91
- }
92
- return false;
93
- });
111
+ const pathLocaleSegment = getPathLocaleSegment(pathname);
112
+ const pathLocale = getCanonicalLocale(pathLocaleSegment);
113
+ const pathnameHasLocale = pathLocale !== undefined;
94
114
  // 2. URL에 locale이 포함된 경우
95
115
  if (pathnameHasLocale) {
116
+ if (pathLocaleSegment !== pathLocale) {
117
+ return redirectWithLocale(request, pathname, pathLocale, cookieLocale !== pathLocale);
118
+ }
96
119
  // 이미 path에 locale이 있을 때
97
120
  const response = setRoutingHeaders(NextResponse.next(), pathname, request.nextUrl.search);
98
121
  // 쿠키 값이 path locale과 다를 경우
99
- if (localeCookie?.value !== pathLocale) {
122
+ if (cookieLocale !== pathLocale) {
100
123
  // 기본 locale인 경우 URL에서 제거
101
124
  if (LOCALE_PREFIX === 'as-needed' && pathLocale === defaultFallback) {
102
125
  return redirectWithoutLocale(request, pathname, pathLocale, true);
@@ -112,14 +135,14 @@ export function handleLocaleRouting(request) {
112
135
  }
113
136
  // 3. URL에 locale이 없는 경우
114
137
  // 쿠키가 기본 locale이고, as-needed 설정일 때 내부적으로만 locale 처리
115
- if (LOCALE_PREFIX === 'as-needed' && localeCookie?.value === defaultFallback) {
138
+ if (LOCALE_PREFIX === 'as-needed' && cookieLocale === defaultFallback) {
116
139
  return rewriteWithLocale(request, pathname, defaultFallback);
117
140
  }
118
141
  // 4. 적절한 locale 감지 및 적용
119
142
  const defaultLocale = getRequestLocale(request.headers);
120
143
  let locale = getCookieLocale(request.headers, pathname) || defaultLocale;
121
- const findLocale = match([locale], locales, defaultLocale);
122
- if (locale !== findLocale) {
144
+ const findLocale = getCanonicalLocale(match([locale], locales, defaultLocale));
145
+ if (findLocale && locale !== findLocale) {
123
146
  locale = findLocale;
124
147
  }
125
148
  // 기본 locale이고 as-needed 설정인 경우 URL에는 포함하지 않음
@@ -142,24 +165,17 @@ export function getRequestLocale(requestHeaders) {
142
165
  headers: { 'accept-language': langHeader },
143
166
  }).languages(locales.slice());
144
167
  const activeLocale = languages[0] || DEFAULT_FALLBACK();
145
- return activeLocale;
168
+ return getCanonicalLocale(activeLocale) || activeLocale;
146
169
  }
147
170
  export function getCookieLocale(requestHeaders, pathname) {
148
- const cookieLocale = getCookieFromHeaders(requestHeaders, LOCALE_COOKIE_NAME);
171
+ const cookieLocale = getCanonicalLocale(getCookieFromHeaders(requestHeaders, LOCALE_COOKIE_NAME));
149
172
  if (cookieLocale) {
150
173
  return cookieLocale;
151
174
  }
152
- let pathLocale = undefined;
153
- if (pathname) {
154
- const pathMatch = pathname.match(/^\/([^\\/]+)(?:\/|$)/);
155
- if (pathMatch && pathMatch[1]) {
156
- const potentialLocale = pathMatch[1];
157
- if (Array.isArray(locales) && locales.includes(potentialLocale)) {
158
- pathLocale = potentialLocale;
159
- }
160
- }
175
+ if (!pathname) {
176
+ return undefined;
161
177
  }
162
- return pathLocale;
178
+ return getCanonicalLocale(getPathLocaleSegment(pathname));
163
179
  }
164
180
  export function getChangeLocalePath(pathname, currentLocale, nextLocale) {
165
181
  // Create the new path based on whether the current path has a locale segment or not
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korioinc/next-core",
3
- "version": "2.0.24",
3
+ "version": "2.0.25",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./ads": {
@@ -79,7 +79,8 @@
79
79
  "tsc-alias": "^1.8.16",
80
80
  "tw-animate-css": "^1.4.0",
81
81
  "typescript": "^5.9.3",
82
- "@korioinc/next-configs": "2.0.24"
82
+ "vitest": "^4.0.18",
83
+ "@korioinc/next-configs": "2.0.25"
83
84
  },
84
85
  "dependencies": {
85
86
  "@floating-ui/react": "^0.27.19",
@@ -94,7 +95,7 @@
94
95
  "schema-dts": "^1.1.5",
95
96
  "tailwind-merge": "^3.5.0",
96
97
  "valtio": "^2.3.1",
97
- "@korioinc/next-conf": "2.0.24"
98
+ "@korioinc/next-conf": "2.0.25"
98
99
  },
99
100
  "publishConfig": {
100
101
  "access": "public",
@@ -119,6 +120,7 @@
119
120
  "scripts": {
120
121
  "build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json && cp -r src/styles dist/ && cp -r src/types dist/",
121
122
  "dev": "tsc -p tsconfig.json -w & tsc-alias -p tsconfig.json -w",
123
+ "test": "vitest run --config vitest.config.ts",
122
124
  "lint": "eslint . --max-warnings 0",
123
125
  "generate:component": "turbo gen react-component"
124
126
  }