func/config 会为 func 应用自动生成配置文件,并帮助用户屏蔽底层的权限、读写、原子化操作,可用于存储用户信息、默认应用配置等。下面以 ship login 保存用户名和 email 为例。
定义并注册配置
继承 Config(defaults) 创建配置类,再通过 ConfigModule.register() 注册:
import { Module, createApp } from 'func'
import { Config, ConfigFile, ConfigModule } from 'func/config'
@ConfigFile({ maxBytes: 64 * 1024 })
export class ProfileConfig extends Config({
username: '',
email: '',
}) {}
@Module({
imports: [
ConfigModule.register({
configs: [ProfileConfig],
}),
],
})
export class AppModule {}
const app = createApp(AppModule, {
appName: 'ship',
})
void app.bootstrap()上述代码会自动为用户创建 ~/.ship/config.json 并提供基础 API 支持。在默认设置中,文件名为 config.json,你可以指定覆盖,如有必要,你也可以创建多个配置文件。
func 使用 Node.js 识别到的用户主目录,不依赖当前工作目录。以上例为准,各系统通常会把配置文件保存到:
| 系统 | 默认位置 |
|---|---|
| macOS | /Users/<用户名>/.ship/config.json |
| Linux | /home/<用户名>/.ship/config.json |
| Windows | C:\Users\<用户名>\.ship\config.json |
主目录可能受系统账户和运行环境影响;例如 Linux 的 root 用户通常对应 /root/.ship/profile.json。
应用名由 createApp({ appName }) 统一提供,func 只负责收集并透传。Config、Log 等使用应用目录的能力会在各自的 Provider 初始化时验证 appName。应用名和文件名都必须是安全的单个路径片段,不能包含斜杠、反斜杠、空白边界或 .、..。
读取和更新配置
注册后的配置类可以像普通 Provider 一样注入 Command、Module 或其他 Provider:
import { Command, Handler, Value } from 'func'
import { ProfileConfig } from '../profile.config'
@Command('login')
export class LoginCommand {
@Value()
username?: string
@Value()
email?: string
constructor(private readonly config: ProfileConfig) {}
@Handler()
login() {
const username = this.username ?? this.config.get('username')
const email = this.email ?? this.config.get('email')
this.config.set('username', username)
this.config.set('email', email)
console.log(this.config.getAll())
}
}执行 ship login --username alice --email alice@example.com 会更新两个字段;省略其中一个选项时,会从现有配置中读取该字段。这个示例只保存非敏感的用户资料,密码和访问令牌不应直接写入配置文件。
defaults 会把字符串、数字、布尔值、null、数组和嵌套对象映射为对应类型。公开操作包括:
| 方法 | 行为 |
|---|---|
get(key) | 读取一个顶层配置值。 |
getAll() | 读取完整配置。 |
set(key, value) | 更新一个顶层配置值并写入文件。 |
reset(key) | 把一个顶层配置值恢复为默认值。 |
resetAll() | 把完整配置恢复为 defaults。 |
读取结果是副本,修改返回对象不会改变运行时配置。需要保存变更时必须调用 set、reset 或 resetAll。
文件创建与校验
配置文件在第一次 get、getAll、set、reset 或 resetAll 时才创建。写入会先生成临时文件,再替换原文件,避免留下部分写入的 JSON。
读取时只接受 defaults 中已经声明的 key,并检查每个已知值的 JSON 形状:
- 缺失值使用 defaults 补齐;
- 未声明的存储字段会被忽略;
- 类型不匹配、无效 JSON 或非对象根值会产生运行时异常;
- 失败读取不会自动覆盖原文件。
maxBytes 同时限制读取文件和准备写入的 JSON 大小。一个 Module 可以注册多个配置类,但相同文件路径不能重复注册。
读取写入失败
读取失败是会发生的,这常见于用户手动编辑了配置文件但格式错误,开发者可以捕获此类错误,默认用户可以接收到最终抛出的错误,作为简单的解决方案,我们可以提供 ship config reset 作为重置修复,如果需要进一步的处理细节,则可以在应用中手动使用 try...catch 或是监听全局错误,进行更细致的提示。
写入失败发生于权限不足,这会阻止正常的命令行应用工作流程。除非用户手动编辑了配置文件的权限,否则一般不会发生,不建议为其添加额外的容错逻辑。
API 速查
| API | 用途 |
|---|---|
Config(defaults) | 创建从 defaults 推断类型的配置基类。 |
@ConfigFile({ name, maxBytes? }) | 设置文件名与可选大小限制。 |
ConfigModule.register({ configs }) | 注册并导出一个或多个配置 Provider。 |
ConfigInstance | 描述 get、getAll、set、reset 和 resetAll。 |
CONFIG_SYSTEM / CONFIG_RUNTIME | 配置能力的稳定错误码枚举。 |