create-lumfall 1.0.0 → 1.1.1
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 +24 -17
- package/cli.js +179 -24
- package/package.json +5 -7
- package/templates/basic/README.md +0 -113
- package/templates/basic/app/controller/demo.js +0 -28
- package/templates/basic/app/extend/README.md +0 -3
- package/templates/basic/app/middleware/README.md +0 -2
- package/templates/basic/app/middleware.js +0 -13
- package/templates/basic/app/pages/home/entry.home.js +0 -4
- package/templates/basic/app/pages/home/home.vue +0 -164
- package/templates/basic/app/router/demo.js +0 -17
- package/templates/basic/app/router-schema/demo.js +0 -29
- package/templates/basic/app/service/demo.js +0 -51
- package/templates/basic/app/webpack.config.js +0 -4
- package/templates/basic/build.js +0 -7
- package/templates/basic/config/config.beta.js +0 -4
- package/templates/basic/config/config.default.js +0 -32
- package/templates/basic/config/config.local.js +0 -4
- package/templates/basic/config/config.prod.js +0 -6
- package/templates/basic/package.json +0 -31
- package/templates/basic/server.js +0 -26
- package/templates/document/README.md +0 -145
- package/templates/document/app/extend/README.md +0 -6
- package/templates/document/app/middleware/README.md +0 -2
- package/templates/document/app/middleware.js +0 -2
- package/templates/document/app/pages/docs/assets/docs-logo.svg +0 -5
- package/templates/document/app/pages/docs/components/doc-layout.vue +0 -144
- package/templates/document/app/pages/docs/components/doc-navbar.vue +0 -103
- package/templates/document/app/pages/docs/components/doc-search.vue +0 -131
- package/templates/document/app/pages/docs/components/doc-sidebar.vue +0 -29
- package/templates/document/app/pages/docs/components/doc-toc.vue +0 -21
- package/templates/document/app/pages/docs/content.js +0 -28
- package/templates/document/app/pages/docs/docs-config.js +0 -154
- package/templates/document/app/pages/docs/docs.vue +0 -24
- package/templates/document/app/pages/docs/entry.docs.js +0 -26
- package/templates/document/app/pages/docs/markdown/highlight.js +0 -30
- package/templates/document/app/pages/docs/markdown/index.js +0 -129
- package/templates/document/app/pages/docs/search.js +0 -142
- package/templates/document/app/pages/docs/styles/docs.less +0 -1091
- package/templates/document/app/pages/docs/styles/vars.less +0 -87
- package/templates/document/app/pages/docs/theme.js +0 -47
- package/templates/document/app/pages/docs/utils.js +0 -58
- package/templates/document/app/pages/docs/views/doc-home.vue +0 -56
- package/templates/document/app/pages/docs/views/doc-page.vue +0 -147
- package/templates/document/app/webpack.config.js +0 -15
- package/templates/document/build.js +0 -6
- package/templates/document/config/config.beta.js +0 -2
- package/templates/document/config/config.default.js +0 -9
- package/templates/document/config/config.local.js +0 -2
- package/templates/document/config/config.prod.js +0 -2
- package/templates/document/docs/advanced/dashboard.md +0 -66
- package/templates/document/docs/advanced/health.md +0 -72
- package/templates/document/docs/advanced/monitoring.md +0 -60
- package/templates/document/docs/advanced/security.md +0 -88
- package/templates/document/docs/core/app-instance.md +0 -114
- package/templates/document/docs/core/controller-service.md +0 -113
- package/templates/document/docs/core/lifecycle.md +0 -60
- package/templates/document/docs/core/middleware.md +0 -83
- package/templates/document/docs/core/plugins.md +0 -77
- package/templates/document/docs/core/router-schema.md +0 -102
- package/templates/document/docs/dsl/api-contract.md +0 -88
- package/templates/document/docs/dsl/extend.md +0 -311
- package/templates/document/docs/dsl/menu.md +0 -101
- package/templates/document/docs/dsl/model-project.md +0 -122
- package/templates/document/docs/dsl/overview.md +0 -116
- package/templates/document/docs/dsl/reference.md +0 -175
- package/templates/document/docs/dsl/schema-actions.md +0 -135
- package/templates/document/docs/dsl/schema.md +0 -121
- package/templates/document/docs/frontend/build.md +0 -118
- package/templates/document/docs/frontend/curl.md +0 -75
- package/templates/document/docs/frontend/page.md +0 -99
- package/templates/document/docs/frontend/widgets.md +0 -151
- package/templates/document/docs/guide/config.md +0 -108
- package/templates/document/docs/guide/deployment.md +0 -115
- package/templates/document/docs/guide/getting-started.md +0 -199
- package/templates/document/docs/guide/introduction.md +0 -55
- package/templates/document/docs/guide/structure.md +0 -98
- package/templates/document/docs/reference/commands.md +0 -70
- package/templates/document/docs/reference/faq.md +0 -94
- package/templates/document/package.json +0 -36
- package/templates/document/scripts/build-static.js +0 -129
- package/templates/document/server.js +0 -13
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
# 快速开始
|
|
2
|
-
|
|
3
|
-
从零搭一个最小可运行的 lumfall 业务项目。
|
|
4
|
-
|
|
5
|
-
## 1. 安装
|
|
6
|
-
|
|
7
|
-
```sh
|
|
8
|
-
mkdir my-app && cd my-app
|
|
9
|
-
pnpm init
|
|
10
|
-
pnpm add lumfall
|
|
11
|
-
pnpm add -D nodemon concurrently
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
::: tip 框架共享依赖无需重复安装
|
|
15
|
-
业务页面可以直接 import 框架暴露的共享依赖——`vue`、`@arco-design/web-vue`、
|
|
16
|
-
`vue-router`、`pinia`、`@babel/runtime`、`axios`、`lodash`、`moment`、`md5`。
|
|
17
|
-
框架在 webpack.base 的 `resolve.alias` 里维护这份白名单(指向框架自身的
|
|
18
|
-
依赖目录),无需在业务 `package.json` 里重复安装,且运行时保证只有一份实例
|
|
19
|
-
(需要 lumfall ≥ 1.1.1,白名单见[前端构建](../frontend/build.md))。
|
|
20
|
-
|
|
21
|
-
只有框架没有的库才需要自己安装:
|
|
22
|
-
|
|
23
|
-
```sh
|
|
24
|
-
pnpm add <你的三方库>
|
|
25
|
-
```
|
|
26
|
-
:::
|
|
27
|
-
|
|
28
|
-
## 2. 服务端入口
|
|
29
|
-
|
|
30
|
-
`server.js`:
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
const { serviceStart } = require("lumfall");
|
|
34
|
-
|
|
35
|
-
const app = serviceStart({
|
|
36
|
-
name: "my-app",
|
|
37
|
-
// 未命中路由时的 302 兜底目标。传了 options 对象就必须显式写 homePath,
|
|
38
|
-
// 否则兜底会退化为 "/"
|
|
39
|
-
homePath: "/view/health",
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
module.exports = app;
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
框架自带 `health` 页面与接口,此时已经可以启动:
|
|
46
|
-
|
|
47
|
-
```sh
|
|
48
|
-
_ENV=local node server.js
|
|
49
|
-
# Server running on http://0.0.0.0:3000
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
访问 `http://localhost:3000/view/health` 看到健康页,
|
|
53
|
-
`http://localhost:3000/health/live` 返回 `{"status":"ok"}`。
|
|
54
|
-
|
|
55
|
-
::: warning 环境变量是 `_ENV`,不是 `NODE_ENV`
|
|
56
|
-
`_ENV` 的取值是 `local` / `beta` / `prod`(缺省 `local`),决定加载哪份环境配置。
|
|
57
|
-
`NODE_ENV` 对框架配置加载不起作用。
|
|
58
|
-
:::
|
|
59
|
-
|
|
60
|
-
## 3. 前端构建入口
|
|
61
|
-
|
|
62
|
-
`build.js`:
|
|
63
|
-
|
|
64
|
-
```js
|
|
65
|
-
const { frontendBuild } = require("lumfall");
|
|
66
|
-
|
|
67
|
-
// _ENV=local 启动 Webpack dev server(HMR,默认 127.0.0.1:9002)
|
|
68
|
-
// _ENV=prod 产物构建到 app/public/dist/prod/
|
|
69
|
-
frontendBuild(process.env._ENV);
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
`package.json` 的 scripts:
|
|
73
|
-
|
|
74
|
-
```json
|
|
75
|
-
{
|
|
76
|
-
"scripts": {
|
|
77
|
-
"dev": "_ENV='local' nodemon --exitcrash server.js",
|
|
78
|
-
"prod": "_ENV='prod' node server.js",
|
|
79
|
-
"build:dev": "_ENV='local' node --max_old_space_size=4096 ./build.js",
|
|
80
|
-
"build:prod": "_ENV='prod' node ./build.js",
|
|
81
|
-
"start:dev": "concurrently --kill-others-on-fail -n webpack,server -c cyan,green \"pnpm build:dev\" \"pnpm dev\"",
|
|
82
|
-
"start:prod": "pnpm build:prod && pnpm prod"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## 4. 写第一个 API
|
|
88
|
-
|
|
89
|
-
四个文件,目录约定见[目录结构与挂载点](./structure.md):
|
|
90
|
-
|
|
91
|
-
`app/service/demo.js`:
|
|
92
|
-
|
|
93
|
-
```js
|
|
94
|
-
module.exports = (app) => {
|
|
95
|
-
const BaseService = require("lumfall").Service.Base(app);
|
|
96
|
-
|
|
97
|
-
return class DemoService extends BaseService {
|
|
98
|
-
greeting(name) {
|
|
99
|
-
return `hello, ${name || "world"}`;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
`app/controller/demo.js`:
|
|
106
|
-
|
|
107
|
-
```js
|
|
108
|
-
module.exports = (app) => {
|
|
109
|
-
const BaseController = require("lumfall").Controller.Base(app);
|
|
110
|
-
|
|
111
|
-
return class DemoController extends BaseController {
|
|
112
|
-
async getGreeting(ctx) {
|
|
113
|
-
const { demo: demoService } = this.services;
|
|
114
|
-
const message = demoService.greeting(ctx.request.query.name);
|
|
115
|
-
await this.success(ctx, { message });
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
};
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
`app/router/demo.js`:
|
|
122
|
-
|
|
123
|
-
```js
|
|
124
|
-
module.exports = (app, router) => {
|
|
125
|
-
const { demo: demoController } = app.controllers;
|
|
126
|
-
router.get(
|
|
127
|
-
"/api/demo/greeting",
|
|
128
|
-
demoController.getGreeting.bind(demoController)
|
|
129
|
-
);
|
|
130
|
-
};
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
`app/router-schema/demo.js`:
|
|
134
|
-
|
|
135
|
-
```js
|
|
136
|
-
module.exports = {
|
|
137
|
-
"/api/demo/greeting": {
|
|
138
|
-
get: {
|
|
139
|
-
query: {
|
|
140
|
-
type: "object",
|
|
141
|
-
properties: {
|
|
142
|
-
name: { type: "string" },
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
重启服务后访问:
|
|
151
|
-
|
|
152
|
-
```sh
|
|
153
|
-
curl "http://localhost:3000/api/demo/greeting?name=lumfall"
|
|
154
|
-
# {"success":true,"data":{"message":"hello, lumfall"},"metadata":{}}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
参数校验不通过时返回 HTTP 200 + `code 442`(详见[路由与参数校验](../core/router-schema.md))。
|
|
158
|
-
|
|
159
|
-
## 5. 写第一个页面
|
|
160
|
-
|
|
161
|
-
推荐用脚手架生成(页面名必须是 kebab-case):
|
|
162
|
-
|
|
163
|
-
```sh
|
|
164
|
-
node ./node_modules/lumfall/scripts/generate-page.js hello
|
|
165
|
-
# 生成 app/pages/hello/entry.hello.js + app/pages/hello/hello.vue
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
也可以手动创建,入口文件 `entry.hello.js`:
|
|
169
|
-
|
|
170
|
-
```js
|
|
171
|
-
import boot from "$lumfallBoot";
|
|
172
|
-
import Hello from "./hello.vue";
|
|
173
|
-
|
|
174
|
-
boot(Hello);
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
页面组件 `hello.vue`:
|
|
178
|
-
|
|
179
|
-
```vue
|
|
180
|
-
<template>
|
|
181
|
-
<main class="hello-page">
|
|
182
|
-
<h1>Hello Lumfall</h1>
|
|
183
|
-
</main>
|
|
184
|
-
</template>
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
本地开发时同时起 webpack dev server 与服务:
|
|
188
|
-
|
|
189
|
-
```sh
|
|
190
|
-
pnpm start:dev
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
访问 `http://localhost:3000/view/hello`。改组件代码,浏览器热更新。
|
|
194
|
-
|
|
195
|
-
## 6. 接下来
|
|
196
|
-
|
|
197
|
-
- [目录结构与挂载点](./structure.md):把目录约定吃透
|
|
198
|
-
- [配置](./config.md):按环境组织配置
|
|
199
|
-
- [常见错误自查](../reference/faq.md):启动失败时先看这里
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# 简介
|
|
2
|
-
|
|
3
|
-
**Lumfall** 是一个基于 Node.js 的全栈框架:服务端是 Koa 2,前端是 Vue 3 + Webpack 5,
|
|
4
|
-
通过 **目录约定** 组织代码,按目录自动加载并挂载,开箱即用。
|
|
5
|
-
|
|
6
|
-
```sh
|
|
7
|
-
pnpm add lumfall
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
服务端只需要一个入口文件(`serviceStart()`);前端由 Webpack 按 `app/pages/` 下的页面入口
|
|
11
|
-
自动多页打包,产物交给 Koa 渲染。业务代码写在自己的项目目录里,框架以 npm 包 `lumfall`
|
|
12
|
-
的形式被依赖。
|
|
13
|
-
|
|
14
|
-
## 它解决什么问题
|
|
15
|
-
|
|
16
|
-
搭一个 Koa + Vue 的业务工程,通常要解决一串工程化问题:目录怎么组织、配置怎么分环境、
|
|
17
|
-
路由和参数校验怎么管、前端怎么多页构建、接口安全怎么兜底……Lumfall 把这些固化为约定:
|
|
18
|
-
|
|
19
|
-
- **目录自动加载**:`app/controller`、`app/service`、`app/router`、`app/middleware`、
|
|
20
|
-
`app/extend` 下的文件自动扫描、按约定挂载到 `app` 对象上,新建文件即生效
|
|
21
|
-
- **声明式 Dashboard DSL**:Model + Project 两层配置声明整个 B 端管理台;
|
|
22
|
-
一份字段 schema 驱动搜索栏 / 表格 / 表单 / 详情四个视图,写配置不改代码
|
|
23
|
-
(见 [DSL 章节](../dsl/overview.md))
|
|
24
|
-
- **四层配置合并**:框架配置与业务配置按 `_ENV` 环境分层浅合并,支持 JSON Schema 强校验
|
|
25
|
-
- **页面系统**:`app/pages/<name>/entry.<name>.js` 自动发现为页面入口,
|
|
26
|
-
访问 `/view/<name>`,支持开发热更新(HMR)
|
|
27
|
-
- **前端构建管线**:Webpack 5 多页构建、vendor/common 分包长缓存、
|
|
28
|
-
`webpack-dev-middleware` + HMR 的开发服务
|
|
29
|
-
- **统一 API 形态**:controller / service 基类 + 统一响应结构 + 基于 JSON Schema 的参数校验
|
|
30
|
-
- **安全策略**:接口签名校验、project_key 校验等内置中间件,配置即用
|
|
31
|
-
- **可观测性**:`/health/live`、`/health/ready` 健康检查探针、请求级 monitoring 钩子、
|
|
32
|
-
启动产物诊断清单(路由 / 页面 / 探针一览)
|
|
33
|
-
- **插件与生命周期**:数据库、缓存等前置能力以插件注册;启动 / 退出 hook 覆盖全流程
|
|
34
|
-
|
|
35
|
-
## 适用场景
|
|
36
|
-
|
|
37
|
-
- 中小型全栈业务系统:管理后台、内容系统、内部工具
|
|
38
|
-
- B 端多项目(多租户)控制台:配合内置 Dashboard 与声明式 DSL,
|
|
39
|
-
用配置生成整站管理页
|
|
40
|
-
- 需要快速起步、统一团队工程约定的全栈项目
|
|
41
|
-
|
|
42
|
-
不适合的场景:重 SSR / SEO 的 C 端站点(页面是客户端渲染的 SPA)。
|
|
43
|
-
|
|
44
|
-
## 快速了解
|
|
45
|
-
|
|
46
|
-
- 从零建一个项目,看 [快速开始](./getting-started.md)
|
|
47
|
-
- 想直接抄目录骨架,用同工作区的 `lumfall-basic-project/`
|
|
48
|
-
- 声明一个 B 端管理台,看 [DSL 总览](../dsl/overview.md)
|
|
49
|
-
- B 端管理台完整参考(登录、Dashboard、schema 表格表单)看 `lumfall-business/`
|
|
50
|
-
- 技术文档站模板参考 `lumfall-document/`(本站即由它构建)
|
|
51
|
-
|
|
52
|
-
## 下一步
|
|
53
|
-
|
|
54
|
-
- [快速开始](./getting-started.md):十分钟跑起第一个项目
|
|
55
|
-
- [目录结构与挂载点](./structure.md):框架的核心约定
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# 目录结构与挂载点
|
|
2
|
-
|
|
3
|
-
Lumfall 的核心约定:**业务根目录 = 进程的 `process.cwd()`**。
|
|
4
|
-
启动服务与执行构建都必须在业务根目录下运行,否则框架会加载错目录。
|
|
5
|
-
|
|
6
|
-
## 业务项目结构
|
|
7
|
-
|
|
8
|
-
```text
|
|
9
|
-
<app-root>/
|
|
10
|
-
├── server.js # 服务端入口:serviceStart()
|
|
11
|
-
├── build.js # 前端构建入口:frontendBuild(_ENV)
|
|
12
|
-
├── package.json
|
|
13
|
-
├── config/ # 配置(见「配置」)
|
|
14
|
-
│ ├── config.default.js
|
|
15
|
-
│ ├── config.local.js # 可选
|
|
16
|
-
│ ├── config.beta.js # 可选
|
|
17
|
-
│ └── config.prod.js # 可选
|
|
18
|
-
├── model/ # 可选,Dashboard 的 Model + Project 配置
|
|
19
|
-
└── app/
|
|
20
|
-
├── middleware.js # 可选,业务全局中间件注册入口
|
|
21
|
-
├── middleware/ # 可复用中间件
|
|
22
|
-
├── controller/
|
|
23
|
-
├── service/
|
|
24
|
-
├── router/
|
|
25
|
-
├── router-schema/
|
|
26
|
-
├── extend/
|
|
27
|
-
├── pages/ # Vue 页面,entry.<name>.js 是入口
|
|
28
|
-
├── webpack.config.js # 可选,扩展 Webpack 配置
|
|
29
|
-
└── public/ # 静态文件;构建产物在 app/public/dist/
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## 挂载点一览
|
|
33
|
-
|
|
34
|
-
文件名 / 目录名用 `kebab-case` 或 `snake_case`,加载后自动转 `camelCase`。
|
|
35
|
-
|
|
36
|
-
| 业务目录 | 导出约定 | 挂载结果 |
|
|
37
|
-
| --- | --- | --- |
|
|
38
|
-
| `app/middleware/**/*.js` | `(app) => (ctx, next) => {}` | `app.middlewares.<dir>.<name>` |
|
|
39
|
-
| `app/controller/**/*.js` | `(app) => class` | `app.controllers.<dir>.<name>`,启动时实例化 |
|
|
40
|
-
| `app/service/**/*.js` | `(app) => class` | `app.services.<dir>.<name>`,启动时实例化 |
|
|
41
|
-
| `app/extend/**/*.js` | `(app) => object` | 直接挂到 `app` 上,例如 `app.logger` |
|
|
42
|
-
| `app/router/**/*.js` | `(app, router) => {}` | 注册路由到 `app.router` |
|
|
43
|
-
| `app/router-schema/**/*.js` | schema 对象或 `(app) => map` | 合并进 `app.routerSchema` |
|
|
44
|
-
| `app/middleware.js` | `(app) => { app.use(...) }` | 全局中间件注册入口 |
|
|
45
|
-
| `app/webpack.config.js` | 配置对象 | 与框架 Webpack 配置 `merge.smart` 合并 |
|
|
46
|
-
|
|
47
|
-
例子:
|
|
48
|
-
|
|
49
|
-
- `app/service/user-service.js` → `app.services.userService`
|
|
50
|
-
- `app/controller/admin/user-list.js` → `app.controllers.admin.userList`
|
|
51
|
-
- `app/middleware/api/params-verify.js` → `app.middlewares.api.paramsVerify`
|
|
52
|
-
|
|
53
|
-
框架自带的同名类别文件也会被加载(controller、service、middleware、router-schema、
|
|
54
|
-
router、extend 都有内置实现);页面入口同名时,**业务页面覆盖框架页面**。
|
|
55
|
-
|
|
56
|
-
## app 对象上有什么
|
|
57
|
-
|
|
58
|
-
启动完成后,`serviceStart()` 返回的 `app`(Koa 实例)上至少有:
|
|
59
|
-
|
|
60
|
-
| 属性 | 说明 |
|
|
61
|
-
| --- | --- |
|
|
62
|
-
| `app.options` | `serviceStart()` 的入参 |
|
|
63
|
-
| `app.baseDir` | `process.cwd()` |
|
|
64
|
-
| `app.businessPath` | `<app-root>/app` 的绝对路径,**业务代码取路径用它,不要用 `__dirname`** |
|
|
65
|
-
| `app.env` | 环境:`app.env.get()` 返回 `_ENV` 值 |
|
|
66
|
-
| `app.middlewares` | 全部中间件(框架 + 业务) |
|
|
67
|
-
| `app.routerSchema` | 合并后的 API 参数校验 schema |
|
|
68
|
-
| `app.controllers` / `app.services` | 已实例化的控制器 / 服务 |
|
|
69
|
-
| `app.config` | 合并后的配置(`configLoader` 之后才存在) |
|
|
70
|
-
| `app.plugins` | 插件注册结果 |
|
|
71
|
-
| `app.router` | KoaRouter 实例 |
|
|
72
|
-
| `app.logger` | 框架日志(log4js) |
|
|
73
|
-
| `app.health` | 健康检查注册器 |
|
|
74
|
-
| `app.diagnostics` | 诊断清单:`app.diagnostics.getManifest()` |
|
|
75
|
-
| `app.server` | `app.listen()` 返回的 server |
|
|
76
|
-
| `app.stop()` | 优雅退出(Promise) |
|
|
77
|
-
|
|
78
|
-
## 加载规则与三条硬约束
|
|
79
|
-
|
|
80
|
-
启动流程按固定顺序同步执行(详见 [app 对象与启动流程](../core/app-instance.md)),
|
|
81
|
-
由顺序推出三条约束:
|
|
82
|
-
|
|
83
|
-
1. **`app.config` 在 controller / service 工厂执行期还不存在**
|
|
84
|
-
→ 只能在请求阶段读配置(基类的 `this.config` getter 是安全的)
|
|
85
|
-
2. **controller 比 service 先加载**
|
|
86
|
-
→ 不要在 controller 工厂或构造期取 `app.services`(请求阶段用 `this.services`)
|
|
87
|
-
3. **任何 loader 遇到非法导出会直接抛错并中断启动**,不会静默跳过
|
|
88
|
-
→ 导出形状必须严格按上表约定
|
|
89
|
-
|
|
90
|
-
## 路径书写注意
|
|
91
|
-
|
|
92
|
-
- 业务路径统一走 `app.businessPath` + `path.join` / `path.resolve`,不要硬编码 `/`
|
|
93
|
-
- 例外:`glob` v7 的结果始终是 `/` 分隔,拼接前先按 `/` 拆开再 `join(path.sep)`
|
|
94
|
-
|
|
95
|
-
## 下一步
|
|
96
|
-
|
|
97
|
-
- [Controller 与 Service](../core/controller-service.md):写业务逻辑
|
|
98
|
-
- [路由与参数校验](../core/router-schema.md):暴露 API
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# 命令与环境速查
|
|
2
|
-
|
|
3
|
-
## 命令速查
|
|
4
|
-
|
|
5
|
-
在业务根目录(`<app-root>`)下执行:
|
|
6
|
-
|
|
7
|
-
| 场景 | 命令 |
|
|
8
|
-
| --- | --- |
|
|
9
|
-
| 安装依赖 | `pnpm install` |
|
|
10
|
-
| 本地开发(前端 HMR + 服务) | `pnpm start:dev`(= `build:dev` + `dev` 并行) |
|
|
11
|
-
| 只起服务 | `_ENV=local node server.js` |
|
|
12
|
-
| 生产构建 | `_ENV=prod node build.js` |
|
|
13
|
-
| 生产启动 | `_ENV=prod node server.js` |
|
|
14
|
-
| 构建 + 启动 | `pnpm start:prod` |
|
|
15
|
-
| 生成页面 | `pnpm new-page <name> [--header]` 或 `node ./node_modules/lumfall/scripts/generate-page.js <name>` |
|
|
16
|
-
| 排查挂载 | 启动后读 `app.diagnostics.getManifest()` |
|
|
17
|
-
|
|
18
|
-
`new-page` 的页面名必须是 kebab-case;`--header` 套 HeaderContainer 布局。
|
|
19
|
-
|
|
20
|
-
## 环境变量
|
|
21
|
-
|
|
22
|
-
| 变量 | 取值 | 作用 |
|
|
23
|
-
| --- | --- | --- |
|
|
24
|
-
| `_ENV` | `local`(默认)/ `beta` / `prod` | 环境标识,决定加载哪份 `config.<env>.js` 与构建模式 |
|
|
25
|
-
| `PORT` | 默认 3000 | Koa 服务端口 |
|
|
26
|
-
| `IP` | 默认 0.0.0.0 | Koa 监听地址 |
|
|
27
|
-
|
|
28
|
-
注意:**不是** `NODE_ENV`;`_ENV` 同时决定配置加载与前端构建模式。
|
|
29
|
-
|
|
30
|
-
## serviceStart 选项
|
|
31
|
-
|
|
32
|
-
| 选项 | 类型 | 作用 |
|
|
33
|
-
| --- | --- | --- |
|
|
34
|
-
| `name` | string | 应用名(页面 `<title>`) |
|
|
35
|
-
| `homePath` | string | 未命中路由的 302 兜底;传 options 时必须显式声明 |
|
|
36
|
-
| `configSchema` | object | 合并配置的 JSON Schema 校验 |
|
|
37
|
-
| `lifecycle` | object | 启动 / 停止 hook(7 个) |
|
|
38
|
-
| `plugins` | array | 插件描述符 |
|
|
39
|
-
| `monitoring` | object | 请求观测 hook |
|
|
40
|
-
|
|
41
|
-
## 端口速记
|
|
42
|
-
|
|
43
|
-
| 端口 | 归属 |
|
|
44
|
-
| --- | --- |
|
|
45
|
-
| 3000 | Koa 服务(`PORT` 可覆盖) |
|
|
46
|
-
| 9002 | Webpack dev server + HMR(仅 `_ENV=local` 构建,写死在框架 `webpack.dev.js`) |
|
|
47
|
-
|
|
48
|
-
## 错误码速记
|
|
49
|
-
|
|
50
|
-
| code | 含义 | 来源 |
|
|
51
|
-
| --- | --- | --- |
|
|
52
|
-
| 442 | 参数校验失败 | apiParamsVerify(router-schema) |
|
|
53
|
-
| 445 | 签名校验失败 | apiSignVerify |
|
|
54
|
-
| 446 | 缺 project_key | projectHandler |
|
|
55
|
-
| 5000 | 服务端异常兜底 | errorHandler |
|
|
56
|
-
| 4041 | 页面不存在 | ViewController |
|
|
57
|
-
| 5031 | 页面模板未构建 | ViewController |
|
|
58
|
-
| 50000 | 业务失败约定码 | `this.fail` 示例约定 |
|
|
59
|
-
|
|
60
|
-
## 框架内置路由
|
|
61
|
-
|
|
62
|
-
| 路由 | 说明 |
|
|
63
|
-
| --- | --- |
|
|
64
|
-
| `/view/:page`、`/view/:page/*` | 页面渲染 |
|
|
65
|
-
| `/health/live` | 存活探针 |
|
|
66
|
-
| `/health/ready` | 就绪探针 |
|
|
67
|
-
| `/api/project/model_list` | Dashboard Model 列表(免 project_key) |
|
|
68
|
-
| `/api/project/list` | Dashboard 项目列表(免 project_key) |
|
|
69
|
-
| `/api/project` | Dashboard 单项目配置 |
|
|
70
|
-
| 其余未命中 GET | 302 → `homePath` |
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# 常见错误自查
|
|
2
|
-
|
|
3
|
-
启动失败或行为不符合预期时,按这份清单排查。
|
|
4
|
-
|
|
5
|
-
## 启动期
|
|
6
|
-
|
|
7
|
-
1. **`Class extends value ... is not a constructor`**
|
|
8
|
-
`Controller.Base` / `Service.Base` 是工厂函数,要调用:
|
|
9
|
-
`require("lumfall").Controller.Base(app)`,少写 `(app)` 就会这样报错
|
|
10
|
-
|
|
11
|
-
2. **controller / service 工厂返回了对象而不是 class**
|
|
12
|
-
loader 直接抛错中断启动;检查 `return class Xxx extends Base { ... }`
|
|
13
|
-
|
|
14
|
-
3. **router-schema 启动报 `does not match a registered route`**
|
|
15
|
-
schema 的 key(path)写错、漏写路由,或 method 用了大写
|
|
16
|
-
(必须小写且确实以该方法注册过)
|
|
17
|
-
|
|
18
|
-
4. **`[lifecycle] hook "xxx" must be synchronous`**
|
|
19
|
-
启动期 hook 不能返回 Promise;异步初始化放到 `serviceStart()` 之前做
|
|
20
|
-
(`beforeStop` / `afterStop` 除外)
|
|
21
|
-
|
|
22
|
-
5. **`unknown hook "xxx"`**
|
|
23
|
-
lifecycle 只接受七个 hook 名,见[生命周期](../core/lifecycle.md)
|
|
24
|
-
|
|
25
|
-
6. **插件报 name 重复 / 缺依赖 / 循环依赖 / register 必须同步**
|
|
26
|
-
见[插件](../core/plugins.md)规则表
|
|
27
|
-
|
|
28
|
-
7. **配置校验失败 `merged configuration for "prod" is invalid`**
|
|
29
|
-
合并后的配置不满足 `configSchema`;注意浅合并是整键替换,
|
|
30
|
-
环境文件里对象型配置要写完整
|
|
31
|
-
|
|
32
|
-
8. **extend 报 `must export a factory function`**
|
|
33
|
-
`app/extend/` 下的文件要导出 `(app) => object`,返回值挂到 app 上
|
|
34
|
-
|
|
35
|
-
9. **同名页面报 `duplicate business page entry`**
|
|
36
|
-
同一来源(业务目录)里出现了两个同名 `entry.<name>.js`
|
|
37
|
-
|
|
38
|
-
## 运行期
|
|
39
|
-
|
|
40
|
-
10. **配置不生效**
|
|
41
|
-
- 用的是 `NODE_ENV` 而不是 `_ENV`?环境变量是 `_ENV` ∈ local / beta / prod
|
|
42
|
-
- 配置是浅合并:环境文件里对象型配置整键替换,可能把 default 的子键覆盖丢了
|
|
43
|
-
- 在请求阶段读了吗?工厂执行期 `this.config` 是 undefined
|
|
44
|
-
|
|
45
|
-
11. **挂载点拿不到(undefined)**
|
|
46
|
-
- 用文件名而不是 camelCase 挂载名?
|
|
47
|
-
`app.middlewares.apiParamsVerify` 而不是 `app.middlewares["api-params-verify"]`
|
|
48
|
-
- 用 `__dirname` 拼业务路径?业务路径统一 `app.businessPath`
|
|
49
|
-
|
|
50
|
-
12. **`/view/<未知页面>` 返回 404 `4041` 而不是跳首页**
|
|
51
|
-
这是约定:页面不存在是 404 `4041`;页面存在但没构建模板是 503 `5031`;
|
|
52
|
-
只有**非 /view 的未命中路由**才 302 到 `homePath`
|
|
53
|
-
|
|
54
|
-
13. **接口返回 `code 442`**
|
|
55
|
-
router-schema 校验失败,看响应 message 里的具体字段提示
|
|
56
|
-
|
|
57
|
-
14. **接口返回 `code 445`**
|
|
58
|
-
签名校验失败:检查 `secret` 是否与客户端一致、时间戳是否过期(`maxAgeMs`)、
|
|
59
|
-
时间戳是否在未来
|
|
60
|
-
|
|
61
|
-
15. **接口返回 `code 446`**
|
|
62
|
-
`/api/project/` 路径缺 `project_key` 头;URL 带 `?projectKey=xxx`
|
|
63
|
-
时 `$lumfallCurl` 会自动带上
|
|
64
|
-
|
|
65
|
-
16. **接口返回 `code 5000`**
|
|
66
|
-
服务端异常被 errorHandler 兜底,详情在日志里(工作区 `logs/` 目录)
|
|
67
|
-
|
|
68
|
-
17. **页面白屏**
|
|
69
|
-
- 看浏览器控制台与 Network:JS/CSS 404 说明产物 publicPath 对不上
|
|
70
|
-
(确认跑过对应模式的构建)
|
|
71
|
-
- dev 模式资源指向 `127.0.0.1:9002`,先起 `build:dev` 再访问 3000 端口
|
|
72
|
-
|
|
73
|
-
18. **排查「文件明明写了却没生效」**
|
|
74
|
-
读诊断清单:`app.diagnostics.getManifest()`,看 `routes` / `pages`
|
|
75
|
-
里有没有你注册的东西
|
|
76
|
-
|
|
77
|
-
## 工程与依赖
|
|
78
|
-
|
|
79
|
-
19. **业务代码 import 框架的传递依赖报模块找不到**
|
|
80
|
-
lumfall ≥ 1.1.1 起框架已把内置依赖(vue / @arco-design/web-vue / pinia /
|
|
81
|
-
vue-router / @babel/runtime 等)暴露给业务代码,直接 import 即可;
|
|
82
|
-
旧版本(≤ 1.1.0)+ pnpm 下需在业务 `package.json` 显式声明(见[快速开始](../guide/getting-started.md))。
|
|
83
|
-
框架没有的库仍然要先 `pnpm add`
|
|
84
|
-
|
|
85
|
-
20. **`frontendBuild("beta")` 什么都不做**
|
|
86
|
-
构建只支持 `_ENV=local`(dev server)与 `_ENV=prod`(产物)
|
|
87
|
-
|
|
88
|
-
21. **从错误目录启动 / 构建**
|
|
89
|
-
业务根目录 = `process.cwd()`;必须在 `<app-root>` 下执行 `node server.js`
|
|
90
|
-
与构建命令
|
|
91
|
-
|
|
92
|
-
22. **服务端启动时控制台输出大量内容**
|
|
93
|
-
lumfall ≥ 1.2.0 启动日志已降级为摘要(各 loader 条目数,不打印配置内容);
|
|
94
|
-
若仍看到全量配置 dump,说明框架版本过旧,升级即可
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "lumfall-document",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "基于 lumfall 的技术文档站模板:导航、侧栏、目录、搜索、暗色模式,对标 VitePress 的使用体验",
|
|
5
|
-
"main": "server.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "_ENV='local' nodemon --exitcrash server.js",
|
|
8
|
-
"beta": "_ENV='beta' nodemon --exitcrash server.js",
|
|
9
|
-
"prod": "_ENV='prod' node server.js",
|
|
10
|
-
"build:dev": "_ENV='local' node --max_old_space_size=4096 ./build.js",
|
|
11
|
-
"build:prod": "_ENV='prod' node ./build.js",
|
|
12
|
-
"start:dev": "concurrently --kill-others-on-fail -n webpack,server -c cyan,green \"pnpm build:dev\" \"pnpm dev\"",
|
|
13
|
-
"start:prod": "pnpm build:prod && pnpm prod",
|
|
14
|
-
"build:static": "node ./scripts/build-static.js"
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"lumfall": "^1.2.1",
|
|
18
|
-
"highlight.js": "^11.9.0",
|
|
19
|
-
"markdown-it": "^14.0.0",
|
|
20
|
-
"markdown-it-container": "^4.0.0"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"concurrently": "^10.0.5",
|
|
24
|
-
"nodemon": "^3.1.14"
|
|
25
|
-
},
|
|
26
|
-
"packageManager": "pnpm@10.30.0",
|
|
27
|
-
"keywords": [
|
|
28
|
-
"lumfall",
|
|
29
|
-
"docs",
|
|
30
|
-
"documentation",
|
|
31
|
-
"vitepress",
|
|
32
|
-
"koa",
|
|
33
|
-
"vue"
|
|
34
|
-
],
|
|
35
|
-
"license": "ISC"
|
|
36
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 静态站点构建脚本:产出可直接托管在 Vercel / Netlify / Nginx 的纯静态目录。
|
|
3
|
-
*
|
|
4
|
-
* 用法:pnpm build:static (等价于 _ENV=prod node build.js + 组装 dist-static/)
|
|
5
|
-
*
|
|
6
|
-
* 文档站是纯前端 SPA:markdown 在构建期打进 bundle,运行时不调用任何接口、
|
|
7
|
-
* 不读取服务端注入的 window.__LUMFALL__,因此可以脱离 Koa 以静态文件部署。
|
|
8
|
-
*
|
|
9
|
-
* 产物结构(dist-static/):
|
|
10
|
-
* index.html "/" 的重定向页 → /view/docs
|
|
11
|
-
* app.html 应用外壳(由 entry.docs.tpl 转换,占位符替换为静态值)
|
|
12
|
-
* vercel.json SPA rewrite(/view/docs/* → app.html)+ 长缓存头
|
|
13
|
-
* dist/prod/... 构建产物(保持 /dist/prod/ 绝对路径可解析)
|
|
14
|
-
*/
|
|
15
|
-
const { spawnSync } = require("child_process");
|
|
16
|
-
const fs = require("fs");
|
|
17
|
-
const path = require("path");
|
|
18
|
-
|
|
19
|
-
const rootDir = process.cwd();
|
|
20
|
-
const distDir = path.join(rootDir, "app", "public", "dist");
|
|
21
|
-
const outDir = path.join(rootDir, "dist-static");
|
|
22
|
-
|
|
23
|
-
// 1. 复用生产构建:子进程执行 build.js(_ENV=prod),进程退出即构建完成
|
|
24
|
-
console.log("[static] running production build (build.js) ...\n");
|
|
25
|
-
const build = spawnSync("node", ["build.js"], {
|
|
26
|
-
stdio: "inherit",
|
|
27
|
-
env: { ...process.env, _ENV: "prod" },
|
|
28
|
-
});
|
|
29
|
-
if (build.status !== 0) {
|
|
30
|
-
process.exitCode = build.status || 1;
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// 2. 组装输出目录(保留 .vercel/ 项目链接目录,否则 vercel link 需要重做。
|
|
35
|
-
// 注意必须在 rmSync 之前把文件内容读进内存——rm 后同名路径会被重建,按路径保存无效)
|
|
36
|
-
const linkDir = path.join(outDir, ".vercel");
|
|
37
|
-
const savedLinkFiles = fs.existsSync(linkDir)
|
|
38
|
-
? fs.readdirSync(linkDir).map((name) => ({
|
|
39
|
-
name,
|
|
40
|
-
content: fs.readFileSync(path.join(linkDir, name)),
|
|
41
|
-
}))
|
|
42
|
-
: null;
|
|
43
|
-
fs.rmSync(outDir, { recursive: true, force: true });
|
|
44
|
-
fs.mkdirSync(path.join(outDir, "dist"), { recursive: true });
|
|
45
|
-
if (savedLinkFiles) {
|
|
46
|
-
fs.mkdirSync(linkDir, { recursive: true });
|
|
47
|
-
for (const { name, content } of savedLinkFiles) {
|
|
48
|
-
fs.writeFileSync(path.join(linkDir, name), content);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
fs.cpSync(path.join(distDir, "prod"), path.join(outDir, "dist", "prod"), {
|
|
52
|
-
recursive: true,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// 3. entry.docs.tpl → app.html:把服务端渲染的 __LUMFALL__ 占位换成静态值。
|
|
56
|
-
// 文档站前端不读这些值,保留空对象是为了模板使用方将来加 curl 调接口时行为一致。
|
|
57
|
-
// 同时剥掉 {# ... #} nunjucks 注释——Koa 渲染时会剥离,静态导出不剥会变成
|
|
58
|
-
// 页面顶部的可见文本(浏览器会把 head 里的游离文本挪进 body 渲染)。
|
|
59
|
-
// lumfall >= 1.2.0 起页面模板按模式分目录,prod 构建产物在 dist/prod/ 下。
|
|
60
|
-
const tplPath = path.join(distDir, "prod", "entry.docs.tpl");
|
|
61
|
-
const tpl = fs
|
|
62
|
-
.readFileSync(tplPath, "utf8")
|
|
63
|
-
.replace(/\{#[\s\S]*?#\}/g, "");
|
|
64
|
-
const marker = "window.__LUMFALL__";
|
|
65
|
-
const start = tpl.indexOf(marker);
|
|
66
|
-
if (start < 0) {
|
|
67
|
-
throw new Error(`[static] ${marker} not found in entry.docs.tpl`);
|
|
68
|
-
}
|
|
69
|
-
const scriptEnd = tpl.indexOf("</script>", start);
|
|
70
|
-
if (scriptEnd < 0) {
|
|
71
|
-
throw new Error("[static] </script> not found after __LUMFALL__ block");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const staticGlobals = `window.__LUMFALL__ = {
|
|
75
|
-
name: "lumfall-document",
|
|
76
|
-
env: "static",
|
|
77
|
-
options: {},
|
|
78
|
-
projectKey: "",
|
|
79
|
-
};`;
|
|
80
|
-
const appHtml = tpl.slice(0, start) + staticGlobals + tpl.slice(scriptEnd);
|
|
81
|
-
fs.writeFileSync(path.join(outDir, "app.html"), appHtml);
|
|
82
|
-
|
|
83
|
-
// 4. "/" 重定向页:静态托管下没有 302 兜底路由,用 meta refresh + JS 跳到 /view/docs
|
|
84
|
-
fs.writeFileSync(
|
|
85
|
-
path.join(outDir, "index.html"),
|
|
86
|
-
`<!DOCTYPE html>
|
|
87
|
-
<html lang="zh-CN">
|
|
88
|
-
<head>
|
|
89
|
-
<meta charset="UTF-8" />
|
|
90
|
-
<title>Lumfall 文档</title>
|
|
91
|
-
<meta http-equiv="refresh" content="0; url=/view/docs" />
|
|
92
|
-
<script>location.replace("/view/docs");</script>
|
|
93
|
-
</head>
|
|
94
|
-
<body></body>
|
|
95
|
-
</html>
|
|
96
|
-
`
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
// 5. SPA 路由规则:/view/docs/* 深链接重写到 app.html(文件优先,静态资源不受影响)
|
|
100
|
-
fs.writeFileSync(
|
|
101
|
-
path.join(outDir, "vercel.json"),
|
|
102
|
-
JSON.stringify(
|
|
103
|
-
{
|
|
104
|
-
rewrites: [
|
|
105
|
-
{ source: "/view/docs", destination: "/app.html" },
|
|
106
|
-
{ source: "/view/docs/:path*", destination: "/app.html" },
|
|
107
|
-
],
|
|
108
|
-
headers: [
|
|
109
|
-
{
|
|
110
|
-
// 构建产物带内容 hash,可永久缓存;app.html 不缓存保证发版即生效
|
|
111
|
-
source: "/dist/prod/(.*)",
|
|
112
|
-
headers: [
|
|
113
|
-
{ key: "Cache-Control", value: "public, max-age=31536000, immutable" },
|
|
114
|
-
],
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
source: "/app.html",
|
|
118
|
-
headers: [{ key: "Cache-Control", value: "no-cache" }],
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
},
|
|
122
|
-
null,
|
|
123
|
-
2
|
|
124
|
-
)
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
console.log(`\n[static] done → ${path.relative(rootDir, outDir)}`);
|
|
128
|
-
console.log("[static] deploy: cd dist-static && vercel --prod");
|
|
129
|
-
console.log("[static] (或 Vercel 项目设置 Build Command: pnpm build:static, Output Directory: dist-static)");
|