@lark-apaas/coding-templates 0.1.23 → 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/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
|
@@ -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 });
|