@_tc/template-core 0.0.1-bate.59 → 0.0.1-bate.60
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/.skills/tc-component-usage-skills/reference/template-core-frontend.md +3 -1
- package/.skills/tc-generator/reference/runtime-extensions.md +10 -0
- package/AGENT_README.md +28 -4
- package/README.md +30 -4
- package/cjs/bundler/utils.js +1 -1
- package/esm/bundler/utils.js +1 -0
- package/fe/frontend/apps/dash/dash.entry.js +6 -1
- package/fe/frontend/apps/dash/types.d.ts +2 -0
- package/fe/frontend/src/index.d.ts +2 -1
- package/fe/frontend/src/index.js +2 -2
- package/fe/frontend/src/main.d.ts +6 -0
- package/fe/frontend/src/main.js +30 -13
- package/package.json +6 -1
- package/types/packages/core/loader/middleware.d.ts +1 -1
|
@@ -6,10 +6,12 @@ Use these when building business UI on top of TemplateCore, especially Dashboard
|
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
import { AsyncSelect, LanguageSwitch, ThemeSwitch } from '@_tc/template-core/fe'
|
|
9
|
+
import { api, get, post, request } from '@_tc/template-core/fe/common/request'
|
|
10
|
+
import type { BaseResponse, RequestConfig, ResponseConfig } from '@_tc/template-core/fe/common/request'
|
|
9
11
|
import { Button, DataTable, TableSearch } from '@_tc/template-core/fe/rc'
|
|
10
12
|
```
|
|
11
13
|
|
|
12
|
-
Inside this repository, use `@tc/ui-react` for the raw UI workspace package. In published business projects, prefer `@_tc/template-core/fe/rc` for UI components
|
|
14
|
+
Inside this repository, use `@tc/ui-react` for the raw UI workspace package. In published business projects, prefer `@_tc/template-core/fe/rc` for UI components, `@_tc/template-core/fe` for frontend runtime helpers, and `@_tc/template-core/fe/common/request` when only the request layer is needed.
|
|
13
15
|
|
|
14
16
|
## LanguageSwitch
|
|
15
17
|
|
|
@@ -239,6 +239,16 @@ Import TemplateCore frontend helpers from `@_tc/template-core/fe`:
|
|
|
239
239
|
import { AsyncSelect, LanguageSwitch, ThemeSwitch } from '@_tc/template-core/fe'
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
+
Request helpers and request types are available from the root frontend entry or the explicit request subpath:
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import { api, get, post, request } from '@_tc/template-core/fe'
|
|
246
|
+
import type { BaseResponse, RequestConfig, ResponseConfig } from '@_tc/template-core/fe'
|
|
247
|
+
|
|
248
|
+
import { api as requestApi } from '@_tc/template-core/fe/common/request'
|
|
249
|
+
import type { BaseResponse as ApiResponse } from '@_tc/template-core/fe/common/request'
|
|
250
|
+
```
|
|
251
|
+
|
|
242
252
|
Import UI components from the published UI re-export:
|
|
243
253
|
|
|
244
254
|
```tsx
|
package/AGENT_README.md
CHANGED
|
@@ -19,6 +19,7 @@ TemplateCore 是一个 TypeScript + Koa + React 的后台框架包。它提供
|
|
|
19
19
|
| `@_tc/template-core` | `serverStart`、`baseFn`、Koa/Controller/Service 类型。 |
|
|
20
20
|
| `@_tc/template-core/bundler` | `buildFE()`、`buildBE()`。 |
|
|
21
21
|
| `@_tc/template-core/fe` | 前端初始化、请求方法、Dashboard/Schema 类型、共享状态、业务前端组件。 |
|
|
22
|
+
| `@_tc/template-core/fe/common/request` | 请求实例、请求方法和请求类型的显式子路径。 |
|
|
22
23
|
| `@_tc/template-core/fe/rc` | React UI 组件和 SchemaForm 组件。 |
|
|
23
24
|
| `@_tc/template-core/fe/tailwind_ui.css` | 内置前端/UI 全局样式。 |
|
|
24
25
|
| `@_tc/template-core/model` | `ModelDataType` 等 model 配置类型。 |
|
|
@@ -365,6 +366,7 @@ import '@_tc/template-core/fe/tailwind_ui.css'
|
|
|
365
366
|
|
|
366
367
|
```ts
|
|
367
368
|
import {
|
|
369
|
+
api,
|
|
368
370
|
get,
|
|
369
371
|
post,
|
|
370
372
|
request,
|
|
@@ -374,6 +376,19 @@ import {
|
|
|
374
376
|
LanguageSwitch,
|
|
375
377
|
ThemeSwitch,
|
|
376
378
|
} from '@_tc/template-core/fe'
|
|
379
|
+
|
|
380
|
+
import type {
|
|
381
|
+
BaseResponse,
|
|
382
|
+
RequestConfig,
|
|
383
|
+
ResponseConfig,
|
|
384
|
+
} from '@_tc/template-core/fe'
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
只需要请求层时,可以走显式子路径:
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
import { api, get, post, request } from '@_tc/template-core/fe/common/request'
|
|
391
|
+
import type { BaseResponse, RequestConfig, ResponseConfig } from '@_tc/template-core/fe/common/request'
|
|
377
392
|
```
|
|
378
393
|
|
|
379
394
|
UI 组件从 `@_tc/template-core/fe/rc` 导入:
|
|
@@ -384,7 +399,8 @@ import { Button, DataTable, Form, Input, Modal, Select } from '@_tc/template-cor
|
|
|
384
399
|
|
|
385
400
|
常用前端能力:
|
|
386
401
|
|
|
387
|
-
- 请求方法:`request`、`get`、`post`、`put`、`patch`、`del`。
|
|
402
|
+
- 请求方法:`api`、`request`、`get`、`post`、`put`、`patch`、`del`。
|
|
403
|
+
- 请求类型:`BaseResponse`、`PageParams`、`PageResponse`、`RequestConfig`、`ResponseConfig`、`AxiosError`。
|
|
388
404
|
- 请求 header:自动添加 `s_t`、`s_sign`、`projk`;存在短 token 时添加 `Authorization: Bearer ${token}`。
|
|
389
405
|
- 本地 key:短 token 是 `localStorage.auth_token`,项目 key 是 `localStorage.p_J_k`;刷新 token header 默认名是 `RT`。
|
|
390
406
|
- `getAuthToken()` 是同步函数,读取前无需 `await`。
|
|
@@ -413,6 +429,12 @@ const AdminApp = ({ children }: { children: ReactNode }) => (
|
|
|
413
429
|
|
|
414
430
|
initApp(AdminApp, {
|
|
415
431
|
initModeData: false,
|
|
432
|
+
beforeRender: async () => {
|
|
433
|
+
// 注册请求拦截器等渲染前初始化逻辑
|
|
434
|
+
},
|
|
435
|
+
afterRender: () => {
|
|
436
|
+
// 首次渲染提交后的逻辑
|
|
437
|
+
},
|
|
416
438
|
})
|
|
417
439
|
```
|
|
418
440
|
|
|
@@ -586,9 +608,11 @@ chokidar
|
|
|
586
608
|
|
|
587
609
|
1. SchemaForm 字段类型、CallCom 组件、Koa app 类型都支持 TypeScript 声明合并。
|
|
588
610
|
2. 运行时组件通常放在业务 `frontend/extended/`。
|
|
589
|
-
3. Dashboard 扩展点包括 `frontend/extended/dash/customRoutes.tsx`、`frontend/extended/dash/components.tsx`、`frontend/extended/dash/routeGuard.ts`。
|
|
590
|
-
4. `dash/
|
|
591
|
-
5.
|
|
611
|
+
3. Dashboard 扩展点包括 `frontend/extended/dash/init.ts`、`frontend/extended/dash/customRoutes.tsx`、`frontend/extended/dash/components.tsx`、`frontend/extended/dash/routeGuard.ts`。
|
|
612
|
+
4. `dash/init` 在 React 渲染前执行,适合注册 axios 拦截器等全局初始化逻辑;类型是 `DashInit`。
|
|
613
|
+
5. `dash/routeGuard` 用来判断登录。返回登录页 URL 字符串时,Dashboard 用 `window.location.href` 跳转;`DashRouteGuardResult` 是 `string | undefined`,`undefined`(包括已登录分支的隐式返回)会打断 Dashboard 内置重定向,业务方可自行跳转。
|
|
614
|
+
6. 自定义入口的 `initApp` 支持 `beforeRender` / `afterRender`,分别在 React 渲染前和首次渲染提交后触发。
|
|
615
|
+
7. 优先查看公开入口 `.d.ts` 和 npm 包根目录 `README.md`;如果当前环境是源码仓库,再继续读 `docs/type-extension.md` 和 `docs/schema-form.md`。
|
|
592
616
|
|
|
593
617
|
## 8. 继续深入
|
|
594
618
|
|
package/README.md
CHANGED
|
@@ -189,7 +189,7 @@ http://localhost:9000/dash?projk=demo
|
|
|
189
189
|
| `Ctx` | Controller、middleware、router handler 的 Koa 请求上下文类型,包含框架补充的 `ctx.params` 和可选 `ctx.reqData`。 |
|
|
190
190
|
| `ControllerFN` | `app/controller/*.ts` 默认导出工厂函数的约束类型,适合配合 `satisfies ControllerFN` 保留 controller 实例方法类型。 |
|
|
191
191
|
| `ServiceFN` | `app/service/*.ts` 默认导出工厂函数的约束类型,适合配合 `satisfies ServiceFN` 约束 service class 工厂。 |
|
|
192
|
-
| `MiddlewareFN` | `app/
|
|
192
|
+
| `MiddlewareFN` | `app/middlewares/*.ts` 默认导出工厂函数的约束类型,返回标准 Koa middleware。 |
|
|
193
193
|
| `RouterFN` | `app/router/*.ts` 默认导出函数的约束类型,用于接收 `app` 和 `router` 并注册路由。 |
|
|
194
194
|
|
|
195
195
|
示例:
|
|
@@ -301,6 +301,7 @@ VS Code 建议安装官方 Tailwind CSS IntelliSense 插件,并在工作区 `.
|
|
|
301
301
|
|
|
302
302
|
```ts
|
|
303
303
|
import {
|
|
304
|
+
api,
|
|
304
305
|
apiFreezerStore,
|
|
305
306
|
AsyncSelect,
|
|
306
307
|
eventsInfo,
|
|
@@ -320,6 +321,7 @@ import {
|
|
|
320
321
|
} from '@_tc/template-core/fe'
|
|
321
322
|
|
|
322
323
|
import type {
|
|
324
|
+
BaseResponse,
|
|
323
325
|
CallComComponentsMap,
|
|
324
326
|
CallComRenderer,
|
|
325
327
|
DashComponentsMap,
|
|
@@ -327,6 +329,10 @@ import type {
|
|
|
327
329
|
DashRoutesExtender,
|
|
328
330
|
FormFieldSchema,
|
|
329
331
|
ApiFreezerRequestMatcher,
|
|
332
|
+
PageParams,
|
|
333
|
+
PageResponse,
|
|
334
|
+
RequestConfig,
|
|
335
|
+
ResponseConfig,
|
|
330
336
|
SchemaFormComponentsMap,
|
|
331
337
|
SchemaFormNamespace,
|
|
332
338
|
SelectProps,
|
|
@@ -342,13 +348,16 @@ import type {
|
|
|
342
348
|
| Dash 扩展类型 | `DashRoutesExtender`、`DashRoutesContext`、`DashComponentsMap`、`DashHeaderUserAreaProps`、`DashRouteGuard` |
|
|
343
349
|
| SchemaPage 类型 | `CallComComponentsMap`、`CallComRenderer` |
|
|
344
350
|
| SchemaForm 类型 | `FormFieldSchema`、`SchemaFormComponentsMap`、`SchemaFormNamespace`、`SelectProps` |
|
|
345
|
-
| 请求方法 | `request`、`get`、`post`、`put`、`patch`、`del` |
|
|
351
|
+
| 请求方法 | `api`、`request`、`get`、`post`、`put`、`patch`、`del` |
|
|
352
|
+
| 请求类型 | `BaseResponse`、`PageParams`、`PageResponse`、`RequestConfig`、`ResponseConfig`、`AxiosError` |
|
|
346
353
|
| 请求冻结 | `apiFreezerStore`、`useApiFreezer`、`FreezeState`、`ApiFreezerRequestMatcher` |
|
|
347
354
|
| 事件工具 | `eventsInfo`、`merge` |
|
|
348
355
|
| 共享状态 | `modeStore`、`useModeStore`、`schemaStore`、`useSchemaStore`、`schemaEventBus` |
|
|
349
356
|
|
|
350
357
|
`modeStore` 管项目模型和当前项目数据,`schemaStore` 管当前 Schema 页面的 schema/cache,`schemaEventBus` 用来在 Search、Table、CallCom 之间通信。
|
|
351
358
|
|
|
359
|
+
只使用请求层时,也可以从 `@_tc/template-core/fe/common/request` 导入 `api`、请求方法和请求类型;这是发布包显式子路径,不要下钻到 `frontend/src/common/request`。
|
|
360
|
+
|
|
352
361
|
### 前端多语言
|
|
353
362
|
|
|
354
363
|
TemplateCore 前端通过 `@_tc/template-core/fe` 暴露 i18n 能力。框架内置中文、英文资源,项目可以继续追加自己的资源。
|
|
@@ -538,7 +547,8 @@ export function Toolbar() {
|
|
|
538
547
|
**使用示例**:
|
|
539
548
|
|
|
540
549
|
```ts
|
|
541
|
-
import { get, post, put, del } from '@_tc/template-core/fe'
|
|
550
|
+
import { api, get, post, put, del } from '@_tc/template-core/fe'
|
|
551
|
+
import type { BaseResponse, RequestConfig, ResponseConfig } from '@_tc/template-core/fe'
|
|
542
552
|
|
|
543
553
|
// GET 请求
|
|
544
554
|
const data = await get('/product/list', { page: 1, pageSize: 10 })
|
|
@@ -553,6 +563,22 @@ await put('/product', { id: 1, name: '新名称' })
|
|
|
553
563
|
await del('/product', { id: 1 })
|
|
554
564
|
```
|
|
555
565
|
|
|
566
|
+
如果希望把请求能力单独拆出来导入:
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
import {
|
|
570
|
+
api,
|
|
571
|
+
get,
|
|
572
|
+
post,
|
|
573
|
+
request,
|
|
574
|
+
} from '@_tc/template-core/fe/common/request'
|
|
575
|
+
import type {
|
|
576
|
+
BaseResponse,
|
|
577
|
+
RequestConfig,
|
|
578
|
+
ResponseConfig,
|
|
579
|
+
} from '@_tc/template-core/fe/common/request'
|
|
580
|
+
```
|
|
581
|
+
|
|
556
582
|
**请求冻结器**:
|
|
557
583
|
|
|
558
584
|
请求冻结器用于在某段流程中暂停普通 API 请求,典型场景是 RT 刷新 token:刷新期间冻结其它请求,刷新接口本身走白名单放行,刷新完成后再解冻并回放队列。
|
|
@@ -708,7 +734,7 @@ function MyComponent() {
|
|
|
708
734
|
```text
|
|
709
735
|
app/controller/**/*.(js|ts) -> app.controller
|
|
710
736
|
app/service/**/*.(js|ts) -> app.service
|
|
711
|
-
app/
|
|
737
|
+
app/middlewares/**/*.(js|ts) -> app.middlewares
|
|
712
738
|
app/middleware.(js|ts) -> 全局中间件编排,按框架 -> 项目顺序执行
|
|
713
739
|
app/router/**/*.(js|ts) -> Koa router
|
|
714
740
|
app/router-schema/**/*.(js|ts) -> app.routerSchema
|
package/cjs/bundler/utils.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.js`),t=require(`../packages/utils/path.js`);require(`../packages/utils/index.js`);const n=require(`../packages/common/log/index.js`),r=require(`./state.js`);let i=require(`path`);i=e.__toESM(i);let a=require(`glob`);a=e.__toESM(a);let o=require(`fs`),s=require(`vite`),c=require(`@tailwindcss/vite`);c=e.__toESM(c);let l=require(`module`),u=require(`url`);var d=typeof __filename==`string`?__filename:(0,u.fileURLToPath)({}.url),f=i.default.dirname(d),p=process.cwd(),m=(0,l.createRequire)(d),h=i.default.resolve(f,`../../`),g=i.default.resolve(f,`../`),_=i.default.resolve(h,`./fe/frontend`),v=i.default.resolve(p,`.`),y=i.default.resolve(v,`./frontend`),b=e=>i.default.dirname(m.resolve(`${e}/package.json`)),x=i.default.join(v,`.tc-entry-html`),S=`<!-- {{InsertScript}} -->`,C={head:`<!-- TC_HEAD_SLOT -->`,bodyBeforeRoot:`<!-- TC_BODY_BEFORE_ROOT_SLOT -->`,bodyAfterRoot:`<!-- TC_BODY_AFTER_ROOT_SLOT -->`},w=[`head`,`body-before-root`,`body-after-root`],T=()=>({head:``,"body-before-root":``,"body-after-root":``}),E=e=>e.split(i.default.sep).join(`/`),D=e=>{let t=E(e);return t.startsWith(`.`)||t.startsWith(`/`)?t:`./${t}`},O=e=>/<html[\s>]/i.test(e)&&e.includes(`window['_basePath']`)&&/<div\s+id=["']root["']\s*><\/div>/i.test(e),k=e=>{let t=T(),r=(0,o.readFileSync)(e,`utf-8`),i=/<!--\s*tc-slot:([\w-]+)\s*-->([\s\S]*?)<!--\s*\/tc-slot:\1\s*-->/g,a=!1,s=r;if(r.replace(i,(n,r,i)=>{if(a=!0,!w.includes(r))throw Error(`Invalid html slot "${r}" in ${e}. Allowed slots: ${w.join(`, `)}.`);return t[r]+=i.trim()?`${i}\n`:``,s=s.replace(n,``),n}),s.trim()){if(O(r))return n.logYellow(`ignored legacy generated entry html: ${e}`),T();throw Error([`Invalid custom entry html: ${e}.`,`Custom entry html can only contain tc-slot blocks, for example:`,`<!-- tc-slot:head -->`,`<meta name="description" content="..." />`,`<!-- /tc-slot:head -->`].join(`
|
|
2
2
|
`))}return a&&n.logYellow(`using custom entry html slots: ${e}`),t},A=(e,n)=>{let r=i.default.basename(e),s=[i.default.join(e,`${n}.html`),i.default.join(e,`${r}.html`)].find(e=>(0,o.existsSync)(e));if(s)return s;if(a.sync(t.resolve(e,`*.html`)).length)throw Error([`Entry html filename is not supported in ${e}.`,`Use ${n}.html or ${r}.html as a tc-slot file.`].join(`
|
|
3
|
-
`))},j=(e,t)=>e.replace(C.head,t.head).replace(C.bodyBeforeRoot,t[`body-before-root`]).replace(C.bodyAfterRoot,t[`body-after-root`]),M=(e,t,n,r)=>{let a=i.default.join(x,`${n}.html`),s=D(i.default.relative(i.default.dirname(a),t));return(0,o.writeFileSync)(a,j(e,r).replace(S,`<script type="module" src="${s}"><\/script>`)),a};function N(){n.logWhite(`frameFEPath: `+_),n.logWhite(`runFEPath: `+y);let e=[_,y].reduce((e,n)=>{let r=[`js`,`jsx`,`ts`,`tsx`].map(e=>t.resolve(n,`/**`,`/*.`+e)),i=a.sync(r).filter(e=>e.includes(`.entry.`)).filter(e=>!e.includes(`.d.ts`));return[...e,...i]},[]),r={};if(!e.length)return r;(0,o.rmSync)(x,{recursive:!0,force:!0}),(0,o.mkdirSync)(x,{recursive:!0});let s=(0,o.readFileSync)(i.default.resolve(g,`./app/view/entry.tpl`),`utf-8`);for(let t=0;t<e.length;t++){let n=e[t],a=i.default.resolve(n,`../`),o=A(a,i.default.basename(n).replace(/\.entry\.[^.]+$/,``)),c=o?i.default.basename(o,`.html`):a.split(i.default.sep).at(-1);r[c]=M(s,n,c,o?k(o):T())}return r}function P(){let e=r.buildFESharedState.options.output??`frame`;return e!==`frame`&&n.logRed(`Please pay attention to the build output.`),e===`run`?i.default.join(v,`./app/public/dist`):typeof e==`function`?e():i.default.join(g,`./app/public/dist`)}var F=async e=>{let t=(await import(`chokidar`)).watch(y,{ignored:/(^|[\/\\])\../,persistent:!0,ignoreInitial:!0}),r=!1;t.on(`change`,async t=>{if(!r){r=!0,n.logYellow(`changed: ${t}`);try{await e?.()}catch(e){n.logRed(e)}finally{r=!1}}}),n.logYellow(`watching ${y}/...`)};function I(e=`.html`){let t=[...new Set(Array.isArray(e)?e:[e])];return{name:`flatten-html`,apply:`build`,closeBundle(){let e=P();if(!(0,o.existsSync)(e))return;let r=a.sync(`**/*.html`,{cwd:e,dot:!0});for(let n of r){let r=i.default.join(e,n),a=i.default.basename(n,`.html`),s=`.entry`;a.indexOf(s)===-1&&(a+=s);for(let n of t){let t=a+n,s=i.default.join(e,t);r!==s&&(0,o.copyFileSync)(r,s)}t.some(t=>r===i.default.join(e,a+t))||(0,o.unlinkSync)(r)}let s=[...new Set(r.map(e=>i.default.dirname(e)))].sort().reverse(),c=e=>{if(!((0,o.existsSync)(e)?(0,o.readdirSync)(e):[1]).length){(0,o.rmdirSync)(e);let t=e.split(i.default.sep).slice(0,-1);t.length&&c(t.join(i.default.sep))}};for(let t of s)t&&t!==`.`&&c(i.default.join(e,t));let l=t.flatMap(t=>a.sync(`**/*`+t,{cwd:e,dot:!0}));for(let e of l)n.logYellow(`final file path -----> `+e)}}}function L(e){let t=[``,`.ts`,`.tsx`,`.js`,`.jsx`];return e.reduce((e,[n,r,a=y,s=`./defaultAlias`])=>{let c=t.map(e=>i.default.resolve(a,r+e)).find(e=>(0,o.existsSync)(e)),l=i.default.resolve(f,s);return c&&(l=c),e[n]=l,e},{})}var R=(...e)=>new RegExp(e.join(`[\\/]`)),z=e=>`_${e}_tcC`,B=async(e,t)=>{try{await(0,s.build)({configFile:!1,base:`/dist`,plugins:[(await import(`@vitejs/plugin-react`)).default(),I([`.tpl`,`.html`]),(0,c.default)()],build:{outDir:P(),emptyOutDir:!0,sourcemap:t===`dev`?`inline`:`hidden`,rolldownOptions:{input:e,output:{codeSplitting:{groups:[{name:z(`__nm`),test:R(`node_modules`),maxSize:5e5,minShareCount:2,priority:1},{name:z(`r`),test:R(`node_modules`,`react`),minShareCount:2,maxSize:5e5,priority:5},{name:z(`r-d`),test:R(`node_modules`,`react-dom`),minShareCount:2,maxSize:2e5,priority:10},{name:z(`r-r-d`),test:R(`node_modules`,`react-router-dom`),minShareCount:2,maxSize:5e5,priority:10},{name:z(`ajv`),test:R(`node_modules`,`ajv`),minShareCount:2,maxSize:2e5,priority:10},{name:z(`ui-lib`),test:R(`components`),minShareCount:2,maxSize:2e5,priority:5},{name:z(`ui-f-lib`),test:R(`components`,`.*?[Ff]orm.*?`),minShareCount:2,maxSize:5e4,priority:10}]}}}},resolve:{alias:{react:b(`react`),"react-dom":b(`react-dom`),"@tc/ui-react/hooks":i.default.resolve(f,`../../fe/packages/react/hooks/`),"@tc/ui-react":i.default.resolve(f,`../../fe/packages/react/ui/`),...L([[`@tc/scalability/SchemaForm/frameData`,`./extended/SchemaForm/data`,_],[`@tc/scalability/SchemaForm/data`,`./extended/SchemaForm/data`],[`@tc/scalability/SchemaPage/CallCom/data`,`./extended/SchemaPage/CallCom/data`],[`@tc/scalability/dash/customRoutes`,`./extended/dash/customRoutes`],[`@tc/scalability/dash/components`,`./extended/dash/components`],[`@tc/scalability/dash/routeGuard`,`./extended/dash/routeGuard`,void 0,`./defaultRouteGuard`]])}}}),n.logJoinColorized(n.colorize(`builded`,`green`),n.colorize(` -> `,`white`),n.colorize(`time: `,`cyan`),n.colorize(new Date,`pink`))}catch(e){if(n.logRed(e),t!==`dev`)throw e}};exports.VBuildFE=B,exports.entries=N,exports.outDir=P,exports.watchFiles=F;
|
|
3
|
+
`))},j=(e,t)=>e.replace(C.head,t.head).replace(C.bodyBeforeRoot,t[`body-before-root`]).replace(C.bodyAfterRoot,t[`body-after-root`]),M=(e,t,n,r)=>{let a=i.default.join(x,`${n}.html`),s=D(i.default.relative(i.default.dirname(a),t));return(0,o.writeFileSync)(a,j(e,r).replace(S,`<script type="module" src="${s}"><\/script>`)),a};function N(){n.logWhite(`frameFEPath: `+_),n.logWhite(`runFEPath: `+y);let e=[_,y].reduce((e,n)=>{let r=[`js`,`jsx`,`ts`,`tsx`].map(e=>t.resolve(n,`/**`,`/*.`+e)),i=a.sync(r).filter(e=>e.includes(`.entry.`)).filter(e=>!e.includes(`.d.ts`));return[...e,...i]},[]),r={};if(!e.length)return r;(0,o.rmSync)(x,{recursive:!0,force:!0}),(0,o.mkdirSync)(x,{recursive:!0});let s=(0,o.readFileSync)(i.default.resolve(g,`./app/view/entry.tpl`),`utf-8`);for(let t=0;t<e.length;t++){let n=e[t],a=i.default.resolve(n,`../`),o=A(a,i.default.basename(n).replace(/\.entry\.[^.]+$/,``)),c=o?i.default.basename(o,`.html`):a.split(i.default.sep).at(-1);r[c]=M(s,n,c,o?k(o):T())}return r}function P(){let e=r.buildFESharedState.options.output??`frame`;return e!==`frame`&&n.logRed(`Please pay attention to the build output.`),e===`run`?i.default.join(v,`./app/public/dist`):typeof e==`function`?e():i.default.join(g,`./app/public/dist`)}var F=async e=>{let t=(await import(`chokidar`)).watch(y,{ignored:/(^|[\/\\])\../,persistent:!0,ignoreInitial:!0}),r=!1;t.on(`change`,async t=>{if(!r){r=!0,n.logYellow(`changed: ${t}`);try{await e?.()}catch(e){n.logRed(e)}finally{r=!1}}}),n.logYellow(`watching ${y}/...`)};function I(e=`.html`){let t=[...new Set(Array.isArray(e)?e:[e])];return{name:`flatten-html`,apply:`build`,closeBundle(){let e=P();if(!(0,o.existsSync)(e))return;let r=a.sync(`**/*.html`,{cwd:e,dot:!0});for(let n of r){let r=i.default.join(e,n),a=i.default.basename(n,`.html`),s=`.entry`;a.indexOf(s)===-1&&(a+=s);for(let n of t){let t=a+n,s=i.default.join(e,t);r!==s&&(0,o.copyFileSync)(r,s)}t.some(t=>r===i.default.join(e,a+t))||(0,o.unlinkSync)(r)}let s=[...new Set(r.map(e=>i.default.dirname(e)))].sort().reverse(),c=e=>{if(!((0,o.existsSync)(e)?(0,o.readdirSync)(e):[1]).length){(0,o.rmdirSync)(e);let t=e.split(i.default.sep).slice(0,-1);t.length&&c(t.join(i.default.sep))}};for(let t of s)t&&t!==`.`&&c(i.default.join(e,t));let l=t.flatMap(t=>a.sync(`**/*`+t,{cwd:e,dot:!0}));for(let e of l)n.logYellow(`final file path -----> `+e)}}}function L(e){let t=[``,`.ts`,`.tsx`,`.js`,`.jsx`];return e.reduce((e,[n,r,a=y,s=`./defaultAlias`])=>{let c=t.map(e=>i.default.resolve(a,r+e)).find(e=>(0,o.existsSync)(e)),l=i.default.resolve(f,s);return c&&(l=c),e[n]=l,e},{})}var R=(...e)=>new RegExp(e.join(`[\\/]`)),z=e=>`_${e}_tcC`,B=async(e,t)=>{try{await(0,s.build)({configFile:!1,base:`/dist`,plugins:[(await import(`@vitejs/plugin-react`)).default(),I([`.tpl`,`.html`]),(0,c.default)()],build:{outDir:P(),emptyOutDir:!0,sourcemap:t===`dev`?`inline`:`hidden`,rolldownOptions:{input:e,output:{codeSplitting:{groups:[{name:z(`__nm`),test:R(`node_modules`),maxSize:5e5,minShareCount:2,priority:1},{name:z(`r`),test:R(`node_modules`,`react`),minShareCount:2,maxSize:5e5,priority:5},{name:z(`r-d`),test:R(`node_modules`,`react-dom`),minShareCount:2,maxSize:2e5,priority:10},{name:z(`r-r-d`),test:R(`node_modules`,`react-router-dom`),minShareCount:2,maxSize:5e5,priority:10},{name:z(`ajv`),test:R(`node_modules`,`ajv`),minShareCount:2,maxSize:2e5,priority:10},{name:z(`ui-lib`),test:R(`components`),minShareCount:2,maxSize:2e5,priority:5},{name:z(`ui-f-lib`),test:R(`components`,`.*?[Ff]orm.*?`),minShareCount:2,maxSize:5e4,priority:10}]}}}},resolve:{alias:{react:b(`react`),"react-dom":b(`react-dom`),"@tc/ui-react/hooks":i.default.resolve(f,`../../fe/packages/react/hooks/`),"@tc/ui-react":i.default.resolve(f,`../../fe/packages/react/ui/`),...L([[`@tc/scalability/SchemaForm/frameData`,`./extended/SchemaForm/data`,_],[`@tc/scalability/SchemaForm/data`,`./extended/SchemaForm/data`],[`@tc/scalability/SchemaPage/CallCom/data`,`./extended/SchemaPage/CallCom/data`],[`@tc/scalability/dash/customRoutes`,`./extended/dash/customRoutes`],[`@tc/scalability/dash/init`,`./extended/dash/init`],[`@tc/scalability/dash/components`,`./extended/dash/components`],[`@tc/scalability/dash/routeGuard`,`./extended/dash/routeGuard`,void 0,`./defaultRouteGuard`]])}}}),n.logJoinColorized(n.colorize(`builded`,`green`),n.colorize(` -> `,`white`),n.colorize(`time: `,`cyan`),n.colorize(new Date,`pink`))}catch(e){if(n.logRed(e),t!==`dev`)throw e}};exports.VBuildFE=B,exports.entries=N,exports.outDir=P,exports.watchFiles=F;
|
package/esm/bundler/utils.js
CHANGED
|
@@ -228,6 +228,7 @@ var Y = (...e) => new RegExp(e.join("[\\/]")), X = (e) => `_${e}_tcC`, Z = async
|
|
|
228
228
|
["@tc/scalability/SchemaForm/data", "./extended/SchemaForm/data"],
|
|
229
229
|
["@tc/scalability/SchemaPage/CallCom/data", "./extended/SchemaPage/CallCom/data"],
|
|
230
230
|
["@tc/scalability/dash/customRoutes", "./extended/dash/customRoutes"],
|
|
231
|
+
["@tc/scalability/dash/init", "./extended/dash/init"],
|
|
231
232
|
["@tc/scalability/dash/components", "./extended/dash/components"],
|
|
232
233
|
[
|
|
233
234
|
"@tc/scalability/dash/routeGuard",
|
|
@@ -50,5 +50,10 @@ var getDefaultPageRoutes = () => {
|
|
|
50
50
|
initApp(({ children }) => /* @__PURE__ */ jsx("div", {
|
|
51
51
|
className: "dash",
|
|
52
52
|
children
|
|
53
|
-
}), {
|
|
53
|
+
}), {
|
|
54
|
+
routes: getDefaultPageRoutes(),
|
|
55
|
+
beforeRender: async (context) => {
|
|
56
|
+
if (typeof defaultAlias_default === "function") await defaultAlias_default(context);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
54
59
|
//#endregion
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { ProjectInfo } from "../../src/api/baseInfo";
|
|
2
2
|
import type { RouteInfo } from "../../src/components/Router";
|
|
3
|
+
import type { InitAppHook } from "../../src/main";
|
|
3
4
|
import type { ComponentType, LazyExoticComponent } from "react";
|
|
4
5
|
export type DashRoutesContext = {
|
|
5
6
|
topRoutes: RouteInfo[];
|
|
6
7
|
sidebarRoutes: RouteInfo[];
|
|
7
8
|
};
|
|
8
9
|
export type DashRoutesExtender = (context: DashRoutesContext) => void;
|
|
10
|
+
export type DashInit = InitAppHook;
|
|
9
11
|
export type DashHeaderUserAreaProps = {
|
|
10
12
|
projectInfo: ProjectInfo | null;
|
|
11
13
|
};
|
|
@@ -4,7 +4,8 @@ export * from "./main";
|
|
|
4
4
|
export type { CallComComponentsMap, CallComRenderer } from "./defaultPages/SchemaPage/schemaType";
|
|
5
5
|
export * from "./components/index";
|
|
6
6
|
export type { FormFieldSchema, SchemaFormComponentsMap, SchemaFormNamespace, SelectProps } from "../../packages/react/ui/index";
|
|
7
|
-
export { del, get, patch, post, put, request } from "./common/request";
|
|
7
|
+
export { api, del, get, patch, post, put, request } from "./common/request";
|
|
8
|
+
export type { AxiosError, BaseResponse, PageParams, PageResponse, RequestConfig, ResponseConfig } from "./common/request";
|
|
8
9
|
export * from "./defaultPages/SchemaPage/data/eventInfo";
|
|
9
10
|
export { merge } from "./defaultPages/SchemaPage/utils/permissions";
|
|
10
11
|
export * from "./exportStore";
|
package/fe/frontend/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getAuthToken, localKeyMap, setAuthToken } from "./common/auth/index.js"
|
|
|
2
2
|
import { i18n, i18nStore } from "../../packages/common/i18n/index.js";
|
|
3
3
|
import "../../packages/react/ui/i18n/index.js";
|
|
4
4
|
import { FreezeState, apiFreezerStore, useApiFreezer } from "./stores/apiFreezer.js";
|
|
5
|
-
import { del, get, patch, post, put, request } from "./common/request.js";
|
|
5
|
+
import { api, del, get, patch, post, put, request } from "./common/request.js";
|
|
6
6
|
import { modeStore, useModeStore } from "./stores/mode.js";
|
|
7
7
|
import HeaderView from "./components/BasePage/HeaderView.js";
|
|
8
8
|
import { generateRouter } from "./components/Router/index.js";
|
|
@@ -17,4 +17,4 @@ import ThemeSwitch_default from "./components/ThemeSwitch/index.js";
|
|
|
17
17
|
import "./components/index.js";
|
|
18
18
|
import { eventsInfo } from "./defaultPages/SchemaPage/data/eventInfo.js";
|
|
19
19
|
import { merge } from "./defaultPages/SchemaPage/utils/permissions.js";
|
|
20
|
-
export { AsyncSelect_default as AsyncSelect, FreezeState, HeaderView, LanguageSwitch_default as LanguageSwitch, ThemeSwitch_default as ThemeSwitch, apiFreezerStore, del, eventsInfo, generateRouter, get, getAuthToken, i18n, i18nStore, initApp, localKeyMap, merge, modeStore, patch, post, put, request, schemaEventBus, schemaStore, setAuthToken, themeSwitchStorageKey, useApiFreezer, useModeStore, useSchemaStore };
|
|
20
|
+
export { AsyncSelect_default as AsyncSelect, FreezeState, HeaderView, LanguageSwitch_default as LanguageSwitch, ThemeSwitch_default as ThemeSwitch, api, apiFreezerStore, del, eventsInfo, generateRouter, get, getAuthToken, i18n, i18nStore, initApp, localKeyMap, merge, modeStore, patch, post, put, request, schemaEventBus, schemaStore, setAuthToken, themeSwitchStorageKey, useApiFreezer, useModeStore, useSchemaStore };
|
|
@@ -4,11 +4,17 @@ import { RouterType } from "./components/Router/type";
|
|
|
4
4
|
import { type ReactNode } from "react";
|
|
5
5
|
import "./main.css";
|
|
6
6
|
import "./typing/window";
|
|
7
|
+
export type InitAppHookContext = {
|
|
8
|
+
rootElement: HTMLElement;
|
|
9
|
+
};
|
|
10
|
+
export type InitAppHook = (context: InitAppHookContext) => void | Promise<void>;
|
|
7
11
|
export declare const initApp: (RootComponent: (props: {
|
|
8
12
|
children: Exclude<ReactNode, null | undefined>;
|
|
9
13
|
}) => JSX.Element, options?: {
|
|
10
14
|
routes?: RouteInfo[];
|
|
11
15
|
routerType?: RouterType;
|
|
12
16
|
modelType?: string;
|
|
17
|
+
beforeRender?: InitAppHook;
|
|
18
|
+
afterRender?: InitAppHook;
|
|
13
19
|
initModeData?: boolean;
|
|
14
20
|
}) => void;
|
package/fe/frontend/src/main.js
CHANGED
|
@@ -4,7 +4,8 @@ import { renderImportComponent } from "./common/importComponent.js";
|
|
|
4
4
|
import { generateRouter } from "./components/Router/index.js";
|
|
5
5
|
import "./exportStore.js";
|
|
6
6
|
/* empty css */
|
|
7
|
-
import {
|
|
7
|
+
import { useEffect } from "react";
|
|
8
|
+
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
8
9
|
import { createRoot } from "react-dom/client";
|
|
9
10
|
//#region frontend/src/main.tsx
|
|
10
11
|
var renderRouter = async ({ routerDom, requestModelData }) => {
|
|
@@ -40,21 +41,37 @@ var renderRouter = async ({ routerDom, requestModelData }) => {
|
|
|
40
41
|
}, fail);
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
|
-
return { default: () => /* @__PURE__ */ jsx(Fragment, { children: routerDom }) };
|
|
44
|
+
return { default: () => /* @__PURE__ */ jsx(Fragment$1, { children: routerDom }) };
|
|
45
|
+
};
|
|
46
|
+
var AppLifecycle = ({ children, hookContext, afterRender }) => {
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
afterRender?.(hookContext);
|
|
49
|
+
}, [afterRender, hookContext]);
|
|
50
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
44
51
|
};
|
|
45
52
|
var initApp = (RootComponent, options) => {
|
|
46
53
|
console.log("initApp");
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
routerDom,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const bootstrap = async () => {
|
|
55
|
+
const rootElement = document.getElementById("root");
|
|
56
|
+
const root = createRoot(rootElement);
|
|
57
|
+
const { routerType = "browser", routes: pRoutes = [], initModeData = true, modelType = "MD", beforeRender, afterRender } = options ?? {};
|
|
58
|
+
const routes = [...pRoutes];
|
|
59
|
+
const usedRouter = !!routes.length;
|
|
60
|
+
let routerDom;
|
|
61
|
+
if (usedRouter) routerDom = generateRouter(routes, routerType);
|
|
62
|
+
const hookContext = { rootElement };
|
|
63
|
+
await beforeRender?.(hookContext);
|
|
64
|
+
const Component = renderImportComponent(renderRouter({
|
|
65
|
+
routerDom,
|
|
66
|
+
requestModelData: initModeData || modelType === "MD"
|
|
67
|
+
}));
|
|
68
|
+
root.render(/* @__PURE__ */ jsx(AppLifecycle, {
|
|
69
|
+
hookContext,
|
|
70
|
+
afterRender,
|
|
71
|
+
children: /* @__PURE__ */ jsx(RootComponent, { children: Component })
|
|
72
|
+
}));
|
|
73
|
+
};
|
|
74
|
+
bootstrap();
|
|
58
75
|
};
|
|
59
76
|
//#endregion
|
|
60
77
|
export { initApp };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@_tc/template-core",
|
|
3
|
-
"version": "0.0.1-bate.
|
|
3
|
+
"version": "0.0.1-bate.60",
|
|
4
4
|
"description": "A full-stack TypeScript admin framework powered by Koa, React, and Vite - monorepo root",
|
|
5
5
|
"types": "./types/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"require": "./fe/frontend/src/index.js",
|
|
25
25
|
"types": "./fe/frontend/src/index.d.ts"
|
|
26
26
|
},
|
|
27
|
+
"./fe/common/request": {
|
|
28
|
+
"import": "./fe/frontend/src/common/request.js",
|
|
29
|
+
"require": "./fe/frontend/src/common/request.js",
|
|
30
|
+
"types": "./fe/frontend/src/common/request.d.ts"
|
|
31
|
+
},
|
|
27
32
|
"./fe/rc": {
|
|
28
33
|
"import": "./fe/packages/react/ui/index.js",
|
|
29
34
|
"require": "./fe/packages/react/ui/index.js",
|
|
@@ -5,7 +5,7 @@ import type { KoaApp } from "../types";
|
|
|
5
5
|
*
|
|
6
6
|
* @param app
|
|
7
7
|
*
|
|
8
|
-
* 加载 xx/app/
|
|
8
|
+
* 加载 xx/app/middlewares 文件夹下所有文件,可通过 'app.middlewares.${目录}.${文件}' 访问
|
|
9
9
|
*/
|
|
10
10
|
declare const _default: (app: KoaApp) => void;
|
|
11
11
|
export default _default;
|