@qlover/create-app 0.7.7 → 0.7.9

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/build/generateLocales.ts +1 -1
  5. package/dist/templates/next-app/config/IOCIdentifier.ts +15 -2
  6. package/dist/templates/next-app/config/Identifier/common.error.ts +7 -0
  7. package/dist/templates/next-app/config/Identifier/page.home.ts +7 -0
  8. package/dist/templates/next-app/config/i18n/HomeI18n .ts +22 -0
  9. package/dist/templates/next-app/config/theme.ts +1 -0
  10. package/dist/templates/next-app/package.json +5 -5
  11. package/dist/templates/next-app/public/locales/{en/common.json → en.json} +3 -1
  12. package/dist/templates/next-app/public/locales/{zh/common.json → zh.json} +3 -1
  13. package/dist/templates/next-app/src/app/[locale]/layout.tsx +8 -26
  14. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +9 -18
  15. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +28 -24
  16. package/dist/templates/next-app/src/app/[locale]/page.tsx +105 -100
  17. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +92 -0
  18. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +16 -0
  19. package/dist/templates/next-app/src/base/cases/PageParams.ts +74 -0
  20. package/dist/templates/next-app/src/base/cases/RouterService.ts +35 -0
  21. package/dist/templates/next-app/src/base/cases/ServerAuth.ts +17 -0
  22. package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +27 -0
  23. package/dist/templates/next-app/src/base/port/IOCInterface.ts +24 -0
  24. package/dist/templates/next-app/src/base/port/ParamsHandlerInterface.ts +11 -0
  25. package/dist/templates/next-app/src/base/port/RouterInterface.ts +11 -0
  26. package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +3 -0
  27. package/dist/templates/next-app/src/base/port/ServerInterface.ts +12 -0
  28. package/dist/templates/next-app/src/base/types/PageProps.ts +9 -0
  29. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +2 -39
  30. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +78 -0
  31. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +37 -0
  32. package/dist/templates/next-app/src/core/{IocRegisterImpl.ts → clientIoc/ClientIOCRegister.ts} +20 -23
  33. package/dist/templates/next-app/src/core/globals.ts +3 -0
  34. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +52 -0
  35. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +63 -0
  36. package/dist/templates/next-app/src/i18n/request.ts +1 -2
  37. package/dist/templates/next-app/src/i18n/routing.ts +3 -3
  38. package/dist/templates/next-app/src/middleware.ts +6 -3
  39. package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +12 -0
  40. package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +26 -0
  41. package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +16 -0
  42. package/dist/templates/next-app/src/styles/css/page.css +4 -3
  43. package/dist/templates/next-app/src/styles/css/themes/_default.css +1 -0
  44. package/dist/templates/next-app/src/styles/css/themes/dark.css +1 -0
  45. package/dist/templates/next-app/src/styles/css/themes/pink.css +1 -0
  46. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +14 -14
  47. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +27 -0
  48. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +13 -1
  49. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +5 -0
  50. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +49 -21
  51. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +34 -0
  52. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +92 -35
  53. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +9 -2
  54. package/package.json +1 -1
  55. package/dist/templates/next-app/plugins/eslint-plugin-testid.mjs +0 -94
  56. package/dist/templates/next-app/plugins/generateLocalesPlugin.ts +0 -33
  57. package/dist/templates/next-app/src/core/IOC.ts +0 -58
  58. package/dist/templates/next-app/src/server/getServerI18n.ts +0 -26
package/CHANGELOG.md CHANGED
@@ -1,5 +1,126 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 0.7.9
4
+
5
+ ### Patch Changes
6
+
7
+ #### ✨ Features
8
+
9
+ - **next-app:** enhance home page internationalization and SEO support ([54d7714](https://github.com/qlover/fe-base/commit/54d77146f26ce132ddd20edace52a1a10eb03149)) ([#508](https://github.com/qlover/fe-base/pull/508))
10
+ - Added PAGE_HOME_KEYWORDS constant to improve SEO with relevant keywords for the home page.
11
+ - Created HomeI18nInterface to manage internationalization for the home page, incorporating keywords and welcome messages.
12
+ - Updated English and Chinese localization files to include new keywords for the home page, enhancing multilingual support.
13
+ - Refactored Home component to utilize the new i18n structure for better localization handling.
14
+
15
+ These changes aim to improve the user experience by providing comprehensive localization and SEO enhancements for the home page.
16
+
17
+ - **next-app:** enhance login components and improve styling consistency ([59b8b85](https://github.com/qlover/fe-base/commit/59b8b85179e78f29f1eca5b8e7e375b6b8c660eb)) ([#508](https://github.com/qlover/fe-base/pull/508))
18
+ - Updated LoginForm component to use new border color variable for improved styling consistency.
19
+ - Simplified LocaleLink components in LoginForm for cleaner code and better readability.
20
+ - Refactored LoginPage to remove unnecessary elements, streamlining the layout.
21
+ - Enhanced CSS variables in page.css for better color management across the application.
22
+
23
+ These changes aim to improve the user interface and maintain a consistent design across the login components.
24
+
25
+ - **next-app:** update scripts, enhance theme support, and improve styling ([aebcdfa](https://github.com/qlover/fe-base/commit/aebcdfaeb5bae706ac96dc410056f3064eb1e8e9)) ([#508](https://github.com/qlover/fe-base/pull/508))
26
+ - Updated package.json scripts to specify ports for development and production environments.
27
+ - Added new CSS variables for hover states and improved theme management in various CSS files.
28
+ - Enhanced the LanguageSwitcher and ThemeSwitcher components to utilize dropdowns for better user experience.
29
+ - Refactored BaseLayout to include a background color for the main content area.
30
+
31
+ These changes aim to improve the application's usability and maintainability by enhancing the theme management and user interface components.
32
+
33
+ ## 0.7.8
34
+
35
+ ### Patch Changes
36
+
37
+ #### ✨ Features
38
+
39
+ - **next-app:** enhance internationalization support with new localization files and refactor layout structure ([9d73643](https://github.com/qlover/fe-base/commit/9d7364337c18bac196a554b50e2342a51e15154d)) ([#506](https://github.com/qlover/fe-base/pull/506))
40
+ - Added English and Chinese localization files for common application messages to improve internationalization.
41
+ - Refactored layout.tsx to streamline locale handling and message retrieval using the new PageParams class.
42
+ - Removed the obsolete getServerI18n function to simplify the localization setup.
43
+ - Updated LoginPage and related components to utilize the new i18n structure for better translation handling.
44
+
45
+ These changes aim to enhance the user experience by providing comprehensive localization support and improving the overall structure of the application.
46
+
47
+ - **next-app:** enhance IOC integration and introduce new service interfaces ([688ec8e](https://github.com/qlover/fe-base/commit/688ec8e11f94fd92a13257dbdd7ba6359f4a2efc)) ([#506](https://github.com/qlover/fe-base/pull/506))
48
+ - Added UserService interface to IOCIdentifier for improved dependency management.
49
+ - Updated IOCIdentifierMap to include UserService, facilitating better service registration.
50
+ - Refactored LoginForm to utilize the useIOC hook for accessing UserService, enhancing component modularity.
51
+ - Introduced new IOCInterface for defining IOC registration options and container interactions.
52
+ - Created ClientIOC and ServerIOC classes for managing client and server-side IOC functionalities, improving overall architecture.
53
+
54
+ These changes aim to streamline dependency injection and enhance the modularity of the application, providing a clearer structure for service interactions.
55
+
56
+ - **next-app:** refactor layout and enhance parameter handling with BootstrapServer ([3bf30c4](https://github.com/qlover/fe-base/commit/3bf30c4aee2b44a717a67ab26f1a7f36b8dc19e9)) ([#506](https://github.com/qlover/fe-base/pull/506))
57
+ - Updated RootLayout to utilize BootstrapServer for locale and message retrieval, improving internationalization support.
58
+ - Refactored PageParams to implement ParamsHandlerInterface, streamlining parameter management.
59
+ - Introduced BootstrapServer class for better server-side parameter handling and integration with IOC.
60
+ - Created ParamsHandlerInterface to standardize parameter handling across the application.
61
+
62
+ These changes aim to enhance the modularity and maintainability of the application while improving the internationalization experience.
63
+
64
+ - **next-app:** enhance localization and error handling in application ([1b1c7ab](https://github.com/qlover/fe-base/commit/1b1c7ab534657a88d14524ce25432bc4042ec26c)) ([#506](https://github.com/qlover/fe-base/pull/506))
65
+ - Updated generateLocales function to output locale files in a simplified format.
66
+ - Refactored IOCIdentifier to replace Logger type with LoggerInterface for better type safety.
67
+ - Added SERVER_AUTH_ERROR identifier to common error messages for improved error handling.
68
+ - Integrated new error messages into English and Chinese localization files.
69
+ - Removed obsolete common.json files to streamline localization structure.
70
+ - Introduced ServerAuthPlugin and ServerErrorHandler for enhanced server-side authentication and error management.
71
+
72
+ These changes aim to improve the application's internationalization capabilities and error handling processes, providing a more robust user experience.
73
+
74
+ - **next-app:** refactor authentication handling and streamline server integration ([c17bc24](https://github.com/qlover/fe-base/commit/c17bc242067e4a01a50c89832c9060e130ca5291)) ([#506](https://github.com/qlover/fe-base/pull/506))
75
+ - Replaced ServerAuthPlugin with a new ServerAuth class for improved authentication management.
76
+ - Updated Home page to utilize the new ServerAuth class for checking user authentication and redirecting to the login page if necessary.
77
+ - Removed obsolete ServerAuthPlugin file to clean up the codebase.
78
+ - Introduced ServerInterface and ServerAuthInterface for better abstraction and type safety in server interactions.
79
+
80
+ These changes aim to enhance the authentication flow and improve the overall structure of the server-side logic in the application.
81
+
82
+ - **next-app:** enhance routing and dialog handling with new services and components ([944ded9](https://github.com/qlover/fe-base/commit/944ded9c93223d4230d1911552ba17e90a23fd57)) ([#506](https://github.com/qlover/fe-base/pull/506))
83
+ - Introduced RouterService and NavigateBridge for improved navigation management within the application.
84
+ - Added DialogHandler for standardized dialog and notification handling using Ant Design components.
85
+ - Updated IOCIdentifier to include new services and interfaces for better dependency management.
86
+ - Refactored LoginForm to utilize the new RouterService for navigation after login.
87
+ - Enhanced BaseHeader to conditionally display a logout button, improving user experience.
88
+ - Integrated new components and services into the application structure for better modularity and maintainability.
89
+
90
+ These changes aim to streamline navigation and dialog interactions, enhancing the overall user experience and application architecture.
91
+
92
+ - **next-app:** refactor page properties and enhance server integration ([ce49da5](https://github.com/qlover/fe-base/commit/ce49da573ccd621559d3d5cbcea448db93998e66)) ([#506](https://github.com/qlover/fe-base/pull/506))
93
+ - Renamed PageProps to PageParamsProps for clarity in parameter handling.
94
+ - Updated Home and LoginPage components to utilize the new PageParamsProps interface for improved type safety.
95
+ - Enhanced BootstrapServer to include a method for retrieving internationalization interfaces, streamlining localization support.
96
+ - Added error handling in LoginPage to manage missing parameters effectively.
97
+
98
+ These changes aim to improve the structure and clarity of page properties while enhancing server-side integration for better localization management.
99
+
100
+ - **next-app:** refactor server integration and enhance parameter handling ([bd3cd12](https://github.com/qlover/fe-base/commit/bd3cd12cec6f0dc1b2b660e977f5e81cb18951ab)) ([#506](https://github.com/qlover/fe-base/pull/506))
101
+ - Updated layout and page components to utilize the new PageParams class for improved locale and message retrieval.
102
+ - Refactored BootstrapServer to streamline server initialization and removed obsolete methods for better clarity.
103
+ - Enhanced LoginPage to utilize PageParams for internationalization interface retrieval, improving localization support.
104
+
105
+ These changes aim to improve the structure and maintainability of server interactions while enhancing the internationalization experience across the application.
106
+
107
+ #### 🐞 Bug Fixes
108
+
109
+ - **next-app:** update eslint-plugin dependency to latest version ([2fea1ab](https://github.com/qlover/fe-base/commit/2fea1ab9c94cc52ffe34e82d2980baf99de55aae)) ([#506](https://github.com/qlover/fe-base/pull/506))
110
+ - Changed the @qlover/eslint-plugin dependency in package.json from a local file reference to the latest version. This update ensures that the project uses the most recent features and fixes available in the eslint-plugin package.
111
+
112
+ This change aims to improve code quality and maintainability by leveraging the latest enhancements in the eslint-plugin.
113
+
114
+ #### ♻️ Refactors
115
+
116
+ - **next-app:** remove obsolete ESLint plugin and streamline localization setup ([d1c81b4](https://github.com/qlover/fe-base/commit/d1c81b4185131bad25f497e0004ebc48001ac9fe)) ([#506](https://github.com/qlover/fe-base/pull/506))
117
+ - Deleted the custom eslint-plugin-testid as it has been replaced by @qlover/eslint-plugin for better testability.
118
+ - Removed the generateLocalesPlugin as its functionality is now integrated into the build process.
119
+ - Added English and Chinese localization files for common application messages to enhance internationalization support.
120
+ - Updated the i18n request configuration to dynamically load the appropriate locale files based on the selected language.
121
+
122
+ These changes aim to simplify the localization setup and improve the overall structure of the internationalization implementation.
123
+
3
124
  ## 0.7.7
4
125
 
5
126
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ ${t}`,dn=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),_n=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,f=this.#g(a," "),p=u+i+s+f+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function tr(e){return new Ze(e)}async function rr(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=tr(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 HD=require("fs");var P=require("path"),me=require("fs"),cr=h(lr(),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:rs,stat:us}=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,cr.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 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}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),kD=h(LD(),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,kD.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 $D=["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,HD.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.FeScriptContext(t),this.subPackages=["node-lib","react-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 WD.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 $D.includes(t)}async getGeneratorContext(){let t=Ot(this.subPackages,$D),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=wt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:(0,v.join)(D,u),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 bt={name:"@qlover/create-app",version:"0.7.7",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:{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 Ul(){let e=new YD.Command;return e.version(bt.version,"-v, --version","Show version").description(bt.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 Wl(){let{dryRun:e,verbose:t,...r}=Ul(),u=(0,vt.resolve)("./templates"),i=(0,vt.resolve)("./configs");(0,xt.existsSync)(u)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(i)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Wl().catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,cr.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 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}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),kD=h(LD(),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,kD.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 $D=["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,HD.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.FeScriptContext(t),this.subPackages=["node-lib","react-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 WD.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 $D.includes(t)}async getGeneratorContext(){let t=Ot(this.subPackages,$D),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=wt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:(0,v.join)(D,u),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 bt={name:"@qlover/create-app",version:"0.7.9",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:{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 Ul(){let e=new YD.Command;return e.version(bt.version,"-v, --version","Show version").description(bt.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 Wl(){let{dryRun:e,verbose:t,...r}=Ul(),u=(0,vt.resolve)("./templates"),i=(0,vt.resolve)("./configs");(0,xt.existsSync)(u)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(i)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Wl().catch(e=>{console.error(e),process.exit(1)});
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ ${t}`,cn=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),fn=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Le(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=E[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&&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??" ",D=t.text??this.text,s=typeof D=="string"?(i?" ":"")+D:"",a=t.suffixText??this.#o,f=this.#g(a," "),p=u+i+s+f+`
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:D}=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(D===void 0?void 0:typeof D=="string"?D:D(s)),s}}import{existsSync as Hl}from"fs";var Dr=ue(ir(),1);import{dirname as Jn,join as Ze}from"path";import{existsSync as Zn,readFileSync as Qn}from"fs";import{promises as nr}from"fs";import{existsSync as Kn,mkdirSync as Xn}from"fs";var H=class{static ensureDir(t){Kn(t)||Xn(t,{recursive:!0})}};var{copyFile:es,stat:ts}=nr,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(!Zn(r))return;let D=Qn(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(D)}async copyFiles(t,r,u,i){let D=await nr.readdir(t);await Promise.all(D.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(Jn(a)),(await ts(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await es(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var ID=ue(qD(),1);import{readFileSync as Ll,writeFileSync as kl,existsSync as $l}from"fs";var me=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 Ll(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){kl(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,ID.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 $l(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var jD=["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(!Hl(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Ul(t),this.subPackages=["node-lib","react-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new me}get logger(){return this.context.logger}async steps(t){try{return await Wl.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 jD.includes(t)}async getGeneratorContext(){let t=_t(this.subPackages,jD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=mt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:N(D,u),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:D}=this.context.options;for(let o of u){let s=N(D,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.7.7",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:{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 zl(){let e=new Vl;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 Kl(){let{dryRun:e,verbose:t,...r}=zl(),u=ND("./templates"),i=ND("./configs");GD(u)||(console.error("Template is empty!"),process.exit(1)),GD(i)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Kl().catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(D)}async copyFiles(t,r,u,i){let D=await nr.readdir(t);await Promise.all(D.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(Jn(a)),(await ts(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await es(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var ID=ue(qD(),1);import{readFileSync as Ll,writeFileSync as kl,existsSync as $l}from"fs";var me=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 Ll(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){kl(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,ID.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 $l(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var jD=["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(!Hl(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Ul(t),this.subPackages=["node-lib","react-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new me}get logger(){return this.context.logger}async steps(t){try{return await Wl.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 jD.includes(t)}async getGeneratorContext(){let t=_t(this.subPackages,jD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=mt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:N(D,u),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:D}=this.context.options;for(let o of u){let s=N(D,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.7.9",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:{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 zl(){let e=new Vl;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 Kl(){let{dryRun:e,verbose:t,...r}=zl(),u=ND("./templates"),i=ND("./configs");GD(u)||(console.error("Template is empty!"),process.exit(1)),GD(i)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Kl().catch(e=>{console.error(e),process.exit(1)});
@@ -15,7 +15,7 @@ export async function generateLocales() {
15
15
  source: path,
16
16
  // You can use namespace
17
17
  // target: `./public/locales/{{lng}}/{{${name}}}.json`
18
- target: `./public/locales/{{lng}}/common.json`
18
+ target: `./public/locales/{{lng}}.json`
19
19
  }));
20
20
 
21
21
  const ts2Locale = new Ts2Locales(locales);
@@ -1,8 +1,11 @@
1
1
  import type { AppConfig } from '@/base/cases/AppConfig';
2
+ import type { DialogHandler } from '@/base/cases/DialogHandler';
3
+ import type { RouterService } from '@/base/cases/RouterService';
2
4
  import type { I18nService } from '@/base/services/I18nService';
5
+ import type { UserService } from '@/base/services/UserService';
3
6
  import type * as CorekitBridge from '@qlover/corekit-bridge';
4
7
  import type * as FeCorekit from '@qlover/fe-corekit';
5
- import type * as Logger from '@qlover/logger';
8
+ import type { LoggerInterface } from '@qlover/logger';
6
9
 
7
10
  /**
8
11
  * IOC identifier
@@ -15,6 +18,8 @@ export const IOCIdentifier = Object.freeze({
15
18
  LocalStorage: 'LocalStorage',
16
19
  LocalStorageEncrypt: 'LocalStorageEncrypt',
17
20
  CookieStorage: 'CookieStorage',
21
+ UserServiceInterface: 'UserServiceInterface',
22
+ RouterServiceInterface: 'RouterServiceInterface',
18
23
  I18nServiceInterface: 'I18nServiceInterface'
19
24
  });
20
25
 
@@ -30,7 +35,7 @@ export const I = IOCIdentifier;
30
35
  */
31
36
  export interface IOCIdentifierMap {
32
37
  [IOCIdentifier.JSONSerializer]: FeCorekit.JSONSerializer;
33
- [IOCIdentifier.Logger]: Logger.Logger;
38
+ [IOCIdentifier.Logger]: LoggerInterface;
34
39
  [IOCIdentifier.LocalStorage]: FeCorekit.SyncStorage<
35
40
  unknown,
36
41
  FeCorekit.ObjectStorageOptions
@@ -41,5 +46,13 @@ export interface IOCIdentifierMap {
41
46
  >;
42
47
  [IOCIdentifier.CookieStorage]: CorekitBridge.CookieStorage;
43
48
  [IOCIdentifier.AppConfig]: AppConfig;
49
+ [IOCIdentifier.UserServiceInterface]: UserService;
50
+ [IOCIdentifier.RouterServiceInterface]: RouterService;
44
51
  [IOCIdentifier.I18nServiceInterface]: I18nService;
52
+ [IOCIdentifier.DialogHandler]: DialogHandler;
53
+ }
54
+
55
+ export interface IOCIdentifierMapServer {
56
+ [IOCIdentifier.AppConfig]: AppConfig;
57
+ [IOCIdentifier.Logger]: LoggerInterface;
45
58
  }
@@ -32,3 +32,10 @@ export const RES_NO_TOKEN = 'err__response__no__token';
32
32
  * @localEn Not found component
33
33
  */
34
34
  export const NOT_FOUND_COMPONENT = 'err__not__found__component';
35
+
36
+ /**
37
+ * @description 服务器认证错误
38
+ * @localZh 服务器认证错误
39
+ * @localEn Server auth error
40
+ */
41
+ export const SERVER_AUTH_ERROR = 'err__server__auth__error';
@@ -12,6 +12,13 @@ export const PAGE_HOME_TITLE = 'page__home__title';
12
12
  */
13
13
  export const PAGE_HOME_DESCRIPTION = 'page__home__description';
14
14
 
15
+ /**
16
+ * @description Home page keywords
17
+ * @localZh 现代前端实用库, 实用工具, 组件
18
+ * @localEn Modern frontend utility library, practical tools, components
19
+ */
20
+ export const PAGE_HOME_KEYWORDS = 'page__home__keywords';
21
+
15
22
  /**
16
23
  * @description Home page welcome message
17
24
  * @localZh 欢迎来到主页
@@ -0,0 +1,22 @@
1
+ import * as i18nKeys from '../Identifier/page.home';
2
+
3
+ /**
4
+ * Home page i18n interface
5
+ *
6
+ * @description
7
+ * - welcome: welcome message
8
+ */
9
+ export type HomeI18nInterface = typeof homeI18n;
10
+
11
+ export const homeI18n = Object.freeze({
12
+ // basic meta properties
13
+ title: i18nKeys.PAGE_HOME_TITLE,
14
+ description: i18nKeys.PAGE_HOME_DESCRIPTION,
15
+ content: i18nKeys.PAGE_HOME_DESCRIPTION,
16
+ keywords: i18nKeys.PAGE_HOME_KEYWORDS,
17
+
18
+ welcome: i18nKeys.HOME_WELCOME,
19
+ getStartedTitle: i18nKeys.HOME_GET_STARTED_TITLE,
20
+ getStartedDescription: i18nKeys.HOME_GET_STARTED_DESCRIPTION,
21
+ getStartedButton: i18nKeys.HOME_GET_STARTED_BUTTON
22
+ });
@@ -20,4 +20,5 @@ export const themeConfig = {
20
20
  } as ThemeConfig
21
21
  } as const;
22
22
 
23
+ export type SupportedTheme = (typeof themeConfig.supportedThemes)[number];
23
24
  export type CommonThemeConfig = typeof themeConfig;
@@ -3,13 +3,13 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "cross-env APP_ENV=localhost next dev --turbopack",
7
- "dev:staging": "cross-env APP_ENV=staging next dev --turbopack",
8
- "dev:prod": "cross-env APP_ENV=production next dev --turbopack",
6
+ "dev": "cross-env APP_ENV=localhost next dev --turbopack --port 3100",
7
+ "dev:staging": "cross-env APP_ENV=staging next dev --turbopack --port 3100",
8
+ "dev:prod": "cross-env APP_ENV=production next dev --turbopack --port 3100",
9
9
  "build": "cross-env APP_ENV=localhost next build --turbopack",
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
- "start": "next start",
12
+ "start": "next start --port 3101",
13
13
  "lint": "eslint .",
14
14
  "lint:fix": "eslint . --ext .ts,.tsx --fix",
15
15
  "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
@@ -43,7 +43,7 @@
43
43
  "@types/node": "^20",
44
44
  "@types/react": "^19",
45
45
  "@types/react-dom": "^19",
46
- "@qlover/eslint-plugin": "file:../../../packages/eslint-plugin",
46
+ "@qlover/eslint-plugin": "latest",
47
47
  "cross-env": "^7.0.3",
48
48
  "eslint": "^9",
49
49
  "eslint-config-next": "15.5.0",
@@ -179,5 +179,7 @@
179
179
  "page__request__trigger_api_catch": "Trigger API Catch Result",
180
180
  "page__request__stop_api_catch": "Stop API Catch Result",
181
181
  "page__login__content": "Login Page Content",
182
- "page__login__keywords": "Login Page Keywords"
182
+ "page__login__keywords": "Login Page Keywords",
183
+ "err__server__auth__error": "Server auth error",
184
+ "page__home__keywords": "Modern frontend utility library, practical tools, components"
183
185
  }
@@ -179,5 +179,7 @@
179
179
  "page__request__trigger_api_catch": "触发 API 捕获结果",
180
180
  "page__request__stop_api_catch": "停止 API 捕获结果",
181
181
  "page__login__content": "登录页面内容",
182
- "page__login__keywords": "登录页面关键词"
182
+ "page__login__keywords": "登录页面关键词",
183
+ "err__server__auth__error": "服务器认证错误",
184
+ "page__home__keywords": "现代前端实用库, 实用工具, 组件"
183
185
  }
@@ -1,42 +1,24 @@
1
- import { notFound } from 'next/navigation';
2
1
  import { NextIntlClientProvider } from 'next-intl';
3
- import { getMessages } from 'next-intl/server';
4
- import { i18nConfig } from '@config/i18n';
5
2
  import { themeConfig } from '@config/theme';
6
- import { BaseHeader } from '@/uikit/components/BaseHeader';
3
+ import { PageParams } from '@/base/cases/PageParams';
4
+ import type { PageLayoutProps } from '@/base/types/PageProps';
7
5
  import { ComboProvider } from '@/uikit/components/ComboProvider';
8
- import type { LocaleType } from '@config/i18n';
9
6
  import '@/styles/css/index.css';
10
7
 
11
8
  export default async function RootLayout({
12
9
  children,
13
10
  params
14
- }: {
15
- children: React.ReactNode;
16
- params: Promise<{ locale: string }>;
17
- }) {
18
- // Extract the locale from the route params (async for Next.js App Router)
19
- const { locale } = await params;
20
-
21
- // Validate that the requested locale is supported
22
- if (!i18nConfig.supportedLngs.includes(locale as LocaleType)) {
23
- notFound();
24
- }
25
-
26
- // Load the translation messages for the selected locale
27
- const messages = await getMessages({ locale });
11
+ }: PageLayoutProps) {
12
+ const pageParams = new PageParams(await params!);
13
+ const locale = pageParams.getLocale();
14
+ const messages = await pageParams.getI18nMessages();
28
15
 
29
16
  // TODO: suppressHydrationWarning 暂时解决 hydration 问题
30
17
  return (
31
18
  <html data-testid="RootLayout" lang={locale} suppressHydrationWarning>
32
19
  <body>
33
- <NextIntlClientProvider messages={messages}>
34
- <ComboProvider themeConfig={themeConfig}>
35
- <div className="flex flex-col min-h-screen">
36
- <BaseHeader />
37
- <div className="flex flex-col">{children}</div>
38
- </div>
39
- </ComboProvider>
20
+ <NextIntlClientProvider locale={locale} messages={messages}>
21
+ <ComboProvider themeConfig={themeConfig}>{children}</ComboProvider>
40
22
  </NextIntlClientProvider>
41
23
  </body>
42
24
  </html>
@@ -3,9 +3,9 @@
3
3
  import { UserOutlined, LockOutlined, GoogleOutlined } from '@ant-design/icons';
4
4
  import { Form, Input, Button } from 'antd';
5
5
  import { useState } from 'react';
6
- import { UserService } from '@/base/services/UserService';
7
- import { IOC } from '@/core/IOC';
6
+ import { I } from '@config/IOCIdentifier';
8
7
  import { LocaleLink } from '@/uikit/components/LocaleLink';
8
+ import { useIOC } from '@/uikit/hook/useIOC';
9
9
  import type { LoginI18nInterface } from '@config/i18n/loginI18n';
10
10
 
11
11
  interface LoginFormData {
@@ -15,16 +15,15 @@ interface LoginFormData {
15
15
 
16
16
  export function LoginForm(props: { tt: LoginI18nInterface }) {
17
17
  const { tt } = props;
18
- const userService = IOC(UserService);
18
+ const userService = useIOC(I.UserServiceInterface);
19
+ const routerService = useIOC(I.RouterServiceInterface);
19
20
  const [loading, setLoading] = useState(false);
20
21
 
21
22
  const handleLogin = async (values: LoginFormData) => {
22
23
  try {
23
24
  setLoading(true);
24
- await userService.login({
25
- email: values.email,
26
- password: values.password
27
- });
25
+ await userService.login(values);
26
+ routerService.gotoHome();
28
27
  } catch (error) {
29
28
  console.error('Login error:', error);
30
29
  } finally {
@@ -48,7 +47,7 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
48
47
  prefix={<UserOutlined className="text-text-tertiary" />}
49
48
  placeholder={tt.email}
50
49
  title={tt.emailTitle}
51
- className="h-12 text-base bg-secondary border-border"
50
+ className="h-12 text-base bg-secondary border-c-border"
52
51
  autoComplete="off"
53
52
  />
54
53
  </Form.Item>
@@ -67,11 +66,7 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
67
66
  </Form.Item>
68
67
 
69
68
  <div className="flex justify-end">
70
- <LocaleLink
71
- href="#"
72
- className="text-brand hover:text-brand-hover"
73
- title={tt.forgotPasswordTitle}
74
- >
69
+ <LocaleLink href="#" title={tt.forgotPasswordTitle}>
75
70
  {tt.forgotPassword}
76
71
  </LocaleLink>
77
72
  </div>
@@ -102,11 +97,7 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
102
97
 
103
98
  <div className="text-center mt-6">
104
99
  <span className="text-text-tertiary">{tt.noAccount} </span>
105
- <LocaleLink
106
- href="/register"
107
- className="text-brand hover:text-brand-hover"
108
- title={tt.createAccountTitle}
109
- >
100
+ <LocaleLink href="/register" title={tt.createAccountTitle}>
110
101
  {tt.createAccount}
111
102
  </LocaleLink>
112
103
  </div>
@@ -1,6 +1,11 @@
1
+ import { notFound } from 'next/navigation';
1
2
  import { loginI18n, i18nConfig } from '@config/i18n';
2
- import { getServerI18n } from '@/server/getServerI18n';
3
- import { useI18nInterface } from '@/uikit/hook/useI18nInterface';
3
+ import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
4
+ import { ServerAuth } from '@/base/cases/ServerAuth';
5
+ import type { PageParamsProps } from '@/base/types/PageProps';
6
+ import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
7
+ import { redirect } from '@/i18n/routing';
8
+ import { BaseLayout } from '@/uikit/components/BaseLayout';
4
9
  import { FeatureItem } from './FeatureItem';
5
10
  import { LoginForm } from './LoginForm';
6
11
  import type { Metadata } from 'next';
@@ -21,35 +26,35 @@ export const dynamic = 'auto'; // Enable static generation when possible, fallba
21
26
  export async function generateMetadata({
22
27
  params
23
28
  }: {
24
- params: Promise<{ locale: string }>;
29
+ params: Promise<PageParamsType>;
25
30
  }): Promise<Metadata> {
26
- const { locale } = await params;
31
+ const pageParams = new PageParams(await params);
27
32
 
28
- const tt = await getServerI18n({
29
- locale,
30
- i18nInterface: loginI18n
31
- });
32
-
33
- // Return localized SEO metadata
34
- return tt;
33
+ return await pageParams.getI18nInterface(loginI18n);
35
34
  }
36
35
 
37
- export default function LoginPage() {
38
- const tt = useI18nInterface(loginI18n);
36
+ export default async function LoginPage(props: PageParamsProps) {
37
+ if (!props.params) {
38
+ return notFound();
39
+ }
40
+
41
+ const params = await props.params;
42
+ const pageParams = new PageParams(params);
43
+
44
+ const server = new BootstrapServer();
45
+
46
+ if (await new ServerAuth(server).hasAuth()) {
47
+ return redirect({ href: '/', locale: params.locale! });
48
+ }
49
+
50
+ const tt = await pageParams.getI18nInterface(loginI18n);
39
51
 
40
52
  return (
41
- <div
53
+ <BaseLayout
42
54
  data-testid="LoginPage"
43
- className="flex text-xs1 bg-primary min-h-screen"
55
+ className="text-xs1 bg-primary flex flex-col min-h-screen"
44
56
  >
45
- {/* Left side - Brand section */}
46
57
  <div className="hidden lg:flex bg-secondary lg:w-1/2 p-12 flex-col">
47
- <div className="flex items-center gap-3 mb-12">
48
- <div className="w-10 h-10 bg-brand rounded-lg"></div>
49
- <span className="text-2xl font-semibold text-text">
50
- {'AppConfig.appName'}
51
- </span>
52
- </div>
53
58
  <h1 className="text-4xl font-bold text-text mb-4">{tt.welcome}</h1>
54
59
  <p className="text-text-secondary text-lg mb-8">{tt.subtitle}</p>
55
60
  <div className="space-y-4">
@@ -59,7 +64,6 @@ export default function LoginPage() {
59
64
  </div>
60
65
  </div>
61
66
 
62
- {/* Right side - Login form */}
63
67
  <div className="w-full lg:w-1/2 p-8 sm:p-12 flex items-center justify-center">
64
68
  <div className="w-full max-w-[420px]">
65
69
  <h2 className="text-2xl font-semibold mb-2 text-text">{tt.title}</h2>
@@ -68,6 +72,6 @@ export default function LoginPage() {
68
72
  <LoginForm tt={tt} />
69
73
  </div>
70
74
  </div>
71
- </div>
75
+ </BaseLayout>
72
76
  );
73
77
  }