Skip to content

项目结构与配置 ​

目录结构 ​

Cell 项目就是一个 npm 包。源码遵循少量约定,不需要在别处登记。

路径内容
src/node/后端代码:控制器、服务、中间件
src/browser/前端代码(有前端时)
src/common/前后端都要导入的代码,例如 RPC 接口
src/node/module.ts后端 DI 模块
src/browser/module.ts前端 DI 模块
cell.yml、cell.<mode>.yml应用配置
cell.config.ts(可选)工具链配置
.cell/生成的文件:generated/(DI 装配、合并后的配置)和 output/(构建产物);加入 .gitignore

DI 模块 ​

DI 模块是一个文件:导入声明了组件的文件,并导出 autoBind():

ts
// src/node/module.ts
import './todo-controller';
import './todo-service';
import { autoBind } from '@celljs/core';

export default autoBind();

autoBind() 登记此前定义的所有带装饰器的类(@Component、@Controller、@Rpc……)。没有被模块导入的文件永远不会被加载:如果 cell check 对你写的类报 CELL_DI_NO_BINDING,在这里导入它的文件。

工具链按以下顺序查找项目的模块文件,存在的都会加载:

目标文件(在 src/ 下,扩展名 .ts、.tsx、.js 或 .mjs)
backendcommon/module、node/backend-module、node/module、module、backend-module
frontendcommon/module、browser/frontend-module、browser/module、module、frontend-module

src/common/module 和 src/module 在前后端都会加载。包也可以在 package.json 的 cell.modules 字段里声明其他路径。

组件 ​

package.json 带 cell 字段的依赖都是组件:安装它,就会把它的 DI 模块和 cell.yml 加进应用。装上 @celljs/security 就启用登录,装上 @celljs/serve-static 就会返回前端文件。要去掉某个依赖带进来的组件,在 cell.config.ts 里写:

ts
export default defineConfig({ modules: ['!@celljs/serve-static'] });

npx cell info 列出 Cell 解析出的组件、它们的 DI 模块、入口和 preset;--json 以数据形式给出同样的内容。

应用配置:cell.yml ​

yaml
# yaml-language-server: $schema=./.cell/schema/cell.yml.json
server:
  port: ${env.PORT ?? 8080}      # 组件的配置项直接写在顶层
typeorm:
  ormConfig:
    - type: mysql
      password: ${env.DB_PASSWORD}
app:                              # 你自己的配置写在 app 下
  greeting: Hello
  public:                         # 只有 app.public 会进入浏览器
    apiBase: /api
  • 顶层键就是配置键,没有 cell:、frontend:、backend: 这类分区。用属性或构造器参数上的 @Value('server.port')、@Value('app.greeting') 读取。
  • 每个顶层键都有归属:组件在自己的 cell.schema.json 里声明它拥有的键(server 属于 @celljs/web,security 属于 @celljs/security……)。写在这些键下就是覆盖组件的默认值。
  • 你自己的配置放在 app 下,框架和组件都不会占用它,升级也不会撞名。
  • 写错键会报错:没有归属的顶层键、组件没有的选项、类型不对,cell build / cell dev / cell check 都会报 CELL_CONFIG_INVALID,带文件、行号和"你是不是要写 …"。
  • cell dev / cell build / cell check 会生成 .cell/schema/cell.yml.json;在 cell.yml 第一行加上面的 yaml-language-server 注释,编辑器(VS Code 的 YAML 插件等)就有补全和校验。

前端能看到什么 ​

配置默认只在服务端。 浏览器只拿到组件标记为 public 的键(如 server.path、rpc、react、security 中页面跳转相关的项)和 app.public。密钥写在哪里都不会进前端产物,除非你把它放进 app.public。

  • 前端的值在构建时求值,表达式可以引用服务端的键,只有结果进产物。
  • npx cell config get <key> --target frontend 查看前端拿到的值;查询服务端独有的键会提示 server-only。
  • 项目也可以写自己的 cell.schema.json,描述 app 下的键(类型、说明、"x-cell-public": true)。

合并顺序 ​

配置由这些文件按顺序合并:各组件的 cell.yml(按依赖顺序)、项目的 cell.yml、各个生效 mode 的 cell.<mode>.yml。后面的覆盖前面的;对象深合并,数组整体替换。

mode ​

命令mode
cell devdevelopment
cell buildproduction
createTestApp()test

用 -m <mode>(可重复)加更多:cell build -m staging 还会读取 cell.staging.yml。没有任何文件的 mode 会被 cell check 报出(CELL_MODE_UNKNOWN)。

表达式 ​

${...} 的求值上下文是配置本身加上 env(进程环境变量):

yaml
server:
  endpoint: http://localhost:${server.port}     # 引用其他键
cloud:
  region: ${env.REGION ?? 'eu-west-1'}          # 带默认值的环境变量
typeorm:
  ormConfig:
    - port: ${env.DB_PORT ?? 5432}              # schema 类型是整数:DB_PORT=6543 得到数字 6543
      synchronize: ${env.DB_SYNC ?? false}      # schema 类型是布尔:只接受 true / false
  • 默认值用 ??,与 JavaScript 相同;空的环境变量视为未设置。不需要给值加引号。
  • 按 schema 类型转换:键的类型是数字或布尔时,表达式得到的文本会转换成该类型,转换失败报错并指出键。没有 schema 的键(如 app 下的)保留表达式结果的类型。
  • 后端的值在服务端启动时求值,所以同一个产物可以在多个环境运行,密钥也不会写进产物。前端的值在构建时求值。
  • 表达式嵌在其他文字里时得到字符串;表达式的结果不会再次求值:环境变量里即使有 ${...} 也按原文使用。

环境变量 ​

任何已有的键都可以不改文件、用 CELL_ 开头的环境变量覆盖:

bash
CELL_SERVER__PORT=8080 node .cell/output/server/index.mjs   # server.port,启动时生效
CELL_SERVER__HOST=0.0.0.0                                    # 监听所有网卡(容器里)
CELL_APP__PUBLIC__API_BASE=https://api.example.com npx cell build   # 前端的 app.public.apiBase,构建时生效
  • 前缀后只有一个下划线,层级之间用 __(CELL_SERVER__PORT)。CELL_PRESET、CELL_MODE、CELL_EVENT、CELL_OUT_DIR、CELL_DEBUG 是工具链自己的变量,不当作配置;只有一段、又不是顶层键的变量(如别的工具的 CELL_REGION)直接忽略;带 __ 却找不到键的会报告,写成旧的 CELL__… / CELL_BACKEND__… 时提示正确写法。
  • __ 分隔层级,匹配时不区分大小写,并忽略 _ 和 -(ORM_CONFIG 对应 ormConfig);数组元素用下标。
  • 同一组变量对前后端都生效:服务端启动时应用;cell build 应用后再挑出 public 键进前端。
  • 只能覆盖已经存在的键;新键先在 cell.yml 里写上默认值。
  • 值按原值(或 schema)的类型转换:数字、布尔,对象和数组用 JSON。整体是一个 ${...} 的值会保留并求值:CELL_SERVER__PORT='${env.PORT}'。
  • 找不到键或类型不符的变量不生效并给出警告;cell check 和 cell info 会报出它们。

查看值从哪里来 ​

bash
npx cell config get server.port --why
# server.port (backend) = 8080
#   server-only, owned by @celljs/web
#   Port the server listens on. Override with CELL_SERVER__PORT.
#   node_modules/@celljs/web/cell.yml: 3000
#   cell.yml: 8080

框架的常用配置项:

键默认值所在包
server.port / server.host3000 / 127.0.0.1@celljs/web
server.path/@celljs/web(所有路由的前缀,前端可见)
web.body.limit100kb@celljs/web
web.cors允许所有来源(*)@celljs/web
web.session / web.cookies见 @celljs/web 的 README@celljs/web
html.title / html.rootIdCell Framework / cell-root@celljs/core(生成的 HTML 页面)
logger.levelinfo@celljs/core
rpc.pathapi@celljs/rpc
serveStatic.spatrue@celljs/serve-static

各包的 README 列出了自己的配置项;完整的键、类型和说明在 .cell/schema/cell.yml.json。设计说明见 docs/design/config.md。

工具链配置:cell.config.ts ​

可选,只用于工具链。应用配置不写在这里。

ts
import { defineConfig } from '@celljs/cli';

export default defineConfig({
    preset: 'cloudflare',                          // 默认 node
    modules: ['!@celljs/serve-static'],            // 去掉一个组件
    build: { external: ['sharp'], sourcemap: true },
    vite: { resolve: { alias: { '~': '/src' } } }, // 合并进 Vite 配置
    hooks: { 'build:before': 'prisma generate' },  // 在工具链事件上执行的 shell 命令
    fc: { region: 'cn-shanghai', memory: 512 },    // preset 的选项,写在与 preset 同名的字段
});

未知字段会报错并给出最接近的合法名字(CELL_CONFIG_INVALID、CELL_CONFIG_UNKNOWN_OPTION)。完整说明(包括添加 Vite 插件、检查项、MCP 工具或产物文件的 toolchain 模块)见 CLI 文档。

生成的文件 ​

cell dev 和 cell build 把生成的代码写到 .cell/generated/:每个目标加载的 DI 模块、合并后的配置和项目模型。需要确切知道容器里装了什么时就读这些文件;不要修改它们。