@qlover/create-app 0.10.0 → 0.10.2
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/CHANGELOG.md +145 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/config/IOCIdentifier.ts +2 -2
- package/dist/templates/next-app/config/Identifier/common/common.ts +14 -0
- package/dist/templates/next-app/config/Identifier/pages/index.ts +1 -0
- package/dist/templates/next-app/config/Identifier/pages/page.about.ts +20 -0
- package/dist/templates/next-app/config/common.ts +1 -1
- package/dist/templates/next-app/config/cookies.ts +23 -0
- package/dist/templates/next-app/config/i18n/AboutI18n.ts +14 -0
- package/dist/templates/next-app/config/i18n/i18nConfig.ts +3 -1
- package/dist/templates/next-app/config/i18n/index.ts +1 -0
- package/dist/templates/next-app/config/i18n/loginI18n.ts +8 -0
- package/dist/templates/next-app/config/theme.ts +4 -0
- package/dist/templates/next-app/eslint.config.mjs +4 -1
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +17 -3
- package/dist/templates/next-app/next.config.ts +1 -0
- package/dist/templates/next-app/package.json +15 -7
- package/dist/templates/next-app/public/locales/en.json +5 -0
- package/dist/templates/next-app/public/locales/zh.json +5 -0
- package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +37 -0
- package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +30 -6
- package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +47 -10
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +22 -10
- package/dist/templates/next-app/src/app/[locale]/page.tsx +23 -8
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +21 -9
- package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +7 -28
- package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +7 -34
- package/dist/templates/next-app/src/app/api/admin/locales/route.ts +12 -34
- package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +7 -26
- package/dist/templates/next-app/src/app/api/admin/users/route.ts +14 -33
- package/dist/templates/next-app/src/app/api/locales/json/route.ts +13 -25
- package/dist/templates/next-app/src/app/api/user/login/route.ts +6 -46
- package/dist/templates/next-app/src/app/api/user/logout/route.ts +5 -24
- package/dist/templates/next-app/src/app/api/user/register/route.ts +6 -45
- package/dist/templates/next-app/src/app/manifest.ts +16 -0
- package/dist/templates/next-app/src/app/robots.txt +2 -0
- package/dist/templates/next-app/src/base/cases/DialogHandler.ts +1 -2
- package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +12 -2
- package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +4 -5
- package/dist/templates/next-app/src/base/cases/RouterService.ts +5 -5
- package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +44 -29
- package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +1 -2
- package/dist/templates/next-app/src/base/port/AppApiInterface.ts +22 -0
- package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +22 -10
- package/dist/templates/next-app/src/base/port/IOCInterface.ts +9 -0
- package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +17 -9
- package/dist/templates/next-app/src/base/services/ResourceService.ts +3 -4
- package/dist/templates/next-app/src/base/services/UserService.ts +37 -13
- package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +8 -7
- package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -26
- package/dist/templates/next-app/src/base/types/{PageProps.ts → AppPageRouter.ts} +4 -1
- package/dist/templates/next-app/src/base/types/PagesRouter.ts +9 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +19 -5
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +2 -2
- package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +33 -11
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +8 -5
- package/dist/templates/next-app/src/core/globals.ts +2 -1
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +29 -10
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +6 -7
- package/dist/templates/next-app/src/i18n/loadMessages.ts +103 -0
- package/dist/templates/next-app/src/i18n/request.ts +3 -22
- package/dist/templates/next-app/src/pages/[locale]/about.tsx +61 -0
- package/dist/templates/next-app/src/pages/_app.tsx +50 -0
- package/dist/templates/next-app/src/pages/_document.tsx +13 -0
- package/dist/templates/next-app/src/{middleware.ts → proxy.ts} +2 -1
- package/dist/templates/next-app/src/server/AppPageRouteParams.ts +94 -0
- package/dist/templates/next-app/src/server/NextApiServer.ts +53 -0
- package/dist/templates/next-app/src/server/PagesRouteParams.ts +136 -0
- package/dist/templates/next-app/src/server/{sqlBridges/SupabaseBridge.ts → SupabaseBridge.ts} +2 -0
- package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -3
- package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +74 -0
- package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +39 -0
- package/dist/templates/next-app/src/server/controllers/LocalesController.ts +33 -0
- package/dist/templates/next-app/src/server/controllers/UserController.ts +77 -0
- package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +8 -0
- package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +21 -0
- package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +11 -0
- package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +10 -0
- package/dist/templates/next-app/src/server/port/{ParamsHandlerInterface.ts → RouteParamsnHandlerInterface.ts} +9 -2
- package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
- package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +8 -0
- package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +1 -1
- package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +2 -2
- package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +8 -2
- package/dist/templates/next-app/src/{base → server}/services/AdminLocalesService.ts +2 -2
- package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +25 -10
- package/dist/templates/next-app/src/server/services/ApiUserService.ts +5 -2
- package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +4 -2
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +1 -1
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +10 -10
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -44
- package/dist/templates/next-app/src/styles/css/index.css +1 -1
- package/dist/templates/next-app/src/styles/css/scrollbar.css +34 -0
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +34 -11
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +69 -39
- package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +64 -0
- package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +42 -0
- package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +34 -0
- package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +1 -2
- package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +17 -0
- package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +112 -0
- package/dist/templates/next-app/src/uikit/{components → components-app}/LanguageSwitcher.tsx +15 -19
- package/dist/templates/next-app/src/uikit/{components → components-app}/ThemeSwitcher.tsx +53 -52
- package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +98 -0
- package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +93 -0
- package/dist/templates/next-app/src/uikit/context/IOCContext.ts +16 -4
- package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +32 -0
- package/dist/templates/next-app/tsconfig.json +3 -2
- package/dist/templates/react-app/tsconfig.app.json +1 -3
- package/dist/templates/react-app/tsconfig.node.json +0 -4
- package/dist/templates/react-app/tsconfig.test.json +1 -3
- package/package.json +1 -1
- package/dist/templates/next-app/src/server/PageParams.ts +0 -66
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +0 -80
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +0 -65
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +0 -58
- package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +0 -21
- /package/dist/templates/next-app/{src/app/[locale] → public}/favicon.ico +0 -0
- /package/dist/templates/next-app/src/uikit/{components → components-app}/LogoutButton.tsx +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,150 @@
|
|
|
1
1
|
# @qlover/create-app
|
|
2
2
|
|
|
3
|
+
## 0.10.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
#### ✨ Features
|
|
8
|
+
|
|
9
|
+
- **next-app:** enhance internationalization support and layout components ([6da4f10](https://github.com/qlover/fe-base/commit/6da4f10d79e22619d86db938bdd4ae263af0e084)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
10
|
+
- Added `prettier` configuration to `package.json` for consistent code formatting.
|
|
11
|
+
- Implemented `generateStaticParams` function in `layout.tsx` to support static rendering with locale parameters.
|
|
12
|
+
- Introduced `LanguageSwitcher` and `LocaleSwitcher` components for improved language selection.
|
|
13
|
+
- Refactored `BaseLayout` to streamline layout rendering and removed unused components.
|
|
14
|
+
- Created `LocaleSwitcherSelect` component for better locale selection handling.
|
|
15
|
+
|
|
16
|
+
These changes aim to improve the user experience with enhanced internationalization features and a more organized layout structure.
|
|
17
|
+
|
|
18
|
+
- **next-app:** refactor layout and enhance IOC integration ([d3de8f6](https://github.com/qlover/fe-base/commit/d3de8f6a8ca7336b0768835523bdbd573053ce6d)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
19
|
+
- Replaced `ComboProvider` with `BootstrapsProvider` and `IOCProvider` in `layout.tsx` to improve dependency injection and component structure.
|
|
20
|
+
- Added `register` method to `IOCInterface` for pre-launch dependency registration, enhancing flexibility in SSR environments.
|
|
21
|
+
- Updated `BootstrapClient` to support registration of dependencies and prevent multiple initializations.
|
|
22
|
+
- Introduced `useStrictEffect` hook to ensure effects run correctly in React's Strict Mode.
|
|
23
|
+
- Refactored `BootstrapsProvider` to ensure proper IOC registration before component rendering.
|
|
24
|
+
|
|
25
|
+
These changes aim to streamline the application structure and improve the management of dependencies, enhancing overall performance and maintainability.
|
|
26
|
+
|
|
27
|
+
- **next-app:** enhance internationalization and layout components ([a588f10](https://github.com/qlover/fe-base/commit/a588f104bb3deb9a1c62d6df0e7bc6478a7b9d6d)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
28
|
+
- Updated `layout.tsx` to fetch messages for the current locale, improving language switch experience and preventing flickering.
|
|
29
|
+
- Refactored `BaseHeader` and `BaseLayout` components to utilize `useMemo` for better performance and optimized rendering.
|
|
30
|
+
- Removed deprecated `LocaleSwitcher` and `LocaleSwitcherSelect` components to streamline the codebase.
|
|
31
|
+
- Added `ThemeSwitcher` and `LogoutButton` to `BaseLayout` for enhanced user interface options.
|
|
32
|
+
|
|
33
|
+
These changes aim to improve internationalization support and overall component efficiency in the application.
|
|
34
|
+
|
|
35
|
+
- **next-app:** add internationalization support and new assets ([ae8831f](https://github.com/qlover/fe-base/commit/ae8831f8f580e6e70abcddc5f30885e7baf22864)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
36
|
+
- Updated ESLint configuration to include new paths for `manifest.ts` and `*.tsx` files.
|
|
37
|
+
- Added `favicon.ico` for improved branding and user experience.
|
|
38
|
+
- Introduced `proxy.ts` for handling locale detection and routing with Next.js middleware.
|
|
39
|
+
- Created `manifest.ts` to define application metadata and translations.
|
|
40
|
+
- Added `robots.txt` to guide search engine indexing.
|
|
41
|
+
|
|
42
|
+
These changes enhance internationalization capabilities and improve the overall structure of the application.
|
|
43
|
+
|
|
44
|
+
- **next-app:** enhance internationalization and routing structure ([5f64eb5](https://github.com/qlover/fe-base/commit/5f64eb58083016dfbb6a82eddfa0cac65bc9507b)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
45
|
+
- Updated ESLint configuration to include new paths for TypeScript files in the `src/pages` directory.
|
|
46
|
+
- Added a new constant for the application manifest name in `common.ts` to improve localization.
|
|
47
|
+
- Updated English and Chinese locale files to include the new manifest name key.
|
|
48
|
+
- Refactored `manifest.ts` to utilize the new constant for application name.
|
|
49
|
+
- Introduced `AppPageRouteParams` for better handling of route parameters across the application.
|
|
50
|
+
- Created new `PagesRouteParams` and `RouteParamsnHandlerInterface` for improved routing parameter management.
|
|
51
|
+
|
|
52
|
+
These changes enhance the internationalization capabilities and improve the overall routing structure of the application.
|
|
53
|
+
|
|
54
|
+
- **next-app:** enhance configuration and logging in Next.js application ([181f522](https://github.com/qlover/fe-base/commit/181f52231da7244ab52bfc1ecf4004751f48ed5f)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
55
|
+
- Added `transpilePackages` to `next.config.ts` for improved package handling.
|
|
56
|
+
- Introduced logging for successful startup in `BootstrapClient` to aid in debugging.
|
|
57
|
+
- Enhanced `ClientIOC` with logging during registration to track dependency injection.
|
|
58
|
+
- Updated `_app.tsx` to include `BootstrapsProvider` and `IOCProvider`, ensuring proper context management.
|
|
59
|
+
- Simplified the `About` page by removing unused translations and components.
|
|
60
|
+
|
|
61
|
+
These changes improve the application's configuration, logging capabilities, and overall structure.
|
|
62
|
+
|
|
63
|
+
- **next-app:** update ESLint config and streamline TypeScript paths ([a1ee3bd](https://github.com/qlover/fe-base/commit/a1ee3bd911516821c793c82a6ecba91a39a058bc)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
64
|
+
- Added `manifest.ts` and `proxy.ts` to ESLint configuration for improved linting coverage.
|
|
65
|
+
- Simplified `transpilePackages` array in `next.config.ts` for better readability.
|
|
66
|
+
- Cleaned up TypeScript path mappings in `tsconfig.json` for consistency.
|
|
67
|
+
- Refactored imports in various files to enhance code organization and maintainability.
|
|
68
|
+
|
|
69
|
+
These changes enhance the project's configuration and improve code clarity across the application.
|
|
70
|
+
|
|
71
|
+
- **next-app:** add About page and enhance internationalization support ([49a5108](https://github.com/qlover/fe-base/commit/49a5108800d583f85ce0a97da77fbdbeabcba8c7)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
72
|
+
- Introduced a new About page with SEO metadata using `ClientSeo`.
|
|
73
|
+
- Added `page.about.ts` for About page titles, descriptions, and keywords in both English and Chinese.
|
|
74
|
+
- Updated i18n configuration to include About page translations.
|
|
75
|
+
- Refactored `PagesRouteParams` to support namespace-specific message loading.
|
|
76
|
+
|
|
77
|
+
These changes improve the application's internationalization capabilities and provide a new About page for users.
|
|
78
|
+
|
|
79
|
+
- **next-app:** enhance internationalization and refactor layout components ([56cca75](https://github.com/qlover/fe-base/commit/56cca75cff33581f2e076598bad95438cefe0605)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
80
|
+
- Added `AdminI18nProvider` to manage admin-specific i18n messages, improving localization for admin pages.
|
|
81
|
+
- Refactored layout components to utilize `AppRoutePage`, streamlining the structure and enhancing code clarity.
|
|
82
|
+
- Updated `login` and `register` pages to incorporate admin titles in translations, ensuring consistency across the application.
|
|
83
|
+
- Introduced new `LanguageSwitcher`, `LogoutButton`, and `ThemeSwitcher` components for improved user interface options.
|
|
84
|
+
- Removed deprecated `BaseLayout` and `BaseHeader` components to simplify the codebase.
|
|
85
|
+
|
|
86
|
+
These changes enhance the internationalization capabilities and improve the overall layout structure of the application.
|
|
87
|
+
|
|
88
|
+
- **next-app:** refactor API handling and enhance localization features ([68b81a1](https://github.com/qlover/fe-base/commit/68b81a1346460779b62db860ae80838aa744df66)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
89
|
+
- Updated `useApiLocales` to true, enabling API-based localization data fetching.
|
|
90
|
+
- Refactored API routes for locales to utilize `NextApiServer`, improving error handling and response management.
|
|
91
|
+
- Introduced `AdminLocalesController` and `LocalesController` for better separation of concerns in handling locale-related operations.
|
|
92
|
+
- Enhanced user authentication routes by integrating `UserController` for streamlined login, registration, and logout processes.
|
|
93
|
+
- Added new validation and service layers to improve data handling and validation for locales and user operations.
|
|
94
|
+
|
|
95
|
+
These changes enhance the application's localization capabilities and improve the overall API structure and error handling.
|
|
96
|
+
|
|
97
|
+
- **next-app:** enhance validation and refactor controller logic ([edce45d](https://github.com/qlover/fe-base/commit/edce45d0879777a2edd6cd3144762fbff91e76e5)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
98
|
+
- Updated `ValidatorInterface` to support generic types, allowing for more flexible validation across different data structures.
|
|
99
|
+
- Refactored `AdminLocalesController` and `UserController` to utilize the updated validation methods, improving error handling and data processing.
|
|
100
|
+
- Introduced `PaginationParams` type for better pagination handling in `PaginationValidator`.
|
|
101
|
+
- Enhanced `LocalesValidator` and `LoginValidator` to implement specific data types, ensuring stricter validation rules.
|
|
102
|
+
|
|
103
|
+
These changes improve the robustness of data validation and streamline controller logic, enhancing overall application reliability.
|
|
104
|
+
|
|
105
|
+
- **next-app:** enhance theme management and cookie configuration ([bcfd05d](https://github.com/qlover/fe-base/commit/bcfd05d04a88fd036f1fd15f8227e27c6cc5fe97)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
106
|
+
- Added `cookiesConfig` for managing cookie attributes, improving cookie handling in the application.
|
|
107
|
+
- Updated `i18nConfig` to include a new `storageKey` for locale management.
|
|
108
|
+
- Refactored `RootLayout` to utilize the theme from URL parameters, enhancing user experience with theme persistence.
|
|
109
|
+
- Introduced `getTheme` methods in `AppPageRouteParams` and `PagesRouteParams` for consistent theme retrieval.
|
|
110
|
+
- Enhanced `ThemeSwitcher` to save the selected theme in cookies, ensuring user preferences are maintained across sessions.
|
|
111
|
+
|
|
112
|
+
These changes improve the application's theme management and cookie handling, enhancing overall user experience.
|
|
113
|
+
|
|
114
|
+
- **next-app:** enhance theme configuration and localization support ([4101f66](https://github.com/qlover/fe-base/commit/4101f6690fb28daa173688fe8fb779feeb5ea19f)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
115
|
+
- Updated theme configuration to include `enableSystem` and improved default theme handling.
|
|
116
|
+
- Added new constants for light theme labels in both English and Chinese locales.
|
|
117
|
+
- Refactored `getTheme` method to return the first supported theme if no cookie is found.
|
|
118
|
+
- Enhanced `ThemeSwitcher` component to utilize new theme constants and improve user experience.
|
|
119
|
+
|
|
120
|
+
These changes improve theme management and localization, ensuring a more consistent user interface across different themes.
|
|
121
|
+
|
|
122
|
+
- **next-app:** integrate client rendering and enhance routing functionality ([79f0353](https://github.com/qlover/fe-base/commit/79f0353315bad3a2d648ebe990a3f81d16dc0446)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
123
|
+
- Added `ClinetRenderProvider` to manage client-side rendering for components, preventing flickering during language switches.
|
|
124
|
+
- Updated `AdminRootLayout` to wrap `AdminLayout` with `ClinetRenderProvider` for improved rendering control.
|
|
125
|
+
- Refactored `RouterService` to introduce `replaceHome` method, enhancing navigation capabilities.
|
|
126
|
+
- Modified `LoginForm` to use `routerService.replaceHome()` for smoother redirection after login.
|
|
127
|
+
- Introduced `AppBridge` component to establish a connection between the router and navigation bridge, improving routing management.
|
|
128
|
+
- Added new `scrollbar.css` for custom scrollbar styling and updated CSS imports accordingly.
|
|
129
|
+
|
|
130
|
+
These changes enhance the application's rendering performance and routing structure, providing a better user experience.
|
|
131
|
+
|
|
132
|
+
#### 🐞 Bug Fixes
|
|
133
|
+
|
|
134
|
+
- **next-app:** update ESLint config and enhance i18n message loading ([dad47dc](https://github.com/qlover/fe-base/commit/dad47dc9ca3f7c7f9c4d5542ed6adfaf38c8567b)) ([#549](https://github.com/qlover/fe-base/pull/549))
|
|
135
|
+
- Removed `manifest.ts` from ESLint ignore list to ensure proper linting.
|
|
136
|
+
- Downgraded `eslint-config-next` to version `15.5.0` for compatibility.
|
|
137
|
+
- Added `defaultNamespaces` to `i18nConfig` for improved message loading.
|
|
138
|
+
- Introduced `loadMessages` function to handle loading i18n messages from API or JSON files.
|
|
139
|
+
- Refactored `request.ts` to utilize the new `loadMessages` function for fetching messages.
|
|
140
|
+
- Updated `PagesRouteParams` to support loading messages with specified namespaces.
|
|
141
|
+
|
|
142
|
+
These changes improve the internationalization message handling and ensure consistent linting across the project.
|
|
143
|
+
|
|
144
|
+
## 0.10.1
|
|
145
|
+
|
|
146
|
+
### Patch Changes
|
|
147
|
+
|
|
3
148
|
## 0.10.0
|
|
4
149
|
|
|
5
150
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -8,4 +8,4 @@ ${t}`,_n=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),mn=Objec
|
|
|
8
8
|
`))this.#e+=Math.max(1,Math.ceil(Ye(D,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#D.frames.length,this.#c=t);let{frames:r}=this.#D,u=r[this.#i];this.color&&(u=d[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",D=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+D+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#n;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#n=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#n=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&$e.hide(this.#u),this.#t.discardStdin&&V.default.stdin.isTTY&&(this.#r=!0,Je.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
|
|
9
9
|
`),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&$e.show(this.#u),this.#t.discardStdin&&V.default.stdin.isTTY&&this.#r&&(Je.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:H.success,text:t})}fail(t){return this.stopAndPersist({symbol:H.error,text:t})}warn(t){return this.stopAndPersist({symbol:H.warning,text:t})}info(t){return this.stopAndPersist({symbol:H.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",D=t.text??this.text,s=typeof D=="string"?(i?" ":"")+D:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
|
|
10
10
|
`;return this.stop(),this.#u.write(p),this}};function rr(e){return new Ze(e)}async function ur(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:D}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=rr(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(D===void 0?void 0:typeof D=="string"?D:D(s)),s}}var bt=require("fs");var P=require("path"),me=require("fs"),fr=h(cr(),1),Dt=require("fs");var de=require("fs"),X=class{static ensureDir(t){(0,de.existsSync)(t)||(0,de.mkdirSync)(t,{recursive:!0})}};var{copyFile:us,stat:is}=Dt.promises,_e=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=(0,P.join)(t,this.ignoreFile);if(!(0,me.existsSync)(r))return;let D=(0,me.readFileSync)(r,"utf8").split(`
|
|
11
|
-
`).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,fr.default)().add(D)}async copyFiles(t,r,u,i){let D=await Dt.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,P.join)(t,o),a=(0,P.join)(r,o);if(u&&u.ignores(o))return;if(X.ensureDir((0,P.dirname)(a)),(await is(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await us(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),$D=h(kD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,L.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,L.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,$D.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,L.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var UD=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=ur,this.context=new WD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new _e((0,v.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await HD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return UD.includes(t)}async getGeneratorContext(){let t=wt(this.subPackages,UD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=Pt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,v.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,v.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,v.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,v.join)(D,o),a=(0,v.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var vt={name:"@qlover/create-app",version:"0.10.
|
|
11
|
+
`).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,fr.default)().add(D)}async copyFiles(t,r,u,i){let D=await Dt.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,P.join)(t,o),a=(0,P.join)(r,o);if(u&&u.ignores(o))return;if(X.ensureDir((0,P.dirname)(a)),(await is(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await us(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),$D=h(kD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,L.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,L.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,$D.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,L.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var UD=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=ur,this.context=new WD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new _e((0,v.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await HD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return UD.includes(t)}async getGeneratorContext(){let t=wt(this.subPackages,UD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=Pt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,v.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,v.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,v.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,v.join)(D,o),a=(0,v.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var vt={name:"@qlover/create-app",version:"0.10.2",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Wl(){let e=new YD.Command;return e.version(vt.version,"-v, --version","Show version").description(vt.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function VD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Wl(),i=(0,xt.resolve)(e,"./templates"),D=(0,xt.resolve)(e,"./configs");(0,yt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,yt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}VD(__dirname).catch(e=>{console.error(e),process.exit(1)});
|
package/dist/index.js
CHANGED
|
@@ -8,4 +8,4 @@ ${t}`,pD=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),hD=Objec
|
|
|
8
8
|
`))this.#e+=Math.max(1,Math.ceil(Le(n,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#n.frames.length,this.#c=t);let{frames:r}=this.#n,u=r[this.#i];this.color&&(u=E[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",n=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+n+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#D;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#D=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#D=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&je.hide(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&(this.#r=!0,We.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
|
|
9
9
|
`),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&je.show(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&this.#r&&(We.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:k.success,text:t})}fail(t){return this.stopAndPersist({symbol:k.error,text:t})}warn(t){return this.stopAndPersist({symbol:k.warning,text:t})}info(t){return this.stopAndPersist({symbol:k.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",n=t.text??this.text,s=typeof n=="string"?(i?" ":"")+n:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
|
|
10
10
|
`;return this.stop(),this.#u.write(p),this}};function zt(e){return new He(e)}async function Kt(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:n}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=zt(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(n===void 0?void 0:typeof n=="string"?n:n(s)),s}}import{existsSync as Nn}from"fs";var nr=ue(ir(),1);import{dirname as QD,join as Ze}from"path";import{existsSync as es,readFileSync as ts}from"fs";import{promises as Dr}from"fs";import{existsSync as JD,mkdirSync as ZD}from"fs";var H=class{static ensureDir(t){JD(t)||ZD(t,{recursive:!0})}};var{copyFile:rs,stat:us}=Dr,Ce=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=Ze(t,this.ignoreFile);if(!es(r))return;let n=ts(r,"utf8").split(`
|
|
11
|
-
`).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,nr.default)().add(n)}async copyFiles(t,r,u,i){let n=await Dr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"0.10.
|
|
11
|
+
`).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,nr.default)().add(n)}async copyFiles(t,r,u,i){let n=await Dr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"0.10.2",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
|
|
@@ -3,7 +3,7 @@ import type { DialogHandler } from '@/base/cases/DialogHandler';
|
|
|
3
3
|
import type { RouterService } from '@/base/cases/RouterService';
|
|
4
4
|
import type { I18nService } from '@/base/services/I18nService';
|
|
5
5
|
import type { UserService } from '@/base/services/UserService';
|
|
6
|
-
import type {
|
|
6
|
+
import type { SupabaseBridge } from '@/server/SupabaseBridge';
|
|
7
7
|
import type * as CorekitBridge from '@qlover/corekit-bridge';
|
|
8
8
|
import type * as FeCorekit from '@qlover/fe-corekit';
|
|
9
9
|
import type { LoggerInterface } from '@qlover/logger';
|
|
@@ -70,5 +70,5 @@ export interface IOCIdentifierMap {
|
|
|
70
70
|
export interface IOCIdentifierMapServer {
|
|
71
71
|
[IOCIdentifier.AppConfig]: AppConfig;
|
|
72
72
|
[IOCIdentifier.Logger]: LoggerInterface;
|
|
73
|
-
[IOCIdentifier.DBBridgeInterface]:
|
|
73
|
+
[IOCIdentifier.DBBridgeInterface]: SupabaseBridge;
|
|
74
74
|
}
|
|
@@ -33,6 +33,13 @@ export const COMMON_DETAIL = 'common:detail';
|
|
|
33
33
|
*/
|
|
34
34
|
export const COMMON_THEME_DEFAULT = 'common:theme__default';
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @description Theme switcher light theme label
|
|
38
|
+
* @localZh 亮色主题
|
|
39
|
+
* @localEn Light Theme
|
|
40
|
+
*/
|
|
41
|
+
export const COMMON_THEME_LIGHT = 'common:theme__light';
|
|
42
|
+
|
|
36
43
|
/**
|
|
37
44
|
* @description Theme switcher dark theme label
|
|
38
45
|
* @localZh 暗色主题
|
|
@@ -74,3 +81,10 @@ export const COMMON_LOGOUT_DIALOG_CONTENT = 'common:logout__dialog__content';
|
|
|
74
81
|
* @localEn I18n key format is incorrect
|
|
75
82
|
*/
|
|
76
83
|
export const COMMON_I18N_KEY_INVALID = 'common:i18n_key_invalid';
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @description manifest name
|
|
87
|
+
* @localZh 应用名称
|
|
88
|
+
* @localEn Application Name
|
|
89
|
+
*/
|
|
90
|
+
export const COMMON_MANIFEST_NAME = 'common:manifest__name';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description About page title
|
|
3
|
+
* @localZh 关于我们
|
|
4
|
+
* @localEn About Us
|
|
5
|
+
*/
|
|
6
|
+
export const PAGE_ABOUT_TITLE = 'page_about:title';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @description About page description
|
|
10
|
+
* @localZh 了解我们的团队和使命
|
|
11
|
+
* @localEn Learn about our team and mission
|
|
12
|
+
*/
|
|
13
|
+
export const PAGE_ABOUT_DESCRIPTION = 'page_about:description';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @description About page keywords
|
|
17
|
+
* @localZh 关于我们, 团队, 使命
|
|
18
|
+
* @localEn About us, team, mission
|
|
19
|
+
*/
|
|
20
|
+
export const PAGE_ABOUT_KEYWORDS = 'page_about:keywords';
|
|
@@ -26,7 +26,7 @@ export const useLocaleRoutes = true;
|
|
|
26
26
|
* - true: 使用API获取本地化数据,可以在 /admin/locales 页面中对他进行修改
|
|
27
27
|
* - false: 不使用API获取本地化数据,直接使用 `@brain-toolkit/ts2locales` 生成的json数据
|
|
28
28
|
*/
|
|
29
|
-
export const useApiLocales =
|
|
29
|
+
export const useApiLocales = true;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* 是否在在 useWarnTranslations 中警告缺失的翻译,而不是抛出错误
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const cookiesConfig: Cookies.CookieAttributes = {
|
|
2
|
+
domain: '',
|
|
3
|
+
path: '/',
|
|
4
|
+
expires: 30,
|
|
5
|
+
/**
|
|
6
|
+
* httpOnly 必须为 false,否则客户端 JavaScript 无法设置 cookie
|
|
7
|
+
* httpOnly: true 只能用于服务端设置的 cookie(如认证 token)
|
|
8
|
+
*/
|
|
9
|
+
httpOnly: false,
|
|
10
|
+
/**
|
|
11
|
+
* 是否仅通过 HTTPS 传输
|
|
12
|
+
*
|
|
13
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#secure_cookie_attribute
|
|
14
|
+
*/
|
|
15
|
+
secure: false,
|
|
16
|
+
/**
|
|
17
|
+
* 'strict':最严格,跨站请求(如从其他网站链接过来)不发送 Cookie
|
|
18
|
+
* 其他可选值:'lax'(部分允许)、'none'(允许,但需要 secure: true)
|
|
19
|
+
*
|
|
20
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#samesite_cookie_attribute
|
|
21
|
+
*/
|
|
22
|
+
sameSite: 'strict'
|
|
23
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as i18nKeys from '../Identifier/pages/page.about';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* About page i18n interface
|
|
5
|
+
*/
|
|
6
|
+
export type AboutI18nInterface = typeof aboutI18n;
|
|
7
|
+
|
|
8
|
+
export const aboutI18n = Object.freeze({
|
|
9
|
+
// basic meta properties
|
|
10
|
+
title: i18nKeys.PAGE_ABOUT_TITLE,
|
|
11
|
+
description: i18nKeys.PAGE_ABOUT_DESCRIPTION,
|
|
12
|
+
content: i18nKeys.PAGE_ABOUT_DESCRIPTION,
|
|
13
|
+
keywords: i18nKeys.PAGE_ABOUT_KEYWORDS
|
|
14
|
+
});
|
|
@@ -8,7 +8,9 @@ export const i18nConfig = {
|
|
|
8
8
|
fallbackLng: 'en',
|
|
9
9
|
debug: false,
|
|
10
10
|
supportedLngs: ['en', 'zh'] as const,
|
|
11
|
-
localeDetection: true
|
|
11
|
+
localeDetection: true,
|
|
12
|
+
defaultNamespaces: ['common', 'api'],
|
|
13
|
+
storageKey: 'NEXT_LOCALE'
|
|
12
14
|
} as const;
|
|
13
15
|
|
|
14
16
|
export type LocaleType = (typeof i18nConfig.supportedLngs)[number];
|
|
@@ -8,6 +8,14 @@ import * as i18nKeys from '../Identifier/pages/page.login';
|
|
|
8
8
|
*/
|
|
9
9
|
export type LoginI18nInterface = typeof loginI18n;
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Login page i18n namespace
|
|
13
|
+
*
|
|
14
|
+
* - /login/page.tsx
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export const NS_PAGE_LOGIN = 'page_login';
|
|
18
|
+
|
|
11
19
|
export const loginI18n = Object.freeze({
|
|
12
20
|
// basic meta properties
|
|
13
21
|
title: i18nKeys.PAGE_LOGIN_TITLE,
|
|
@@ -5,7 +5,11 @@ import type { ThemeConfig } from 'antd';
|
|
|
5
5
|
*/
|
|
6
6
|
export const themeConfig = {
|
|
7
7
|
domAttribute: 'data-theme',
|
|
8
|
+
/**
|
|
9
|
+
* If `enableSystem` is false, the default theme is light
|
|
10
|
+
*/
|
|
8
11
|
defaultTheme: 'system',
|
|
12
|
+
enableSystem: true,
|
|
9
13
|
target: 'html',
|
|
10
14
|
supportedThemes: ['light', 'dark', 'pink'],
|
|
11
15
|
storageKey: 'fe_theme',
|
|
@@ -192,6 +192,9 @@ const eslintConfig = [
|
|
|
192
192
|
'src/app/**/not-found.tsx',
|
|
193
193
|
'src/i18n/request.ts',
|
|
194
194
|
'src/middleware.ts',
|
|
195
|
+
'src/pages/**/*.tsx',
|
|
196
|
+
'src/app/manifest.ts',
|
|
197
|
+
'src/proxy.ts',
|
|
195
198
|
'**/*.config.*'
|
|
196
199
|
],
|
|
197
200
|
rules: {
|
|
@@ -200,4 +203,4 @@ const eslintConfig = [
|
|
|
200
203
|
}
|
|
201
204
|
];
|
|
202
205
|
|
|
203
|
-
export default eslintConfig;
|
|
206
|
+
export default eslintConfig;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import type { SafeParseReturnType } from 'zod';
|
|
2
3
|
|
|
3
4
|
export const UserRole = {
|
|
4
5
|
ADMIN: 0,
|
|
@@ -16,9 +17,22 @@ export const userSchema = z.object({
|
|
|
16
17
|
* 加密的token, 包含token, 过期时间
|
|
17
18
|
*/
|
|
18
19
|
credential_token: z.string(),
|
|
19
|
-
email_confirmed_at: z.number(),
|
|
20
|
-
created_at: z.
|
|
21
|
-
updated_at: z.
|
|
20
|
+
email_confirmed_at: z.number().nullable().optional(),
|
|
21
|
+
created_at: z.string(),
|
|
22
|
+
updated_at: z.string().nullable().optional()
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
export type UserSchema = z.infer<typeof userSchema>;
|
|
26
|
+
|
|
27
|
+
export type UserCredential = {
|
|
28
|
+
credential_token: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function isWebUserSchema(
|
|
32
|
+
value: unknown
|
|
33
|
+
): SafeParseReturnType<
|
|
34
|
+
Omit<UserSchema, 'password'>,
|
|
35
|
+
Omit<UserSchema, 'password'>
|
|
36
|
+
> {
|
|
37
|
+
return userSchema.omit({ password: true }).safeParse(value);
|
|
38
|
+
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"build:staging": "cross-env APP_ENV=staging next build --turbopack",
|
|
11
11
|
"build:prod": "cross-env APP_ENV=production next build --turbopack",
|
|
12
12
|
"start": "next start --port 3101",
|
|
13
|
-
"lint": "eslint ./src ./config ./make ./migrations",
|
|
14
|
-
"lint:fix": "eslint ./src ./config ./make ./migrations --ext .ts,.tsx --fix",
|
|
13
|
+
"lint": "tsc --noEmit && eslint ./src ./config ./make ./migrations",
|
|
14
|
+
"lint:fix": "tsc --noEmit && eslint ./src ./config ./make ./migrations --ext .ts,.tsx --fix",
|
|
15
15
|
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
|
|
16
16
|
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
|
|
17
17
|
"fix": "npm run lint:fix && npm run format"
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
"@brain-toolkit/antd-theme-override": "^0.0.3",
|
|
25
25
|
"@brain-toolkit/bridge": "^0.0.1",
|
|
26
26
|
"@brain-toolkit/react-kit": "^0.1.0",
|
|
27
|
-
"@qlover/corekit-bridge": "
|
|
28
|
-
"@qlover/fe-corekit": "
|
|
27
|
+
"@qlover/corekit-bridge": "latest",
|
|
28
|
+
"@qlover/fe-corekit": "latest",
|
|
29
29
|
"@qlover/slice-store-react": "^1.4.1",
|
|
30
|
-
"@supabase/auth-helpers-nextjs": "^0.10.0",
|
|
31
30
|
"@supabase/ssr": "^0.7.0",
|
|
32
31
|
"@supabase/supabase-js": "^2.57.2",
|
|
32
|
+
"@types/js-cookie": "^3.0.6",
|
|
33
33
|
"@types/jsonwebtoken": "^9.0.10",
|
|
34
34
|
"antd": "^5.27.1",
|
|
35
35
|
"clsx": "^2.1.1",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"inversify": "^7.8.1",
|
|
38
38
|
"jsonwebtoken": "^9.0.2",
|
|
39
39
|
"lodash": "^4.17.21",
|
|
40
|
-
"next": "
|
|
40
|
+
"next": "^16.0.3",
|
|
41
41
|
"next-intl": "^4.3.5",
|
|
42
42
|
"next-themes": "^0.4.6",
|
|
43
43
|
"openai": "^5.23.0",
|
|
@@ -67,8 +67,16 @@
|
|
|
67
67
|
"eslint-plugin-prettier": "^5.5.4",
|
|
68
68
|
"eslint-plugin-unused-imports": "^4.2.0",
|
|
69
69
|
"prettier": "^3.6.2",
|
|
70
|
-
"supabase": "^2.40.7",
|
|
71
70
|
"tailwindcss": "^4",
|
|
72
71
|
"typescript": "^5"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": "^20.17.0 || >=22.9.0",
|
|
75
|
+
"npm": ">=10.0.0"
|
|
76
|
+
},
|
|
77
|
+
"prettier": {
|
|
78
|
+
"singleQuote": true,
|
|
79
|
+
"trailingComma": "none",
|
|
80
|
+
"endOfLine": "lf"
|
|
73
81
|
}
|
|
74
82
|
}
|
|
@@ -19,17 +19,22 @@
|
|
|
19
19
|
"common:create": "Create",
|
|
20
20
|
"common:detail": "Detail",
|
|
21
21
|
"common:theme__default": "Default Theme",
|
|
22
|
+
"common:theme__light": "Light Theme",
|
|
22
23
|
"common:theme__dark": "Dark Theme",
|
|
23
24
|
"common:theme__pink": "Pink Theme",
|
|
24
25
|
"common:admin__title": "Admin Backend",
|
|
25
26
|
"common:logout__dialog__title": "Logout",
|
|
26
27
|
"common:logout__dialog__content": "Are you sure you want to logout?",
|
|
27
28
|
"common:i18n_key_invalid": "I18n key format is incorrect",
|
|
29
|
+
"common:manifest__name": "Application Name",
|
|
28
30
|
"common:v:login_params_required": "Not a valid login parameter",
|
|
29
31
|
"common:v:email_invalid": "Invalid email format validation message",
|
|
30
32
|
"common:v:password_min_length": "Password minimum length validation message(6)",
|
|
31
33
|
"common:v:password_max_length": "Password maximum length validation message(50)",
|
|
32
34
|
"common:v:password_special_chars": "Password cannot contain whitespace characters validation message",
|
|
35
|
+
"page_about:title": "About Us",
|
|
36
|
+
"page_about:description": "Learn about our team and mission",
|
|
37
|
+
"page_about:keywords": "About us, team, mission",
|
|
33
38
|
"admin_home:title": "Admin page",
|
|
34
39
|
"admin_home:description": "Admin page",
|
|
35
40
|
"admin_home:keywords": "Admin page",
|
|
@@ -19,17 +19,22 @@
|
|
|
19
19
|
"common:create": "创建",
|
|
20
20
|
"common:detail": "详情",
|
|
21
21
|
"common:theme__default": "默认主题",
|
|
22
|
+
"common:theme__light": "亮色主题",
|
|
22
23
|
"common:theme__dark": "暗色主题",
|
|
23
24
|
"common:theme__pink": "粉色主题",
|
|
24
25
|
"common:admin__title": "管理后台",
|
|
25
26
|
"common:logout__dialog__title": "登出",
|
|
26
27
|
"common:logout__dialog__content": "确定要登出吗?",
|
|
27
28
|
"common:i18n_key_invalid": "i18n 键格式不正确",
|
|
29
|
+
"common:manifest__name": "应用名称",
|
|
28
30
|
"common:v:login_params_required": "不是一个有效的登录参数",
|
|
29
31
|
"common:v:email_invalid": "邮箱格式无效的验证消息",
|
|
30
32
|
"common:v:password_min_length": "密码最小长度验证消息(6)",
|
|
31
33
|
"common:v:password_max_length": "密码最大长度验证消息(50)",
|
|
32
34
|
"common:v:password_special_chars": "密码不能包含空格的验证消息",
|
|
35
|
+
"page_about:title": "关于我们",
|
|
36
|
+
"page_about:description": "了解我们的团队和使命",
|
|
37
|
+
"page_about:keywords": "关于我们, 团队, 使命",
|
|
33
38
|
"admin_home:title": "管理员页面",
|
|
34
39
|
"admin_home:description": "管理员页面",
|
|
35
40
|
"admin_home:keywords": "管理员页面关键词",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { NextIntlClientProvider, useMessages } from 'next-intl';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Client component wrapper to merge admin i18n messages
|
|
7
|
+
* NextIntlClientProvider supports nesting and will merge messages from parent provider
|
|
8
|
+
*/
|
|
9
|
+
export function AdminI18nProvider({
|
|
10
|
+
children,
|
|
11
|
+
locale,
|
|
12
|
+
adminMessages
|
|
13
|
+
}: {
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
locale: string;
|
|
16
|
+
adminMessages: Record<string, string>;
|
|
17
|
+
}) {
|
|
18
|
+
// Get existing messages from parent NextIntlClientProvider (includes common, api)
|
|
19
|
+
const existingMessages = useMessages();
|
|
20
|
+
|
|
21
|
+
// Merge admin messages with existing messages
|
|
22
|
+
// Admin messages will override any existing keys with the same name
|
|
23
|
+
const mergedMessages = {
|
|
24
|
+
...existingMessages,
|
|
25
|
+
...adminMessages
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<NextIntlClientProvider
|
|
30
|
+
data-testid="AdminI18nProvider"
|
|
31
|
+
locale={locale}
|
|
32
|
+
messages={mergedMessages}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</NextIntlClientProvider>
|
|
36
|
+
);
|
|
37
|
+
}
|