@qlover/create-app 0.7.5 → 0.7.6
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 +20 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/react-app/.env.template +0 -2
- package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +1 -1
- package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +6 -31
- package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +1 -1
- package/dist/templates/react-app/config/IOCIdentifier.ts +77 -5
- package/dist/templates/react-app/config/app.router.ts +2 -2
- package/dist/templates/react-app/package.json +4 -7
- package/dist/templates/react-app/public/locales/en/common.json +1 -1
- package/dist/templates/react-app/public/locales/zh/common.json +1 -1
- package/dist/templates/react-app/src/App.tsx +9 -4
- package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +1 -1
- package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +4 -0
- package/dist/templates/react-app/src/base/cases/DialogHandler.ts +16 -13
- package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +4 -3
- package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +2 -2
- package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +39 -0
- package/dist/templates/react-app/src/base/cases/RequestState.ts +20 -0
- package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +2 -2
- package/dist/templates/react-app/src/base/cases/RouterLoader.ts +8 -2
- package/dist/templates/react-app/src/base/port/AsyncStateInterface.ts +7 -0
- package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +24 -0
- package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +10 -0
- package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +20 -0
- package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +20 -0
- package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +23 -0
- package/dist/templates/react-app/src/base/port/RequestStatusInterface.ts +5 -0
- package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +27 -0
- package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +12 -0
- package/dist/templates/react-app/src/base/services/I18nService.ts +10 -6
- package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +23 -5
- package/dist/templates/react-app/src/base/services/RouteService.ts +25 -54
- package/dist/templates/react-app/src/base/services/UserService.ts +10 -20
- package/dist/templates/react-app/src/core/IOC.ts +1 -26
- package/dist/templates/react-app/src/core/IocRegisterImpl.ts +125 -0
- package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +4 -6
- package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +8 -6
- package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
- package/dist/templates/react-app/src/pages/auth/Layout.tsx +2 -2
- package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +5 -6
- package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +8 -7
- package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +8 -19
- package/dist/templates/react-app/src/pages/base/{ErrorIdentifierPage.tsx → IdentifierPage.tsx} +1 -1
- package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +11 -15
- package/dist/templates/react-app/src/pages/base/Layout.tsx +1 -1
- package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +2 -2
- package/dist/templates/react-app/src/pages/base/RequestPage.tsx +34 -46
- package/dist/templates/react-app/src/styles/css/antd-themes/_default.css +2 -2
- package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +2 -2
- package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +2 -2
- package/dist/templates/react-app/src/styles/css/index.css +1 -0
- package/dist/templates/react-app/src/styles/css/page.css +8 -0
- package/dist/templates/react-app/src/styles/css/zIndex.css +9 -0
- package/dist/templates/react-app/src/uikit/{controllers/ExecutorController.ts → bridges/ExecutorPageBridge.ts} +13 -36
- package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +41 -0
- package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +16 -0
- package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +136 -0
- package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +3 -2
- package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +2 -2
- package/dist/templates/react-app/src/uikit/{providers → components}/ProcessExecutorProvider.tsx +4 -4
- package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +1 -1
- package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +3 -5
- package/dist/templates/react-app/src/uikit/{providers → components}/UserAuthProvider.tsx +3 -3
- package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +5 -2
- package/dist/templates/react-app/src/uikit/hooks/{userRouterService.ts → useNavigateBridge.ts} +3 -3
- package/dist/templates/react-app/src/uikit/hooks/useStore.ts +5 -2
- package/dist/templates/react-app/tsconfig.json +1 -4
- package/package.json +1 -1
- package/dist/templates/react-app/src/base/port/InteractionHubInterface.ts +0 -94
- package/dist/templates/react-app/src/base/port/UIDependenciesInterface.ts +0 -37
- package/dist/templates/react-app/src/core/registers/IocRegisterImpl.ts +0 -25
- package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +0 -74
- package/dist/templates/react-app/src/core/registers/RegisterControllers.ts +0 -26
- package/dist/templates/react-app/src/core/registers/RegisterGlobals.ts +0 -30
- package/dist/templates/react-app/src/uikit/controllers/JSONStorageController.ts +0 -49
- package/dist/templates/react-app/src/uikit/controllers/RequestController.ts +0 -158
- /package/dist/templates/react-app/src/uikit/{providers → components}/BaseRouteProvider.tsx +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @qlover/create-app
|
|
2
2
|
|
|
3
|
+
## 0.7.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
#### ✨ Features
|
|
8
|
+
|
|
9
|
+
- **ui:** introduce UI bridge interfaces and notification system ([6969561](https://github.com/qlover/fe-base/commit/6969561b3f4603bb51ae7ad5fdca9e539d75209b)) ([#499](https://github.com/qlover/fe-base/pull/499))
|
|
10
|
+
- Added UIBridgeInterface for decoupling business logic from UI components.
|
|
11
|
+
- Implemented UINotificationInterface and UIDialogInterface for standardized notification handling.
|
|
12
|
+
- Created NavigateBridge to facilitate navigation integration with React Router.
|
|
13
|
+
- Updated RouteService to utilize the new UIBridgeInterface for improved dependency management.
|
|
14
|
+
- Introduced useNavigateBridge hook for setting up navigation in React components.
|
|
15
|
+
- Removed obsolete UIDependenciesInterface to streamline the codebase.
|
|
16
|
+
|
|
17
|
+
- **dialog:** enhance UIDialogInterface and integrate into DialogHandler ([d9d2cdd](https://github.com/qlover/fe-base/commit/d9d2cdd68ff2a68675a2abdd7339c7be0c706724)) ([#499](https://github.com/qlover/fe-base/pull/499))
|
|
18
|
+
- Updated UIDialogInterface to use generic types for notification options, improving flexibility.
|
|
19
|
+
- Integrated UIDialogInterface into DialogHandler for better type safety and consistency in dialog handling.
|
|
20
|
+
- Adjusted RouteService to streamline navigation handling and improve code clarity.
|
|
21
|
+
- Updated package.json to reference local paths for corekit-bridge and fe-corekit for development.
|
|
22
|
+
|
|
3
23
|
## 0.7.5
|
|
4
24
|
|
|
5
25
|
### 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.
|
|
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.6",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.
|
|
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.6",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)});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { IOC, type IOCContainer
|
|
1
|
+
import { IOC, type IOCContainer } from '@/core/IOC';
|
|
2
2
|
import { InversifyContainer } from '@/base/cases/InversifyContainer';
|
|
3
|
-
import { IocRegisterImpl } from '@/core/
|
|
3
|
+
import { IocRegisterImpl } from '@/core/IocRegisterImpl';
|
|
4
4
|
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
5
5
|
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
6
6
|
|
|
@@ -35,7 +35,8 @@ describe('IOC Container Tests', () => {
|
|
|
35
35
|
aiApiToken: '',
|
|
36
36
|
aiApiTokenPrefix: 'Bearer',
|
|
37
37
|
aiApiRequireToken: true,
|
|
38
|
-
bootHref: ''
|
|
38
|
+
bootHref: '',
|
|
39
|
+
isProduction: false
|
|
39
40
|
};
|
|
40
41
|
});
|
|
41
42
|
|
|
@@ -102,12 +103,6 @@ describe('IOC Container Tests', () => {
|
|
|
102
103
|
expect(registerImpl).toBeInstanceOf(IocRegisterImpl);
|
|
103
104
|
});
|
|
104
105
|
|
|
105
|
-
it('should return register list', () => {
|
|
106
|
-
const registerList = registerImpl.getRegisterList();
|
|
107
|
-
expect(Array.isArray(registerList)).toBe(true);
|
|
108
|
-
expect(registerList.length).toBeGreaterThan(0);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
106
|
it('should register services without throwing', () => {
|
|
112
107
|
const mockManager = {
|
|
113
108
|
get: vi.fn(),
|
|
@@ -125,34 +120,14 @@ describe('IOC Container Tests', () => {
|
|
|
125
120
|
describe('IOCIdentifierMap Type Safety', () => {
|
|
126
121
|
it('should have correct type mapping for JSON', () => {
|
|
127
122
|
// This test verifies that the type mapping is correct
|
|
128
|
-
const identifierMap
|
|
123
|
+
const identifierMap = {};
|
|
129
124
|
|
|
130
125
|
// TypeScript should enforce that these keys exist
|
|
131
|
-
expect(IOCIdentifier.
|
|
126
|
+
expect(IOCIdentifier.JSONSerializer in identifierMap).toBeDefined();
|
|
132
127
|
expect(IOCIdentifier.LocalStorage in identifierMap).toBeDefined();
|
|
133
128
|
expect(IOCIdentifier.Logger in identifierMap).toBeDefined();
|
|
134
129
|
expect(IOCIdentifier.AppConfig in identifierMap).toBeDefined();
|
|
135
130
|
});
|
|
136
|
-
|
|
137
|
-
it('should have all required identifiers', () => {
|
|
138
|
-
const requiredIdentifiers = [
|
|
139
|
-
'JSON',
|
|
140
|
-
'LocalStorage',
|
|
141
|
-
'LocalStorageEncrypt',
|
|
142
|
-
'CookieStorage',
|
|
143
|
-
'Logger',
|
|
144
|
-
'FeApiToken',
|
|
145
|
-
'FeApiCommonPlugin',
|
|
146
|
-
'AppConfig',
|
|
147
|
-
'ApiMockPlugin',
|
|
148
|
-
'ApiCatchPlugin',
|
|
149
|
-
'DialogHandler'
|
|
150
|
-
];
|
|
151
|
-
|
|
152
|
-
requiredIdentifiers.forEach((identifier) => {
|
|
153
|
-
expect(IOCIdentifier).toHaveProperty(identifier);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
131
|
});
|
|
157
132
|
|
|
158
133
|
describe('Service Registration Integration', () => {
|
|
@@ -2,7 +2,7 @@ import { InversifyContainer } from '@/base/cases/InversifyContainer';
|
|
|
2
2
|
import { BootstrapApp } from '@/core/bootstraps/BootstrapApp';
|
|
3
3
|
import type { BootstrapAppArgs } from '@/core/bootstraps/BootstrapApp';
|
|
4
4
|
import { createIOCFunction } from '@qlover/corekit-bridge';
|
|
5
|
-
import type { IOCIdentifierMap } from '
|
|
5
|
+
import type { IOCIdentifierMap } from '@config/IOCIdentifier';
|
|
6
6
|
import { name, version } from '../../../../package.json';
|
|
7
7
|
import { browserGlobalsName } from '@config/common';
|
|
8
8
|
|
|
@@ -1,13 +1,85 @@
|
|
|
1
|
+
import type * as CorekitBridge from '@qlover/corekit-bridge';
|
|
2
|
+
import type * as FeCorekit from '@qlover/fe-corekit';
|
|
3
|
+
import type * as Logger from '@qlover/logger';
|
|
4
|
+
import type { DialogHandler } from '@/base/cases/DialogHandler';
|
|
5
|
+
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
6
|
+
import type { RequestStatusCatcher } from '@/base/cases/RequestStatusCatcher';
|
|
7
|
+
import type { I18nService } from '@/base/services/I18nService';
|
|
8
|
+
import type { ProcesserExecutor } from '@/base/services/ProcesserExecutor';
|
|
9
|
+
import type { RouteService } from '@/base/services/RouteService';
|
|
10
|
+
import type { UserService } from '@/base/services/UserService';
|
|
11
|
+
import type { I18nKeyErrorPlugin } from '@/base/cases/I18nKeyErrorPlugin';
|
|
12
|
+
import type { ExecutorPageBridgeInterface } from '@/base/port/ExecutorPageBridgeInterface';
|
|
13
|
+
import type { JSONStoragePageBridgeInterface } from '@/base/port/JSONStoragePageBridgeInterface';
|
|
14
|
+
import type { RequestPageBridgeInterface } from '@/base/port/RequestPageBridgeInterface';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* IOC identifier
|
|
18
|
+
*/
|
|
1
19
|
export const IOCIdentifier = Object.freeze({
|
|
2
|
-
|
|
20
|
+
JSONSerializer: 'JSONSerializer',
|
|
21
|
+
Logger: 'Logger',
|
|
22
|
+
AppConfig: 'AppConfig',
|
|
23
|
+
DialogHandler: 'DialogHandler',
|
|
3
24
|
LocalStorage: 'LocalStorage',
|
|
4
25
|
LocalStorageEncrypt: 'LocalStorageEncrypt',
|
|
5
26
|
CookieStorage: 'CookieStorage',
|
|
6
|
-
|
|
7
|
-
|
|
27
|
+
EnvConfigInterface: 'EnvConfigInterface',
|
|
28
|
+
UIDialogInterface: 'UIDialogInterface',
|
|
29
|
+
AntdStaticApiInterface: 'AntdStaticApiInterface',
|
|
30
|
+
RequestCatcherInterface: 'RequestCatcherInterface',
|
|
31
|
+
I18nServiceInterface: 'I18nServiceInterface',
|
|
32
|
+
ProcesserExecutorInterface: 'ProcesserExecutorInterface',
|
|
33
|
+
RouteServiceInterface: 'RouteServiceInterface',
|
|
34
|
+
UserServiceInterface: 'UserServiceInterface',
|
|
35
|
+
I18nKeyErrorPlugin: 'I18nKeyErrorPlugin',
|
|
8
36
|
FeApiCommonPlugin: 'FeApiCommonPlugin',
|
|
9
|
-
AppConfig: 'AppConfig',
|
|
10
37
|
ApiMockPlugin: 'ApiMockPlugin',
|
|
11
38
|
ApiCatchPlugin: 'ApiCatchPlugin',
|
|
12
|
-
|
|
39
|
+
ThemeService: 'ThemeService',
|
|
40
|
+
ExecutorPageBridgeInterface: 'ExecutorPageBridgeInterface',
|
|
41
|
+
JSONStoragePageInterface: 'JSONStoragePageInterface',
|
|
42
|
+
RequestPageBridgeInterface: 'RequestPageBridgeInterface'
|
|
13
43
|
});
|
|
44
|
+
|
|
45
|
+
export const I = IOCIdentifier;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* IOC identifier map
|
|
49
|
+
*
|
|
50
|
+
* Define the implementation class corresponding to the string identifier
|
|
51
|
+
*
|
|
52
|
+
* - key: interface alias or name
|
|
53
|
+
* - value: implementation class
|
|
54
|
+
*/
|
|
55
|
+
export interface IOCIdentifierMap {
|
|
56
|
+
[IOCIdentifier.JSONSerializer]: FeCorekit.JSONSerializer;
|
|
57
|
+
[IOCIdentifier.Logger]: Logger.Logger;
|
|
58
|
+
[IOCIdentifier.AppConfig]: AppConfig;
|
|
59
|
+
[IOCIdentifier.DialogHandler]: DialogHandler;
|
|
60
|
+
[IOCIdentifier.LocalStorage]: FeCorekit.SyncStorage<
|
|
61
|
+
unknown,
|
|
62
|
+
FeCorekit.ObjectStorageOptions
|
|
63
|
+
>;
|
|
64
|
+
[IOCIdentifier.LocalStorageEncrypt]: FeCorekit.SyncStorage<
|
|
65
|
+
unknown,
|
|
66
|
+
FeCorekit.ObjectStorageOptions
|
|
67
|
+
>;
|
|
68
|
+
[IOCIdentifier.CookieStorage]: CorekitBridge.CookieStorage;
|
|
69
|
+
[IOCIdentifier.EnvConfigInterface]: AppConfig;
|
|
70
|
+
[IOCIdentifier.UIDialogInterface]: DialogHandler;
|
|
71
|
+
[IOCIdentifier.AntdStaticApiInterface]: DialogHandler;
|
|
72
|
+
[IOCIdentifier.RequestCatcherInterface]: RequestStatusCatcher;
|
|
73
|
+
[IOCIdentifier.I18nServiceInterface]: I18nService;
|
|
74
|
+
[IOCIdentifier.ProcesserExecutorInterface]: ProcesserExecutor;
|
|
75
|
+
[IOCIdentifier.RouteServiceInterface]: RouteService;
|
|
76
|
+
[IOCIdentifier.UserServiceInterface]: UserService;
|
|
77
|
+
[IOCIdentifier.I18nKeyErrorPlugin]: I18nKeyErrorPlugin;
|
|
78
|
+
[IOCIdentifier.FeApiCommonPlugin]: CorekitBridge.RequestCommonPlugin;
|
|
79
|
+
[IOCIdentifier.ApiMockPlugin]: CorekitBridge.ApiMockPlugin;
|
|
80
|
+
[IOCIdentifier.ApiCatchPlugin]: CorekitBridge.ApiCatchPlugin;
|
|
81
|
+
[IOCIdentifier.ThemeService]: CorekitBridge.ThemeService;
|
|
82
|
+
[IOCIdentifier.ExecutorPageBridgeInterface]: ExecutorPageBridgeInterface;
|
|
83
|
+
[IOCIdentifier.JSONStoragePageInterface]: JSONStoragePageBridgeInterface;
|
|
84
|
+
[IOCIdentifier.RequestPageBridgeInterface]: RequestPageBridgeInterface;
|
|
85
|
+
}
|
|
@@ -64,8 +64,8 @@ export const baseRoutes: RouteConfigValue[] = [
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
|
-
path: '
|
|
68
|
-
element: 'base/
|
|
67
|
+
path: 'identifier',
|
|
68
|
+
element: 'base/IdentifierPage',
|
|
69
69
|
meta: {
|
|
70
70
|
title: identifier.PAGE_ERROR_IDENTIFIER_TITLE,
|
|
71
71
|
description: identifier.PAGE_ERROR_IDENTIFIER_DESCRIPTION,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"version": "0.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": true,
|
|
7
|
-
"homepage": "",
|
|
7
|
+
"homepage": "https://github.com/qlover/fe-base/tree/master/packages/create-app/templates/react-app#readme",
|
|
8
8
|
"author": "qlover",
|
|
9
9
|
"license": "ISC",
|
|
10
10
|
"repository": {
|
|
@@ -26,9 +26,6 @@
|
|
|
26
26
|
"publishConfig": {
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
|
-
"bin": {
|
|
30
|
-
"fe-release": "./bin/release.js"
|
|
31
|
-
},
|
|
32
29
|
"scripts": {
|
|
33
30
|
"dev": "vite --mode localhost",
|
|
34
31
|
"dev:staging": "vite --mode staging",
|
|
@@ -44,7 +41,7 @@
|
|
|
44
41
|
"@qlover/corekit-bridge": "latest",
|
|
45
42
|
"@qlover/fe-corekit": "latest",
|
|
46
43
|
"@qlover/logger": "^0.1.1",
|
|
47
|
-
"@qlover/slice-store-react": "^1.
|
|
44
|
+
"@qlover/slice-store-react": "^1.4.1",
|
|
48
45
|
"@tailwindcss/postcss": "^4.1.8",
|
|
49
46
|
"@tailwindcss/vite": "^4.1.8",
|
|
50
47
|
"antd": "^5.25.3",
|
|
@@ -64,9 +61,9 @@
|
|
|
64
61
|
"devDependencies": {
|
|
65
62
|
"@brain-toolkit/ts2locales": "^0.2.3",
|
|
66
63
|
"@qlover/env-loader": "latest",
|
|
67
|
-
"@qlover/eslint-plugin": "
|
|
64
|
+
"@qlover/eslint-plugin": "latest",
|
|
68
65
|
"@qlover/fe-scripts": "latest",
|
|
69
|
-
"@qlover/fe-standard": "
|
|
66
|
+
"@qlover/fe-standard": "latest",
|
|
70
67
|
"@testing-library/react": "^16.3.0",
|
|
71
68
|
"@types/lodash": "^4.17.13",
|
|
72
69
|
"@types/react": "^18.3.11",
|
|
@@ -3,11 +3,12 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
|
|
3
3
|
import { lazy, useMemo } from 'react';
|
|
4
4
|
import { RouterRenderComponent } from './uikit/components/RouterRenderComponent';
|
|
5
5
|
import { IOC } from './core/IOC';
|
|
6
|
-
import { RouteService } from './base/services/RouteService';
|
|
7
6
|
import { RouterLoader, type ComponentValue } from '@/base/cases/RouterLoader';
|
|
8
7
|
import { AntdThemeProvider } from '@brain-toolkit/antd-theme-override/react';
|
|
9
8
|
import { routerPrefix } from '@config/common';
|
|
10
9
|
import { useStore } from './uikit/hooks/useStore';
|
|
10
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
11
|
+
import { logger } from './core/globals';
|
|
11
12
|
|
|
12
13
|
function getAllPages() {
|
|
13
14
|
const modules = import.meta.glob('./pages/**/*.tsx');
|
|
@@ -25,11 +26,15 @@ function getAllPages() {
|
|
|
25
26
|
|
|
26
27
|
const routerLoader = new RouterLoader({
|
|
27
28
|
componentMaps: getAllPages(),
|
|
28
|
-
render: RouterRenderComponent
|
|
29
|
+
render: RouterRenderComponent,
|
|
30
|
+
logger: logger
|
|
29
31
|
});
|
|
30
32
|
|
|
31
33
|
function App() {
|
|
32
|
-
const routes = useStore(
|
|
34
|
+
const routes = useStore(
|
|
35
|
+
IOC(IOCIdentifier.RouteServiceInterface),
|
|
36
|
+
(state) => state.routes
|
|
37
|
+
);
|
|
33
38
|
|
|
34
39
|
const routerBase = useMemo(() => {
|
|
35
40
|
const routeList = routes.map((route) => routerLoader.toRoute(route));
|
|
@@ -41,7 +46,7 @@ function App() {
|
|
|
41
46
|
|
|
42
47
|
return (
|
|
43
48
|
<AntdThemeProvider
|
|
44
|
-
staticApi={IOC('
|
|
49
|
+
staticApi={IOC('AntdStaticApiInterface')}
|
|
45
50
|
theme={{
|
|
46
51
|
cssVar: {
|
|
47
52
|
key: 'fe-theme',
|
|
@@ -37,7 +37,7 @@ export class UserApi
|
|
|
37
37
|
protected store: UserAuthStoreInterface<UserInfo> | null = null;
|
|
38
38
|
|
|
39
39
|
constructor(
|
|
40
|
-
@inject(FetchAbortPlugin)
|
|
40
|
+
@inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin,
|
|
41
41
|
@inject(UserApiAdapter) adapter: RequestAdapterFetch
|
|
42
42
|
) {
|
|
43
43
|
super(adapter);
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type ApiCatchPluginResponse
|
|
17
17
|
} from '@qlover/corekit-bridge';
|
|
18
18
|
import { UserApi } from './UserApi';
|
|
19
|
+
import { RequestLanguages } from '../../cases/RequestLanguages';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* UserApiConfig
|
|
@@ -68,6 +69,9 @@ export class UserApiBootstarp implements BootstrapExecutorPlugin {
|
|
|
68
69
|
.get<UserApi>(UserApi)
|
|
69
70
|
.usePlugin(new FetchURLPlugin())
|
|
70
71
|
.usePlugin(IOC.get(IOCIdentifier.FeApiCommonPlugin))
|
|
72
|
+
.usePlugin(
|
|
73
|
+
new RequestLanguages(ioc.get(IOCIdentifier.I18nServiceInterface))
|
|
74
|
+
)
|
|
71
75
|
.usePlugin(IOC.get(IOCIdentifier.ApiMockPlugin))
|
|
72
76
|
.usePlugin(IOC.get(RequestLogger))
|
|
73
77
|
.usePlugin(IOC.get(FetchAbortPlugin))
|
|
@@ -4,11 +4,14 @@ import type {
|
|
|
4
4
|
ModalApi,
|
|
5
5
|
NotificationApi
|
|
6
6
|
} from '@brain-toolkit/antd-theme-override/react';
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
import { UIDialogInterface, NotificationOptions } from '@qlover/corekit-bridge';
|
|
8
|
+
import { ModalFuncProps } from 'antd';
|
|
9
|
+
|
|
10
|
+
export interface DialogHandlerOptions
|
|
11
|
+
extends NotificationOptions,
|
|
12
|
+
ModalFuncProps {
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* Dialog Handler Implementation
|
|
@@ -32,9 +35,9 @@ import type {
|
|
|
32
35
|
* });
|
|
33
36
|
*/
|
|
34
37
|
export class DialogHandler
|
|
35
|
-
implements
|
|
38
|
+
implements UIDialogInterface<DialogHandlerOptions>, AntdStaticApiInterface
|
|
36
39
|
{
|
|
37
|
-
|
|
40
|
+
protected antds: {
|
|
38
41
|
message?: MessageApi;
|
|
39
42
|
modal?: ModalApi;
|
|
40
43
|
notification?: NotificationApi;
|
|
@@ -55,32 +58,32 @@ export class DialogHandler
|
|
|
55
58
|
/**
|
|
56
59
|
* Formats error message from various error types
|
|
57
60
|
*/
|
|
58
|
-
|
|
61
|
+
protected formatErrorMessage(error: unknown): string {
|
|
59
62
|
if (error instanceof Error) return error.message;
|
|
60
63
|
if (typeof error === 'string') return error;
|
|
61
64
|
return 'An unknown error occurred';
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
public success(msg: string, options?:
|
|
67
|
+
public success(msg: string, options?: NotificationOptions): void {
|
|
65
68
|
this.antds.message?.success({ content: msg, ...options });
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
public error(msg: string, options?:
|
|
71
|
+
public error(msg: string, options?: NotificationOptions): void {
|
|
69
72
|
this.antds.message?.error({
|
|
70
73
|
content: options?.error ? this.formatErrorMessage(options.error) : msg,
|
|
71
74
|
...options
|
|
72
75
|
});
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
public info(msg: string, options?:
|
|
78
|
+
public info(msg: string, options?: NotificationOptions): void {
|
|
76
79
|
this.antds.message?.info({ content: msg, ...options });
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
public warn(msg: string, options?:
|
|
82
|
+
public warn(msg: string, options?: NotificationOptions): void {
|
|
80
83
|
this.antds.message?.warning({ content: msg, ...options });
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
public confirm(options:
|
|
86
|
+
public confirm(options: DialogHandlerOptions): void {
|
|
84
87
|
this.antds.modal?.confirm(options);
|
|
85
88
|
}
|
|
86
89
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExecutorContext, ExecutorPlugin } from '@qlover/fe-corekit';
|
|
2
2
|
import type { LoggerInterface } from '@qlover/logger';
|
|
3
3
|
import { inject, injectable } from 'inversify';
|
|
4
|
-
import { I18nService } from '../services/I18nService';
|
|
5
4
|
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
5
|
+
import type { I18nServiceInterface } from '../port/I18nServiceInterface';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* When throw error, it will be converted to i18n key
|
|
@@ -14,8 +14,9 @@ export class I18nKeyErrorPlugin implements ExecutorPlugin {
|
|
|
14
14
|
readonly pluginName = 'I18nKeyErrorPlugin';
|
|
15
15
|
|
|
16
16
|
constructor(
|
|
17
|
-
@inject(IOCIdentifier.Logger)
|
|
18
|
-
@inject(
|
|
17
|
+
@inject(IOCIdentifier.Logger) protected logger: LoggerInterface,
|
|
18
|
+
@inject(IOCIdentifier.I18nServiceInterface)
|
|
19
|
+
protected i18nService: I18nServiceInterface
|
|
19
20
|
) {}
|
|
20
21
|
|
|
21
22
|
onError(context: ExecutorContext<unknown>): Error | void {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IOCIdentifierMap } from '
|
|
1
|
+
import { IOCIdentifierMap } from '@config/IOCIdentifier';
|
|
2
2
|
import {
|
|
3
3
|
IOCContainerInterface,
|
|
4
4
|
ServiceIdentifier
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import { Container } from 'inversify';
|
|
7
7
|
|
|
8
8
|
export class InversifyContainer implements IOCContainerInterface {
|
|
9
|
-
|
|
9
|
+
protected container: Container;
|
|
10
10
|
|
|
11
11
|
constructor() {
|
|
12
12
|
this.container = new Container({
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { I18nServiceInterface } from '@/base/port/I18nServiceInterface';
|
|
2
|
+
import {
|
|
3
|
+
ExecutorContext,
|
|
4
|
+
ExecutorPlugin,
|
|
5
|
+
RequestAdapterConfig
|
|
6
|
+
} from '@qlover/fe-corekit';
|
|
7
|
+
|
|
8
|
+
export class RequestLanguages implements ExecutorPlugin {
|
|
9
|
+
readonly pluginName = 'RequestLanguages';
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
protected i18nService: I18nServiceInterface,
|
|
13
|
+
protected headerName = 'accept-language'
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
buildAcceptLanguage(langs: string[]): string {
|
|
17
|
+
return langs
|
|
18
|
+
.map((lang, index) => {
|
|
19
|
+
const q = Math.max(1 - index * 0.1, 0.1).toFixed(1);
|
|
20
|
+
return index === 0 ? lang : `${lang};q=${q}`;
|
|
21
|
+
})
|
|
22
|
+
.join(',');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onBefore(context: ExecutorContext<RequestAdapterConfig>): void {
|
|
26
|
+
const currentLanguage = this.i18nService.getCurrentLanguage();
|
|
27
|
+
const languages = this.i18nService.getSupportedLanguages();
|
|
28
|
+
|
|
29
|
+
if (!context.parameters.headers) {
|
|
30
|
+
context.parameters.headers = {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const languageValue = this.buildAcceptLanguage(
|
|
34
|
+
Array.from(new Set([currentLanguage, ...languages]))
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
context.parameters.headers[this.headerName] = languageValue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AsyncStateInterface } from '@/base/port/AsyncStateInterface';
|
|
2
|
+
|
|
3
|
+
export class RequestState<T> implements AsyncStateInterface<T> {
|
|
4
|
+
startTime: number;
|
|
5
|
+
endTime: number;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
public loading: boolean = false,
|
|
9
|
+
public result: T | null = null,
|
|
10
|
+
public error: unknown | null = null
|
|
11
|
+
) {
|
|
12
|
+
this.startTime = Date.now();
|
|
13
|
+
this.endTime = 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
end(): this {
|
|
17
|
+
this.endTime = Date.now();
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -9,9 +9,9 @@ export class RequestStatusCatcher
|
|
|
9
9
|
implements RequestCatcherInterface<RequestAdapterResponse>
|
|
10
10
|
{
|
|
11
11
|
constructor(
|
|
12
|
-
@inject(IOCIdentifier.Logger)
|
|
13
|
-
private readonly logger: LoggerInterface
|
|
12
|
+
@inject(IOCIdentifier.Logger) protected logger: LoggerInterface
|
|
14
13
|
) {}
|
|
14
|
+
|
|
15
15
|
/**
|
|
16
16
|
* default handler
|
|
17
17
|
* @override
|