sh-ui-cli 0.59.9 → 0.61.1
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/data/changelog/versions.json +53 -0
- package/data/registry/flutter/widgets/sh_ui_input.dart +0 -79
- package/data/registry/react/components/input/index.module.tsx +0 -70
- package/data/registry/react/components/input/index.tailwind.tsx +0 -53
- package/data/registry/react/components/input/index.tsx +0 -70
- package/data/registry/react/components/input/index.vanilla-extract.tsx +0 -63
- package/data/summaries/react.json +1 -1
- package/package.json +2 -2
- package/src/create/architectures/flat.js +1 -1
- package/src/create/generator.js +717 -26
- package/src/create/index.mjs +1 -1
- package/src/create/plugins/authJwt.js +51 -1
- package/src/create/plugins/nextIntl.js +163 -17
- package/src/create/plugins/sentry.js +43 -23
- package/src/mcp.mjs +2 -2
- package/src/rename-app.mjs +3 -3
- package/templates/flutter-standalone/sh-ui.config.json +0 -1
- package/templates/monorepo/README.md +14 -5
- package/templates/monorepo/packages/eslint-config/flat.js +71 -0
- package/templates/monorepo/packages/eslint-config/fsd.js +0 -21
- package/templates/monorepo/packages/eslint-config/package.json +2 -3
- package/templates/monorepo/packages/typescript-config/package.json +6 -1
- package/templates/monorepo/packages/ui/ui-core/tsconfig.json +1 -1
- package/templates/monorepo/pnpm-workspace.yaml +2 -1
- package/templates/nextjs-app/.env.example +3 -2
- package/templates/nextjs-app/README.md +9 -9
- package/templates/nextjs-app/_arch/flat/app/api/proxy/[...path]/route.ts +1 -1
- package/templates/nextjs-app/_arch/flat/app/layout.tsx +2 -2
- package/templates/nextjs-app/_arch/flat/components/common/PrefetchBoundary/index.tsx +1 -1
- package/templates/nextjs-app/_arch/flat/components/layouts/RootLayout.tsx +2 -0
- package/templates/nextjs-app/_arch/flat/components/providers/GlobalProvider/index.tsx +3 -3
- package/templates/nextjs-app/_arch/flat/components/providers/theme/ThemeProvider.tsx +12 -0
- package/templates/nextjs-app/_arch/flat/eslint.config.js +10 -0
- package/templates/nextjs-app/_arch/flat/lib/api/clientFetch.ts +4 -4
- package/templates/nextjs-app/_arch/flat/lib/api/errorMessages.ts +37 -0
- package/templates/nextjs-app/_arch/flat/lib/hooks/useAppMutation.ts +14 -7
- package/templates/nextjs-app/_arch/flat/lib/test/renderWithProviders.tsx +32 -7
- package/templates/nextjs-app/_arch/flat/lib/utils/formatDate.ts +4 -0
- package/templates/nextjs-app/_arch/flat/lib/utils/formatPrice.ts +13 -5
- package/templates/nextjs-app/_arch/flat/lib/utils/getQueryClient.ts +1 -1
- package/templates/nextjs-app/_arch/flat/tsconfig.json +0 -1
- package/templates/nextjs-app/_arch/fsd/app/api/proxy/[...path]/route.ts +1 -1
- package/templates/nextjs-app/_arch/fsd/app/layout.tsx +2 -2
- package/templates/nextjs-app/_arch/fsd/src/app/layouts/RootLayout.tsx +2 -0
- package/templates/nextjs-app/_arch/fsd/src/app/providers/GlobalProvider/index.tsx +3 -3
- package/templates/nextjs-app/_arch/fsd/src/app/providers/theme/ThemeProvider.tsx +12 -0
- package/templates/nextjs-app/_arch/fsd/src/shared/api/clientFetch.ts +4 -4
- package/templates/nextjs-app/_arch/fsd/src/shared/api/errorMessages.ts +37 -0
- package/templates/nextjs-app/_arch/fsd/src/shared/hooks/useAppMutation.ts +14 -7
- package/templates/nextjs-app/_arch/fsd/src/shared/lib/formatDate.ts +4 -0
- package/templates/nextjs-app/_arch/fsd/src/shared/lib/formatPrice.ts +13 -5
- package/templates/nextjs-app/_arch/fsd/src/shared/lib/getQueryClient.ts +1 -1
- package/templates/nextjs-app/_arch/fsd/src/shared/test/renderWithProviders.tsx +32 -7
- package/templates/nextjs-app/_arch/fsd/src/shared/ui/PrefetchBoundary/index.tsx +1 -1
- package/templates/nextjs-app/vitest.config.ts +4 -0
- package/templates/nextjs-standalone/.env.example +3 -2
- package/templates/nextjs-standalone/_arch/flat/app/api/proxy/[...path]/route.ts +1 -1
- package/templates/nextjs-standalone/_arch/flat/app/layout.tsx +2 -2
- package/templates/nextjs-standalone/_arch/flat/components/common/PrefetchBoundary/index.tsx +1 -1
- package/templates/nextjs-standalone/_arch/flat/components/layouts/RootLayout.tsx +2 -0
- package/templates/nextjs-standalone/_arch/flat/components/providers/GlobalProvider/index.tsx +3 -3
- package/templates/nextjs-standalone/_arch/flat/components/providers/theme/ThemeProvider.tsx +12 -0
- package/templates/nextjs-standalone/_arch/flat/eslint.config.js +123 -0
- package/templates/nextjs-standalone/_arch/flat/lib/api/clientFetch.ts +4 -4
- package/templates/nextjs-standalone/_arch/flat/lib/api/errorMessages.ts +37 -0
- package/templates/nextjs-standalone/_arch/flat/lib/hooks/useAppMutation.ts +14 -7
- package/templates/nextjs-standalone/_arch/flat/lib/test/renderWithProviders.tsx +32 -7
- package/templates/nextjs-standalone/_arch/flat/lib/utils/formatDate.ts +4 -0
- package/templates/nextjs-standalone/_arch/flat/lib/utils/formatPrice.ts +13 -5
- package/templates/nextjs-standalone/_arch/flat/lib/utils/getQueryClient.ts +1 -1
- package/templates/nextjs-standalone/_arch/flat/tsconfig.json +1 -2
- package/templates/nextjs-standalone/_arch/fsd/app/api/proxy/[...path]/route.ts +1 -1
- package/templates/nextjs-standalone/_arch/fsd/app/layout.tsx +2 -2
- package/templates/nextjs-standalone/_arch/fsd/src/app/layouts/RootLayout.tsx +2 -0
- package/templates/nextjs-standalone/_arch/fsd/src/app/providers/GlobalProvider/index.tsx +3 -3
- package/templates/nextjs-standalone/_arch/fsd/src/app/providers/theme/ThemeProvider.tsx +12 -0
- package/templates/nextjs-standalone/_arch/fsd/src/shared/api/clientFetch.ts +4 -4
- package/templates/nextjs-standalone/_arch/fsd/src/shared/api/errorMessages.ts +37 -0
- package/templates/nextjs-standalone/_arch/fsd/src/shared/hooks/useAppMutation.ts +14 -7
- package/templates/nextjs-standalone/_arch/fsd/src/shared/lib/formatDate.ts +4 -0
- package/templates/nextjs-standalone/_arch/fsd/src/shared/lib/formatPrice.ts +13 -5
- package/templates/nextjs-standalone/_arch/fsd/src/shared/lib/getQueryClient.ts +1 -1
- package/templates/nextjs-standalone/_arch/fsd/src/shared/test/renderWithProviders.tsx +32 -7
- package/templates/nextjs-standalone/_arch/fsd/src/shared/ui/PrefetchBoundary/index.tsx +1 -1
- package/templates/nextjs-standalone/eslint.config.js +0 -15
- package/templates/nextjs-standalone/package.json +0 -2
- package/templates/ui-app-template/package.json +2 -2
- package/templates/ui-app-template/postcss.config.mjs +1 -1
- package/templates/monorepo/.eslintrc.js +0 -8
- package/templates/nextjs-app/Dockerfile +0 -11
- package/templates/nextjs-app/_arch/flat/components/providers/theme/ThemeProviders.tsx +0 -12
- package/templates/nextjs-app/_arch/fsd/src/app/providers/theme/ThemeProviders.tsx +0 -12
- package/templates/nextjs-standalone/_arch/flat/components/providers/theme/ThemeProviders.tsx +0 -12
- package/templates/nextjs-standalone/_arch/fsd/src/app/providers/theme/ThemeProviders.tsx +0 -12
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
} from '@tanstack/react-query';
|
|
6
6
|
import { toast } from 'sonner';
|
|
7
7
|
|
|
8
|
-
import { ApiError } from '
|
|
8
|
+
import { ApiError } from '@/src/shared/api/error';
|
|
9
|
+
import { resolveErrorMessage } from '@/src/shared/api/errorMessages';
|
|
9
10
|
|
|
10
11
|
type AppMutationOptions<
|
|
11
12
|
TData = unknown,
|
|
@@ -13,13 +14,19 @@ type AppMutationOptions<
|
|
|
13
14
|
TVariables = void,
|
|
14
15
|
TContext = unknown,
|
|
15
16
|
> = UseMutationOptions<TData, TError, TVariables, TContext> & {
|
|
17
|
+
/** ApiError 가 아니거나 mapping 에 없을 때의 fallback 메시지. */
|
|
16
18
|
errorMessage?: string;
|
|
19
|
+
/** false 면 toast 띄우지 않음. */
|
|
17
20
|
showErrorToast?: boolean;
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
* useMutation 래퍼 — 에러 발생 시
|
|
22
|
-
*
|
|
24
|
+
* useMutation 래퍼 — 에러 발생 시 `resolveErrorMessage` 를 통해 안전한
|
|
25
|
+
* 사용자 facing 메시지를 toast 로 띄운다. backend 가 보낸 raw 메시지를 그대로
|
|
26
|
+
* 띄우지 않고 `errorMessages.ts` 의 mapping 을 우선 사용해 일관된 사용자
|
|
27
|
+
* 경험과 i18n 친화성을 확보.
|
|
28
|
+
*
|
|
29
|
+
* showErrorToast: false 로 자동 toast 끌 수 있고, errorMessage 로 fallback 지정.
|
|
23
30
|
*/
|
|
24
31
|
export const useAppMutation = <
|
|
25
32
|
TData = unknown,
|
|
@@ -39,10 +46,7 @@ export const useAppMutation = <
|
|
|
39
46
|
if (!showErrorToast) return;
|
|
40
47
|
|
|
41
48
|
const [error] = args;
|
|
42
|
-
const message =
|
|
43
|
-
error instanceof ApiError
|
|
44
|
-
? (error.data?.message ?? errorMessage)
|
|
45
|
-
: errorMessage;
|
|
49
|
+
const message = resolveErrorMessage(error, errorMessage);
|
|
46
50
|
|
|
47
51
|
if (message) {
|
|
48
52
|
toast.error(message);
|
|
@@ -50,3 +54,6 @@ export const useAppMutation = <
|
|
|
50
54
|
},
|
|
51
55
|
});
|
|
52
56
|
};
|
|
57
|
+
|
|
58
|
+
// re-export ApiError 타입을 쓰는 사용처 편의용 (선택).
|
|
59
|
+
export type { ApiError };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Date → 로케일 기반 날짜 포맷 (시간 없음).
|
|
3
|
+
*
|
|
4
|
+
* 비-i18n 프로젝트면 default 'ko-KR' 사용. next-intl 활성 시엔 이 util 을 직접
|
|
5
|
+
* 부르지 말고 같은 모듈의 hook (`useFormatDate`) 을 사용하세요 — 현재 locale 을
|
|
6
|
+
* 자동으로 따릅니다 (next-intl 플러그인이 emit).
|
|
3
7
|
*/
|
|
4
8
|
export const formatDate = (date: Date, locale = 'ko-KR'): string =>
|
|
5
9
|
new Intl.DateTimeFormat(locale, {
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 숫자 →
|
|
3
|
-
* 예: 12000
|
|
2
|
+
* 숫자 → 통화 포맷. default ko-KR + KRW 이지만 두 인자 모두 override 가능.
|
|
3
|
+
* 예: formatPrice(12000) → "₩12,000"
|
|
4
|
+
* formatPrice(99.5, 'en-US', 'USD') → "$99.50"
|
|
5
|
+
*
|
|
6
|
+
* next-intl 활성 시엔 같은 모듈의 hook (`useFormatPrice`) 을 사용하면 현재
|
|
7
|
+
* locale 을 자동으로 따릅니다 (next-intl 플러그인이 emit).
|
|
4
8
|
*/
|
|
5
|
-
export const formatPrice = (
|
|
9
|
+
export const formatPrice = (
|
|
10
|
+
amount: number,
|
|
11
|
+
locale = 'ko-KR',
|
|
12
|
+
currency = 'KRW',
|
|
13
|
+
): string =>
|
|
6
14
|
new Intl.NumberFormat(locale, {
|
|
7
15
|
style: 'currency',
|
|
8
|
-
currency
|
|
9
|
-
maximumFractionDigits: 0,
|
|
16
|
+
currency,
|
|
17
|
+
maximumFractionDigits: currency === 'KRW' ? 0 : 2,
|
|
10
18
|
}).format(amount);
|
|
@@ -9,6 +9,6 @@ import {
|
|
|
9
9
|
* RSC 와 클라이언트 양쪽에서 안전하게 호출 가능한 QueryClient 핸들.
|
|
10
10
|
* 서버에서는 React `cache()` 로 요청 단위 싱글턴, 브라우저에서는 모듈 싱글턴.
|
|
11
11
|
*/
|
|
12
|
-
export
|
|
12
|
+
export function getQueryClient() {
|
|
13
13
|
return isServer ? getServerQueryClient() : getBrowserQueryClient();
|
|
14
14
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReactElement, ReactNode } from 'react';
|
|
1
|
+
import type { ComponentType, ReactElement, ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
render,
|
|
@@ -7,11 +7,17 @@ import {
|
|
|
7
7
|
} from '@testing-library/react';
|
|
8
8
|
import userEvent from '@testing-library/user-event';
|
|
9
9
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
10
|
+
import { ThemeProvider } from 'next-themes';
|
|
10
11
|
|
|
11
12
|
import { createTestQueryClient } from './createTestQueryClient';
|
|
12
13
|
|
|
13
14
|
type Options = Omit<RenderOptions, 'wrapper'> & {
|
|
14
15
|
queryClient?: QueryClient;
|
|
16
|
+
/**
|
|
17
|
+
* 외부 Provider 가 필요할 때 (예: next-intl 활성 프로젝트에서
|
|
18
|
+
* `NextIntlClientProvider`) 사용. ThemeProvider 안쪽 / QueryClientProvider 바깥에 wrap.
|
|
19
|
+
*/
|
|
20
|
+
extraWrapper?: ComponentType<{ children: ReactNode }>;
|
|
15
21
|
};
|
|
16
22
|
|
|
17
23
|
type Result = RenderResult & {
|
|
@@ -20,18 +26,37 @@ type Result = RenderResult & {
|
|
|
20
26
|
};
|
|
21
27
|
|
|
22
28
|
/**
|
|
23
|
-
* RTL render + QueryClientProvider + userEvent setup 한 번에.
|
|
24
|
-
*
|
|
29
|
+
* RTL render + ThemeProvider + QueryClientProvider + userEvent setup 한 번에.
|
|
30
|
+
*
|
|
31
|
+
* next-intl 프로젝트에서 `useTranslations` 사용 컴포넌트 테스트 시:
|
|
32
|
+
*
|
|
33
|
+
* const Intl = ({ children }) => (
|
|
34
|
+
* <NextIntlClientProvider locale='ko' messages={ko}>
|
|
35
|
+
* {children}
|
|
36
|
+
* </NextIntlClientProvider>
|
|
37
|
+
* );
|
|
38
|
+
* renderWithProviders(<MyComponent />, { extraWrapper: Intl });
|
|
25
39
|
*/
|
|
26
40
|
export const renderWithProviders = (
|
|
27
41
|
ui: ReactElement,
|
|
28
42
|
options: Options = {},
|
|
29
43
|
): Result => {
|
|
30
|
-
const {
|
|
44
|
+
const {
|
|
45
|
+
queryClient = createTestQueryClient(),
|
|
46
|
+
extraWrapper: Extra,
|
|
47
|
+
...rtlOptions
|
|
48
|
+
} = options;
|
|
31
49
|
|
|
32
|
-
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
const Wrapper = ({ children }: { children: ReactNode }) => {
|
|
51
|
+
const inner = (
|
|
52
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
53
|
+
);
|
|
54
|
+
return (
|
|
55
|
+
<ThemeProvider attribute='class' defaultTheme='light' enableSystem={false}>
|
|
56
|
+
{Extra ? <Extra>{inner}</Extra> : inner}
|
|
57
|
+
</ThemeProvider>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
35
60
|
|
|
36
61
|
const result = render(ui, { wrapper: Wrapper, ...rtlOptions });
|
|
37
62
|
const user = userEvent.setup();
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
HydrationBoundary,
|
|
7
7
|
} from '@tanstack/react-query';
|
|
8
8
|
|
|
9
|
-
import getQueryClient from '@/src/shared/lib/getQueryClient';
|
|
9
|
+
import { getQueryClient } from '@/src/shared/lib/getQueryClient';
|
|
10
10
|
|
|
11
11
|
export type FetchOptions = Pick<FetchQueryOptions, 'queryKey' | 'queryFn'>;
|
|
12
12
|
|
|
@@ -3,13 +3,11 @@ import pluginNext from "@next/eslint-plugin-next"
|
|
|
3
3
|
import eslintConfigPrettier from "eslint-config-prettier"
|
|
4
4
|
import boundaries from "eslint-plugin-boundaries"
|
|
5
5
|
import checkFile from "eslint-plugin-check-file"
|
|
6
|
-
import importX from "eslint-plugin-import-x"
|
|
7
6
|
import onlyWarn from "eslint-plugin-only-warn"
|
|
8
7
|
import pluginReact from "eslint-plugin-react"
|
|
9
8
|
import pluginReactHooks from "eslint-plugin-react-hooks"
|
|
10
9
|
import globals from "globals"
|
|
11
10
|
import tseslint from "typescript-eslint"
|
|
12
|
-
import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript"
|
|
13
11
|
|
|
14
12
|
export default [
|
|
15
13
|
{
|
|
@@ -146,17 +144,4 @@ export default [
|
|
|
146
144
|
},
|
|
147
145
|
},
|
|
148
146
|
|
|
149
|
-
// ── import-x ──
|
|
150
|
-
{
|
|
151
|
-
plugins: { "import-x": importX },
|
|
152
|
-
settings: {
|
|
153
|
-
"import-x/resolver-next": [
|
|
154
|
-
createTypeScriptImportResolver({ alwaysTryTypes: true }),
|
|
155
|
-
],
|
|
156
|
-
},
|
|
157
|
-
rules: {
|
|
158
|
-
"import-x/order": "off",
|
|
159
|
-
"import-x/no-unresolved": "off",
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
147
|
]
|
|
@@ -45,10 +45,8 @@
|
|
|
45
45
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
46
46
|
"eslint": "^9.39.2",
|
|
47
47
|
"eslint-config-prettier": "^10.1.8",
|
|
48
|
-
"eslint-import-resolver-typescript": "^4.4.4",
|
|
49
48
|
"eslint-plugin-boundaries": "^5.4.0",
|
|
50
49
|
"eslint-plugin-check-file": "^3.3.1",
|
|
51
|
-
"eslint-plugin-import-x": "^4.16.1",
|
|
52
50
|
"eslint-plugin-only-warn": "^1.1.0",
|
|
53
51
|
"eslint-plugin-react": "^7.37.5",
|
|
54
52
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@base-ui/react": "^1.4.1",
|
|
11
11
|
"@workspace/ui-core": "workspace:*",
|
|
12
|
-
"lucide-react": "^0.
|
|
12
|
+
"lucide-react": "^0.563.0",
|
|
13
13
|
"next-themes": "^0.4.6",
|
|
14
14
|
"react": "^19.2.4",
|
|
15
15
|
"react-dom": "^19.2.4",
|
|
16
16
|
"react-hook-form": "^7.56.4",
|
|
17
17
|
"sonner": "^2.0.7",
|
|
18
|
-
"zod": "^3.
|
|
18
|
+
"zod": "^4.3.6"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@tailwindcss/postcss": "^4.1.18",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { ThemeProvider } from 'next-themes';
|
|
4
|
-
import type { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
export function ThemeProviders({ children }: { children: ReactNode }) {
|
|
7
|
-
return (
|
|
8
|
-
<ThemeProvider attribute='class' defaultTheme='system' enableSystem>
|
|
9
|
-
{children}
|
|
10
|
-
</ThemeProvider>
|
|
11
|
-
);
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { ThemeProvider } from 'next-themes';
|
|
4
|
-
import type { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
export function ThemeProviders({ children }: { children: ReactNode }) {
|
|
7
|
-
return (
|
|
8
|
-
<ThemeProvider attribute='class' defaultTheme='system' enableSystem>
|
|
9
|
-
{children}
|
|
10
|
-
</ThemeProvider>
|
|
11
|
-
);
|
|
12
|
-
}
|
package/templates/nextjs-standalone/_arch/flat/components/providers/theme/ThemeProviders.tsx
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { ThemeProvider } from 'next-themes';
|
|
4
|
-
import type { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
export function ThemeProviders({ children }: { children: ReactNode }) {
|
|
7
|
-
return (
|
|
8
|
-
<ThemeProvider attribute='class' defaultTheme='system' enableSystem>
|
|
9
|
-
{children}
|
|
10
|
-
</ThemeProvider>
|
|
11
|
-
);
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { ThemeProvider } from 'next-themes';
|
|
4
|
-
import type { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
export function ThemeProviders({ children }: { children: ReactNode }) {
|
|
7
|
-
return (
|
|
8
|
-
<ThemeProvider attribute='class' defaultTheme='system' enableSystem>
|
|
9
|
-
{children}
|
|
10
|
-
</ThemeProvider>
|
|
11
|
-
);
|
|
12
|
-
}
|