@lark-apaas/coding-templates 0.1.22 → 0.1.24
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/README.md +45 -35
- package/package.json +1 -1
- package/template-apex/README.md +5 -1
- package/template-apex/client/index.html +1 -4
- package/template-apex/client/src/lib/utils.ts +6 -0
- package/template-apex/package.json +9 -6
- package/template-apex/scripts/build.sh +1 -2
- package/template-apex/server/index.ts +67 -18
- package/template-apex/tsconfig.server.json +4 -0
- package/template-html/package.json +2 -1
- package/template-vite-react/client/src/lib/utils.ts +3 -3
- package/template-vite-react/package.json +2 -1
- package/template-apex/server/db/index.ts +0 -8
package/README.md
CHANGED
|
@@ -4,39 +4,32 @@ OpenClaw 项目模板包,供 mclaw CLI 使用。
|
|
|
4
4
|
|
|
5
5
|
## 模板列表
|
|
6
6
|
|
|
7
|
-
| 模板 | 目录 | 说明 |
|
|
8
|
-
|
|
9
|
-
| html | `template-html/` | 纯 HTML,零依赖,原型验证 |
|
|
10
|
-
| vite-react | `template-vite-react/` | Vite + React 19 + Tailwind CSS + shadcn/ui |
|
|
7
|
+
| 模板 | 目录 | 类型 | 说明 |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| html | `template-html/` | 纯前端 | 纯 HTML,零依赖,原型验证 |
|
|
10
|
+
| vite-react | `template-vite-react/` | 纯前端 | Vite + React 19 + Tailwind CSS + shadcn/ui |
|
|
11
|
+
| apex | `template-apex/` | 全栈 | Vite + React + Express + Drizzle + PostgreSQL |
|
|
11
12
|
|
|
12
13
|
## 包结构
|
|
13
14
|
|
|
14
15
|
```
|
|
15
16
|
coding-templates/
|
|
16
|
-
├── package.json
|
|
17
|
-
├── meta.json #
|
|
17
|
+
├── package.json
|
|
18
|
+
├── meta.json # 模板元数据
|
|
18
19
|
├── template-html/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
└── template-vite-react/
|
|
23
|
-
├── _gitignore # CLI 复制时重命名为 .gitignore
|
|
24
|
-
├── package.json # mclaw.stack: "vite-react",name 为 {{projectName}}
|
|
25
|
-
├── vite.config.ts
|
|
26
|
-
├── eslint.config.js
|
|
27
|
-
├── components.json
|
|
28
|
-
├── client/
|
|
20
|
+
├── template-vite-react/
|
|
21
|
+
└── template-apex/
|
|
22
|
+
├── client/ # 前端(Vite + React)
|
|
29
23
|
│ ├── index.html
|
|
30
|
-
│ ├── public/
|
|
31
24
|
│ └── src/
|
|
32
|
-
|
|
33
|
-
│
|
|
34
|
-
│
|
|
35
|
-
│
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
├── server/ # 后端(Express + Drizzle)
|
|
26
|
+
│ ├── index.ts
|
|
27
|
+
│ ├── db/
|
|
28
|
+
│ └── routes/
|
|
29
|
+
├── shared/ # 前后端共享类型和 API 契约
|
|
30
|
+
├── scripts/build.sh # 构建脚本
|
|
31
|
+
├── tsconfig.server.json # 服务端 CJS 编译配置
|
|
32
|
+
└── vite.config.ts
|
|
40
33
|
```
|
|
41
34
|
|
|
42
35
|
## 特殊文件命名
|
|
@@ -48,28 +41,45 @@ npm publish 会忽略 `.gitignore` 和 `.env*`,因此模板中使用 `_` 前
|
|
|
48
41
|
| `_gitignore` | `.gitignore` |
|
|
49
42
|
| `_env.local` | `.env.local` |
|
|
50
43
|
|
|
51
|
-
##
|
|
44
|
+
## 模板元数据
|
|
52
45
|
|
|
53
|
-
每个模板的 `package.json` 包含 `mclaw
|
|
46
|
+
每个模板的 `package.json` 包含 `mclaw` 字段:
|
|
54
47
|
|
|
55
48
|
```json
|
|
56
49
|
{
|
|
57
50
|
"mclaw": {
|
|
58
|
-
"stack": "
|
|
59
|
-
"stackVersion": "0.1.0"
|
|
51
|
+
"stack": "apex",
|
|
52
|
+
"stackVersion": "0.1.0",
|
|
53
|
+
"features": ["client", "server"]
|
|
60
54
|
}
|
|
61
55
|
}
|
|
62
56
|
```
|
|
63
57
|
|
|
58
|
+
| 字段 | 说明 |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `stack` | 技术栈标识 |
|
|
61
|
+
| `stackVersion` | 模板版本 |
|
|
62
|
+
| `features` | 应用能力,`init` 时自动推导 archType |
|
|
63
|
+
|
|
64
64
|
## 技术栈
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
### apex(全栈)
|
|
67
|
+
|
|
68
|
+
- **前端**: Vite 8 + React 19 + TypeScript 5.9 + Tailwind CSS 4 + shadcn/ui
|
|
69
|
+
- **后端**: Express 5 + Drizzle ORM + PostgreSQL
|
|
70
|
+
- **平台集成**: @lark-apaas/express-core(用户上下文、CSRF、RLS、viewContext)
|
|
71
|
+
- **开发**: Vite middleware mode 集成到 Express,单进程 HMR
|
|
72
|
+
- **构建**: 前端 Vite build,服务端 tsc CJS 输出
|
|
73
|
+
- **部署**: server.zip(服务端 + 裁剪 node_modules + run.sh)→ FaaS
|
|
74
|
+
|
|
75
|
+
### vite-react(纯前端)
|
|
76
|
+
|
|
77
|
+
- Vite 8 + React 19 + TypeScript 5 + Tailwind CSS 4 + shadcn/ui
|
|
78
|
+
- ESLint 9 flat config + react-router-dom
|
|
79
|
+
|
|
80
|
+
### html(纯前端)
|
|
67
81
|
|
|
68
|
-
-
|
|
69
|
-
- **Tailwind CSS 4**(`@tailwindcss/vite`,CSS-first 配置)
|
|
70
|
-
- **shadcn/ui**(new-york 风格,lucide 图标,radix-ui 基础组件)
|
|
71
|
-
- **ESLint 9** flat config
|
|
72
|
-
- **react-router-dom** 客户端路由
|
|
82
|
+
- 纯 HTML + Tailwind CDN,零构建依赖
|
|
73
83
|
|
|
74
84
|
## 发版
|
|
75
85
|
|
package/package.json
CHANGED
package/template-apex/README.md
CHANGED
|
@@ -118,6 +118,10 @@ app.use("/api/posts", postsRouter);
|
|
|
118
118
|
|
|
119
119
|
### 3. client/ — API 封装和页面
|
|
120
120
|
|
|
121
|
+
> ⚠️ **客户端所有 HTTP 请求必须使用 `axiosForBackend`**
|
|
122
|
+
>
|
|
123
|
+
> `axiosForBackend` 由 `@lark-apaas/client-toolkit-lite` 提供,内置平台鉴权和请求上下文。**禁止使用** `fetch`、`axios`、`XMLHttpRequest` 或其他 HTTP 客户端直接发起请求。
|
|
124
|
+
|
|
121
125
|
`client/src/api/index.ts` 增加封装:
|
|
122
126
|
|
|
123
127
|
```typescript
|
|
@@ -307,7 +311,7 @@ export default function DashboardPage() {
|
|
|
307
311
|
| 页面拆分 | 页面文件只做骨架编排(无 state/effect/逻辑);每个 Section 自包含数据+状态+类型;兄弟 Section 间无互相 import;单文件 ≤150 行 |
|
|
308
312
|
| 命名规范 | 页面目录 PascalCase,页面入口文件与目录同名(PascalCase),组件文件名 kebab-case,组件名 PascalCase |
|
|
309
313
|
| 路由注册 | 默认 `HomePage` 已替换为业务首页;新页面已在 `app.tsx` 注册;跳转使用 `<Link>` / `useNavigate()`,无 `<a href>` |
|
|
310
|
-
| API 调用 | 统一在 `api/`
|
|
314
|
+
| API 调用 | 统一在 `api/` 封装;必须使用 `axiosForBackend` 发起请求,禁止 `fetch`/`axios`;使用 `@shared` 类型 |
|
|
311
315
|
| 输入校验 | zod schema 定义在 `shared/api.interface.ts`;server 和 client 共用 |
|
|
312
316
|
| 主题色 | 使用语义化变量类(`bg-background`、`text-primary` 等);未硬编码颜色值 |
|
|
313
317
|
| 主题修改 | 仅增量覆盖变更的变量;新增色同时注册 `:root` 和 `@theme inline` |
|
|
@@ -9,10 +9,7 @@
|
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<script>
|
|
12
|
-
window.__APP_CONFIG__ = {
|
|
13
|
-
appId: "{{{appId}}}",
|
|
14
|
-
cdnDomain: "{{{cdnDomain}}}"
|
|
15
|
-
};
|
|
12
|
+
window.__APP_CONFIG__ = {{{appConfig}}};
|
|
16
13
|
</script>
|
|
17
14
|
<div id="root"></div>
|
|
18
15
|
<script type="module" src="/src/main.tsx"></script>
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"mclaw": {
|
|
7
7
|
"stack": "apex",
|
|
8
|
-
"stackVersion": "0.1.0"
|
|
8
|
+
"stackVersion": "0.1.0",
|
|
9
|
+
"features": [
|
|
10
|
+
"client",
|
|
11
|
+
"server"
|
|
12
|
+
]
|
|
9
13
|
},
|
|
10
14
|
"scripts": {
|
|
11
|
-
"dev": "
|
|
12
|
-
"dev:server": "tsx --watch server/index.ts",
|
|
13
|
-
"dev:client": "vite --host",
|
|
15
|
+
"dev": "tsx --watch --watch-path server --watch-path shared server/index.ts",
|
|
14
16
|
"build": "bash scripts/build.sh",
|
|
15
17
|
"lint": "npm run lint:client && npm run lint:server",
|
|
16
18
|
"lint:client": "eslint client/src",
|
|
@@ -45,20 +47,20 @@
|
|
|
45
47
|
"@radix-ui/react-toggle-group": "^1.1.10",
|
|
46
48
|
"@radix-ui/react-tooltip": "^1.1.18",
|
|
47
49
|
"@lark-apaas/client-toolkit-lite": "^0.0.2",
|
|
50
|
+
"@lark-apaas/express-core": "^0.0.3",
|
|
48
51
|
"@formkit/auto-animate": "^0.9.0",
|
|
49
52
|
"framer-motion": "^12.38.0",
|
|
50
53
|
"class-variance-authority": "^0.7.1",
|
|
51
54
|
"clsx": "^2.1.1",
|
|
52
55
|
"cmdk": "^1.1.1",
|
|
53
56
|
"date-fns": "^4.1.0",
|
|
54
|
-
"drizzle-orm": "
|
|
57
|
+
"drizzle-orm": "0.44.6",
|
|
55
58
|
"embla-carousel-react": "^8.6.0",
|
|
56
59
|
"express": "^5.1.0",
|
|
57
60
|
"hbs": "^4.2.0",
|
|
58
61
|
"input-otp": "^1.4.2",
|
|
59
62
|
"lucide-react": "^1.7.0",
|
|
60
63
|
"next-themes": "^0.4.6",
|
|
61
|
-
"postgres": "^3.4.8",
|
|
62
64
|
"react": "^19.2.4",
|
|
63
65
|
"react-day-picker": "^9.14.0",
|
|
64
66
|
"react-dom": "^19.2.4",
|
|
@@ -76,6 +78,7 @@
|
|
|
76
78
|
"devDependencies": {
|
|
77
79
|
"@lark-apaas/coding-presets": "^0.2.0",
|
|
78
80
|
"@lark-apaas/coding-vite-preset": "^0.1.0",
|
|
81
|
+
"@vercel/nft": "^0.29.2",
|
|
79
82
|
"@types/express": "^5",
|
|
80
83
|
"@types/node": "^24",
|
|
81
84
|
"@types/react": "^19",
|
|
@@ -32,8 +32,7 @@ echo '{ "version": 1, "type": "apex", "fallback": "index.html" }' > "$OUTPUT/rou
|
|
|
32
32
|
# 服务端产物(tsc 输出保留 server/、shared/ 子目录)
|
|
33
33
|
cp -r "$ROOT/dist/server/"* "$OUTPUT/"
|
|
34
34
|
|
|
35
|
-
#
|
|
36
|
-
cp "$ROOT/package.json" "$OUTPUT/package.json"
|
|
35
|
+
# package.json 和 node_modules 由 mclaw-dev deploy 的 prune 步骤生成
|
|
37
36
|
|
|
38
37
|
# 4. assets/ → dist/output_resource/(JS/CSS/字体,上传到 CDN)
|
|
39
38
|
if [ -d "$ROOT/dist/client/assets" ]; then
|
|
@@ -1,34 +1,83 @@
|
|
|
1
|
-
import express from "express";
|
|
1
|
+
import express, { type Request, type Response } from "express";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
4
|
import hbs from "hbs";
|
|
5
|
+
import { setup, safeStringify, createViewContextMiddleware } from "@lark-apaas/express-core";
|
|
6
|
+
import * as schema from "./db/schema";
|
|
6
7
|
import { registerRoutes } from "./routes/index";
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
9
|
+
const port = Number(process.env.FORCE_SERVER_PORT) || 3000;
|
|
10
|
+
const host = process.env.FORCE_SERVER_HOST || "localhost";
|
|
11
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
10
12
|
|
|
11
13
|
const app = express();
|
|
12
14
|
|
|
13
|
-
//
|
|
14
|
-
app
|
|
15
|
+
// Platform middleware: body parsing, cookie, user context, DB + RLS, view context
|
|
16
|
+
const { close } = setup(app, {
|
|
17
|
+
database: { schema },
|
|
18
|
+
csrf: { enabled: false },
|
|
19
|
+
viewContext: { enabled: false },
|
|
20
|
+
});
|
|
15
21
|
|
|
16
22
|
// Register API routes
|
|
17
23
|
registerRoutes(app);
|
|
18
24
|
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
// HTML rendering: dev uses Vite middleware mode, prod reads build output
|
|
26
|
+
async function setupHtmlRendering() {
|
|
27
|
+
if (isDev) {
|
|
28
|
+
// @ts-ignore -- vite types require bundler moduleResolution, dev-only import
|
|
29
|
+
const { createServer } = await import("vite");
|
|
30
|
+
const vite = await createServer({
|
|
31
|
+
server: { middlewareMode: true },
|
|
32
|
+
appType: "custom",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Vite handles static assets, module serving, and HMR
|
|
36
|
+
app.use(vite.middlewares);
|
|
37
|
+
|
|
38
|
+
app.get("/{*path}", createViewContextMiddleware(), async (req: Request, res: Response) => {
|
|
39
|
+
const rawHtml = fs.readFileSync(
|
|
40
|
+
path.resolve(__dirname, "../client/index.html"),
|
|
41
|
+
"utf-8",
|
|
42
|
+
);
|
|
43
|
+
// Vite transform: inject HMR client, resolve module imports
|
|
44
|
+
const transformed = await vite.transformIndexHtml(req.originalUrl, rawHtml);
|
|
45
|
+
// HBS render: inject platform data
|
|
46
|
+
const template = hbs.compile(transformed);
|
|
47
|
+
const html = template({
|
|
48
|
+
...res.locals,
|
|
49
|
+
appConfig: safeStringify({
|
|
50
|
+
appId: res.locals.appId || process.env.MCLAW_APP_ID || "",
|
|
51
|
+
cdnDomain: process.env.MCLAW_CDN_DOMAIN || "",
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
res.type("html").send(html);
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
const templatePath = path.resolve(__dirname, "../index.html");
|
|
58
|
+
const template = hbs.compile(fs.readFileSync(templatePath, "utf-8"));
|
|
23
59
|
|
|
24
|
-
app.get("*", (_req, res) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
60
|
+
app.get("/{*path}", createViewContextMiddleware(), (_req: Request, res: Response) => {
|
|
61
|
+
const html = template({
|
|
62
|
+
...res.locals,
|
|
63
|
+
appConfig: safeStringify({
|
|
64
|
+
appId: res.locals.appId || process.env.MCLAW_APP_ID || "",
|
|
65
|
+
cdnDomain: process.env.MCLAW_CDN_DOMAIN || "",
|
|
66
|
+
}),
|
|
67
|
+
});
|
|
68
|
+
res.type("html").send(html);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setupHtmlRendering().then(() => {
|
|
74
|
+
const server = app.listen(port, host, () => {
|
|
75
|
+
console.log(`Server running at http://${host}:${port}`);
|
|
28
76
|
});
|
|
29
|
-
res.type("html").send(html);
|
|
30
|
-
});
|
|
31
77
|
|
|
32
|
-
|
|
33
|
-
|
|
78
|
+
// Graceful shutdown
|
|
79
|
+
process.on("SIGTERM", async () => {
|
|
80
|
+
server.close();
|
|
81
|
+
await close();
|
|
82
|
+
});
|
|
34
83
|
});
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"outDir": "./dist/server",
|
|
5
5
|
"rootDir": ".",
|
|
6
|
+
"module": "commonjs",
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"verbatimModuleSyntax": false,
|
|
9
|
+
"esModuleInterop": true,
|
|
6
10
|
"noEmit": false,
|
|
7
11
|
"allowImportingTsExtensions": false,
|
|
8
12
|
"noUnusedParameters": false,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from "clsx"
|
|
2
|
-
import { twMerge } from "tailwind-merge"
|
|
1
|
+
import { clsx, type ClassValue } from "clsx"
|
|
2
|
+
import { twMerge } from "tailwind-merge"
|
|
3
3
|
|
|
4
4
|
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
return twMerge(clsx(inputs))
|
|
5
|
+
return twMerge(clsx(inputs))
|
|
6
6
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { drizzle } from "drizzle-orm/postgres-js";
|
|
2
|
-
import postgres from "postgres";
|
|
3
|
-
import * as schema from "./schema";
|
|
4
|
-
|
|
5
|
-
const connectionString = process.env.DATABASE_URL!;
|
|
6
|
-
const client = postgres(connectionString);
|
|
7
|
-
|
|
8
|
-
export const db = drizzle(client, { schema });
|