cl-nestjs-nacos
v1.1.0
Published
NestJS 集成 Nacos 服务注册和配置中心
Maintainers
Readme
基于 NestJS 框架的 Nacos 集成模块,支持服务注册发现和配置管理
特性
- 🚀 双模式支持: 完整模式(服务注册 + 配置管理)和仅配置模式
- 📦 双模块格式: 同时支持 ESM 和 CJS,兼容所有 Node.js 环境
- 📝 多格式配置: 支持 JSON、JSONC、JSON5、YAML、XML、Properties、HTML、Text 等格式
- 🔄 配置热更新: 支持配置订阅和动态更新
- 🌍 环境变量: 支持环境变量占位符和默认值
- 🎯 类型安全: 完整的 TypeScript 类型支持和类型守卫
- 🔧 智能检测: 自动配置格式检测和降级策略
- 📦 轻量级: 最小化依赖,高性能
- 🛡️ 错误处理: 结构化错误类和详细调试信息
- ⚡ 性能优化: 内置 LRU 缓存和内存管理
- 🔌 可扩展: 插件化架构,支持自定义解析器
- ✅ 全面测试: 完整的 CI/CD 流程和模块兼容性测试
安装
npm install cl-nestjs-nacos
# 或
yarn add cl-nestjs-nacos
# 或
pnpm add cl-nestjs-nacos模块格式支持
此库同时支持 ESM 和 CJS 模块格式,可以在任何 Node.js 环境中使用:
// ESM (ES Modules)
import { NacosModule, NacosService } from 'cl-nestjs-nacos';
import { NacosError } from 'cl-nestjs-nacos/errors';
// CJS (CommonJS)
const { NacosModule, NacosService } = require('cl-nestjs-nacos');
const { NacosError } = require('cl-nestjs-nacos/errors');支持的子模块导入路径:
cl-nestjs-nacos/errors- 错误处理类cl-nestjs-nacos/cache- 缓存功能cl-nestjs-nacos/base- 基础抽象类
快速开始
完整模式(服务注册 + 配置管理)
import { Module, OnModuleInit, Injectable } from '@nestjs/common';
import { NacosService, NacosModule, ConfigType } from 'cl-nestjs-nacos';
@Module({
imports: [
NacosModule.forRoot({
server: process.env.NACOS_SERVER || 'localhost:8848',
namespace: process.env.NACOS_NAMESPACE || 'public',
// 认证方式 1: AccessKey/SecretKey
accessKey: process.env.NACOS_ACCESS_KEY,
secretKey: process.env.NACOS_SECRET_KEY,
// 认证方式 2: Username/Password(二选一即可)
// username: process.env.NACOS_USERNAME,
// password: process.env.NACOS_PASSWORD,
config: {
group: process.env.NACOS_GROUP || 'DEFAULT_GROUP',
dataId: process.env.NACOS_DATA_ID || 'application.yaml',
type: ConfigType.YAML, // 可选:指定配置文件类型
subscribe: true, // 启用配置热更新
commons: [
// 公共配置文件
{
group: 'SHARED_GROUP',
dataId: 'database.properties',
type: ConfigType.PROPERTIES,
},
],
},
}),
],
})
export class AppModule implements OnModuleInit {
constructor(private readonly nacos: NacosService) {}
async onModuleInit(): Promise<void> {
// 注册服务到 Nacos
await this.nacos.register('my-service');
}
}
@Injectable()
export class AppService {
constructor(private readonly nacos: NacosService) {}
async getAppConfig() {
// 获取完整配置
const config = await this.nacos.getConfig();
return config;
}
async getDatabaseConfig() {
// 获取特定配置项
const dbConfig = await this.nacos.getConfig<{
host: string;
port: number;
username: string;
password: string;
}>('database');
return dbConfig;
}
}仅配置模式(只获取配置,不注册服务)
import { Module, Injectable } from '@nestjs/common';
import { NacosConfigService, NacosModule, ConfigType } from 'cl-nestjs-nacos';
@Module({
imports: [
NacosModule.forRoot({
server: process.env.NACOS_SERVER || 'localhost:8848',
namespace: process.env.NACOS_NAMESPACE || 'public',
// 使用 AccessKey/SecretKey 认证
accessKey: process.env.NACOS_ACCESS_KEY,
secretKey: process.env.NACOS_SECRET_KEY,
configOnly: true, // 启用仅配置模式
config: {
group: process.env.NACOS_GROUP || 'DEFAULT_GROUP',
dataId: process.env.NACOS_DATA_ID || 'application.yaml',
subscribe: true, // 支持配置热更新
commons: [
{
group: 'SHARED_GROUP',
dataId: 'redis.json',
type: ConfigType.JSON,
},
],
},
}),
],
})
export class AppModule {}
@Injectable()
export class ConfigService {
constructor(private readonly nacosConfig: NacosConfigService) {}
async getConfig<T = any>(key?: string): Promise<T> {
return await this.nacosConfig.getConfig<T>(key);
}
async getRedisConfig() {
const config = await this.nacosConfig.getConfig<{
host: string;
port: number;
db: number;
}>('redis');
return config;
}
}认证方式
cl-nestjs-nacos 支持两种认证方式连接到 Nacos 服务器:
1. AccessKey/SecretKey 认证(推荐)
这是推荐的认证方式,适用于生产环境:
NacosModule.forRoot({
server: 'nacos.example.com:8848',
namespace: 'production',
accessKey: process.env.NACOS_ACCESS_KEY,
secretKey: process.env.NACOS_SECRET_KEY,
// ... 其他配置
});2. Username/Password 认证
适用于开发和测试环境:
NacosModule.forRoot({
server: 'localhost:8848',
namespace: 'dev',
username: process.env.NACOS_USERNAME,
password: process.env.NACOS_PASSWORD,
// ... 其他配置
});环境变量支持
你可以通过环境变量来配置认证信息,无需在代码中硬编码:
AccessKey/SecretKey 环境变量
export NACOS_ACCESS_KEY=your_access_key
export NACOS_SECRET_KEY=your_secret_keyUsername/Password 环境变量
export NACOS_USERNAME=your_username
export NACOS_PASSWORD=your_password优先级规则
- 配置优先: 代码中直接设置的认证信息优先级最高
- 环境变量: 如果代码中未设置,则使用对应的环境变量
- 必须提供: 必须提供其中一种完整的认证方式(accessKey+secretKey 或 username+password)
// 示例:混合使用配置和环境变量
NacosModule.forRoot({
server: 'nacos.example.com:8848',
namespace: 'production',
// 如果设置了 accessKey,会优先使用,否则使用 NACOS_ACCESS_KEY 环境变量
accessKey: process.env.CUSTOM_ACCESS_KEY,
// secretKey 如果未设置,会使用 NACOS_SECRET_KEY 环境变量
// ... 其他配置
});配置管理
支持的配置格式
cl-nestjs-nacos 支持多种配置文件格式的解析:
| 格式 | 扩展名 | 说明 |
| -------------- | --------------- | ---------------------------------- |
| JSON | .json | 标准 JSON 格式 |
| JSONC | .jsonc | JSON with Comments |
| JSON5 | .json5 | JSON5 格式 |
| YAML | .yaml, .yml | YAML 格式(默认) |
| XML | .xml | XML 格式 |
| Properties | .properties | Java Properties 格式 |
| HTML | .html, .htm | HTML 格式(提取 data-config 属性) |
| Text | .txt | 纯文本格式 |
配置格式使用
1. 明确指定配置类型
import { ConfigType } from 'cl-nestjs-nacos';
NacosModule.forRoot({
// ... 其他配置
config: {
group: 'DEFAULT_GROUP',
dataId: 'config.json',
type: ConfigType.JSON, // 明确指定为JSON格式
commons: [
{
group: 'SHARED_GROUP',
dataId: 'database.properties',
type: ConfigType.PROPERTIES, // 明确指定为Properties格式
},
],
},
});2. 自动格式检测
系统会根据 dataId 的文件扩展名自动检测格式:
NacosModule.forRoot({
// ... 其他配置
config: {
group: 'DEFAULT_GROUP',
dataId: 'config.json', // 自动检测为JSON格式
commons: [
{
group: 'SHARED_GROUP',
dataId: 'app.yaml', // 自动检测为YAML格式
},
{
group: 'DB_GROUP',
dataId: 'database.properties', // 自动检测为Properties格式
},
],
},
});3. 智能内容检测
当无法从文件名推断格式时,系统会根据内容特征自动检测:
- 以
{开头和}结尾 → JSON 格式 - 以
<开头且包含>→ XML 格式 - 包含
key=value或key:value格式 → Properties 格式 - 其他情况 → YAML 格式(默认)
配置格式示例
JSON 格式
{
"database": {
"host": "localhost",
"port": 3306,
"username": "root",
"password": "${DB_PASSWORD}"
},
"redis": {
"host": "${REDIS_HOST:redis-server}",
"port": 6379
}
}YAML 格式
database:
host: localhost
port: 3306
username: root
password: ${DB_PASSWORD}
redis:
host: ${REDIS_HOST:redis-server}
port: 6379
app:
name: ${APP_NAME:my-app}
version: '1.0.0'Properties 格式
# Database Configuration
database.host=localhost
database.port=3306
database.username=root
database.password=${DB_PASSWORD}
# Redis Configuration
redis.host=${REDIS_HOST:redis-server}
redis.port=6379
# App Configuration
app.name=${APP_NAME:my-app}
app.version=1.0.0XML 格式
<config>
<database>
<host>localhost</host>
<port>3306</port>
<username>root</username>
<password>${DB_PASSWORD}</password>
</database>
<redis>
<host>${REDIS_HOST:redis-server}</host>
<port>6379</port>
</redis>
</config>HTML 格式
<html>
<body>
<div data-config="database.host" data-value="localhost">Database Host</div>
<div data-config="database.port">3306</div>
<div data-config="database.password">${DB_PASSWORD}</div>
<!-- 或者使用 script 标签 -->
<script type="application/json">
{
"redis": {
"host": "${REDIS_HOST:redis-server}",
"port": 6379
}
}
</script>
</body>
</html>环境变量支持
cl-nestjs-nacos 支持在配置中使用环境变量占位符,提供了灵活的配置管理方式。
占位符语法
// 基本语法
${VARIABLE_NAME}
// 带默认值的语法
${VARIABLE_NAME:default_value}使用示例
// 在任何支持的配置格式中使用
{
"database": {
"host": "${DB_HOST:localhost}", // 如果 DB_HOST 未设置,使用 localhost
"port": "${DB_PORT:3306}", // 如果 DB_PORT 未设置,使用 3306
"username": "${DB_USERNAME}", // 必须设置 DB_USERNAME
"password": "${DB_PASSWORD}" // 必须设置 DB_PASSWORD
},
"app": {
"port": "${PORT:8080}", // 应用端口,默认 8080
"env": "${NODE_ENV:development}" // 环境,默认 development
}
}启用/禁用环境变量替换
NacosModule.forRoot({
server: process.env.NACOS_SERVER,
// ... 其他配置
enableEnvVars: true, // 默认为 true,启用环境变量替换
config: {
// ... 配置选项
},
});环境变量配置示例
# .env 文件
NACOS_SERVER=nacos.example.com:8848
NACOS_NAMESPACE=production
# 认证方式 1: AccessKey/SecretKey(推荐用于生产环境)
NACOS_ACCESS_KEY=your_access_key
NACOS_SECRET_KEY=your_secret_key
# 认证方式 2: Username/Password(适用于开发和测试环境)
# NACOS_USERNAME=your_username
# NACOS_PASSWORD=your_password
# 注意:只需要配置其中一种认证方式即可
# 应用配置的环境变量
DB_HOST=prod-db.example.com
DB_PORT=5432
DB_USERNAME=app_user
DB_PASSWORD=secure_password
REDIS_HOST=redis.example.com
APP_NAME=my-production-app配置订阅与热更新
支持配置的动态订阅和热更新,无需重启应用即可获取最新配置。
启用配置订阅
NacosModule.forRoot({
// ... 其他配置
config: {
group: 'DEFAULT_GROUP',
dataId: 'application.yaml',
subscribe: true, // 启用配置订阅
commons: [
{
group: 'SHARED_GROUP',
dataId: 'database.yaml',
// commons 中的配置不会自动订阅,只有主配置会被订阅
},
],
},
});监听配置变更
@Injectable()
export class ConfigWatcherService implements OnModuleInit {
constructor(private readonly nacos: NacosService) {}
onModuleInit() {
// 监听配置准备就绪事件
this.nacos.on('ready', () => {
console.log('Nacos 配置已准备就绪');
});
// 注意:配置更新是自动的,会直接更新内部配置对象
// 如果需要在配置更新时执行特定逻辑,可以定期检查配置变化
}
}高级用法
多环境配置
// 根据环境加载不同配置
const getConfigByEnv = (env: string) => {
const baseConfig = {
server: process.env.NACOS_SERVER,
accessKey: process.env.NACOS_ACCESS_KEY,
secretKey: process.env.NACOS_SECRET_KEY,
namespace: process.env.NACOS_NAMESPACE,
};
if (env === 'production') {
return {
...baseConfig,
config: {
group: 'PROD_GROUP',
dataId: 'application-prod.yaml',
subscribe: true,
},
};
} else if (env === 'staging') {
return {
...baseConfig,
config: {
group: 'STAGING_GROUP',
dataId: 'application-staging.yaml',
subscribe: true,
},
};
} else {
return {
...baseConfig,
configOnly: true,
config: {
group: 'DEV_GROUP',
dataId: 'application-dev.yaml',
},
};
}
};
@Module({
imports: [NacosModule.forRoot(getConfigByEnv(process.env.NODE_ENV))],
})
export class AppModule {}配置验证
import { Injectable, OnModuleInit } from '@nestjs/common';
import { NacosService } from 'cl-nestjs-nacos';
interface AppConfig {
database: {
host: string;
port: number;
username: string;
password: string;
};
redis: {
host: string;
port: number;
};
}
@Injectable()
export class ConfigValidationService implements OnModuleInit {
constructor(private readonly nacos: NacosService) {}
async onModuleInit() {
try {
const config = await this.nacos.getConfig<AppConfig>();
// 验证必需的配置项
this.validateConfig(config);
console.log('配置验证通过');
} catch (error) {
console.error('配置验证失败:', error.message);
process.exit(1);
}
}
private validateConfig(config: AppConfig) {
if (!config.database?.host) {
throw new Error('数据库主机配置缺失');
}
if (!config.database?.port || config.database.port <= 0) {
throw new Error('数据库端口配置无效');
}
// 更多验证逻辑...
}
}动态配置更新处理
@Injectable()
export class DynamicConfigService implements OnModuleInit {
private currentConfig: any;
constructor(private readonly nacos: NacosService) {}
async onModuleInit() {
// 初始化配置
this.currentConfig = await this.nacos.getConfig();
// 定期检查配置变化(因为配置订阅会自动更新内部配置)
setInterval(async () => {
const newConfig = await this.nacos.getConfig();
if (JSON.stringify(newConfig) !== JSON.stringify(this.currentConfig)) {
console.log('检测到配置变更');
await this.handleConfigChange(this.currentConfig, newConfig);
this.currentConfig = newConfig;
}
}, 5000); // 每5秒检查一次
}
private async handleConfigChange(oldConfig: any, newConfig: any) {
// 处理配置变更逻辑
console.log('配置已更新:', {
old: oldConfig,
new: newConfig,
});
// 例如:重新初始化数据库连接、重载缓存等
}
}错误处理
import { ConfigError, ServiceError, ValidationError, ConnectionError } from 'cl-nestjs-nacos';
try {
const config = await this.nacos.getConfig('non-existent-key');
} catch (error) {
if (error instanceof ConfigError) {
console.log('配置错误:', error.message, error.context);
} else if (error instanceof ServiceError) {
console.log('服务错误:', error.message, error.context);
}
}API 参考
NacosModule
forRoot(options: NacosOptions, global?: boolean): DynamicModule
创建 Nacos 模块的根模块。
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| --------- | -------------- | ---- | ------ | -------------- |
| options | NacosOptions | ✅ | - | Nacos 配置选项 |
| global | boolean | ❌ | true | 是否为全局模块 |
NacosOptions
Nacos 模块配置选项接口。
interface NacosOptions {
server: string; // Nacos 服务器地址
namespace: string; // 命名空间
// 认证方式 1: AccessKey/SecretKey(推荐用于生产环境)
accessKey?: string; // 访问密钥
secretKey?: string; // 密钥
// 认证方式 2: Username/Password(适用于开发和测试环境)
username?: string; // 用户名
password?: string; // 密码
// 注意:必须提供上述两种认证方式中的一种
enableEnvVars?: boolean; // 是否启用环境变量替换(默认: true)
configOnly?: boolean; // 是否仅配置模式(默认: false)
config?: {
group: string; // 配置分组
dataId: string; // 配置数据ID
type?: ConfigType; // 可选:配置文件类型, 如果不传自动识别
subscribe?: boolean; // 是否订阅配置变更
commons?: BaseConfig[]; // 公共配置文件
};
}NacosService
完整模式下的主要服务类,提供服务注册和配置管理功能。
getConfig<T>(key?: string): Promise<T>
获取配置数据。
参数:
key(可选): 配置键名,如果不提供则返回完整配置对象
返回值:
Promise<T>: 配置数据
示例:
// 获取完整配置
const fullConfig = await nacos.getConfig();
// 获取特定配置项
const dbConfig = await nacos.getConfig<DatabaseConfig>('database');register(name: string, enable?: boolean): Promise<boolean>
注册服务到 Nacos。
参数:
name: 服务名称enable(可选): 是否启用注册(默认: 非开发环境为true)
返回值:
Promise<boolean>: 注册是否成功
示例:
// 注册服务
await nacos.register('my-service');
// 强制注册(即使在开发环境)
await nacos.register('my-service', true);NacosConfigService
仅配置模式下的服务类,只提供配置管理功能。
方法
getConfig<T>(key?: string): Promise<T>
获取配置数据(与 NacosService 中的方法相同)。
ConfigType 枚举
支持的配置文件类型枚举。
enum ConfigType {
TEXT = 'text',
JSON = 'json',
JSONC = 'jsonc',
JSON5 = 'json5',
XML = 'xml',
YAML = 'yaml',
HTML = 'html',
PROPERTIES = 'properties',
}事件
NacosService 继承自 EventEmitter,支持以下事件:
ready
配置加载完成并准备就绪时触发。
nacos.on('ready', () => {
console.log('Nacos 配置已准备就绪');
});错误处理系统
错误类层次
NacosError (抽象基类)
├── ConfigError # 配置相关错误
├── ServiceError # 服务注册错误
├── ValidationError # 验证错误
└── ConnectionError # 连接错误错误类详情
ConfigError - 配置相关错误
class ConfigError extends NacosError {
static configNotFound(key: string): ConfigError;
static parseError(content: string, type?: string): ConfigError;
static loadError(dataId: string, group: string, error: Error): ConfigError;
}ServiceError - 服务注册相关错误
class ServiceError extends NacosError {
static registrationFailed(serviceName: string, error: Error): ServiceError;
static deregistrationFailed(serviceName: string, error: Error): ServiceError;
static ipResolutionFailed(): ServiceError;
}ValidationError - 验证相关错误
class ValidationError extends NacosError {
static requiredOptionMissing(option: string): ValidationError;
static invalidOptionValue(option: string, value: unknown, expected?: string): ValidationError;
}ConnectionError - 连接相关错误
class ConnectionError extends NacosError {
static clientInitializationFailed(error: Error): ConnectionError;
static serverUnreachable(server: string): ConnectionError;
}错误处理特性
- 结构化错误:包含上下文信息
- 错误代码:便于程序化处理
- 调试支持:详细的错误信息
- JSON 序列化:支持日志记录
缓存系统
缓存特性
- TTL 支持:可配置过期时间
- LRU 策略:最近最少使用淘汰
- 自动清理:定期清理过期项
- 内存优化:防止内存泄漏
缓存配置
interface ConfigCacheOptions {
defaultTTL?: number; // 默认TTL(毫秒)
maxSize?: number; // 最大缓存条目数
cleanupInterval?: number; // 清理间隔(毫秒)
}性能优化
1. 缓存策略
- 配置结果缓存
- TTL 和 LRU 结合
- 智能缓存失效
2. 内存管理
- 弱引用使用
- 定期垃圾回收
- 内存泄漏防护
3. 网络优化
- 连接复用
- 请求去重
- 超时控制
故障排除
常见问题
1. 连接 Nacos 服务器失败
错误信息:
Error: connect ECONNREFUSED 127.0.0.1:8848解决方案:
- 检查 Nacos 服务器是否正常运行
- 验证服务器地址和端口是否正确
- 检查网络连接和防火墙设置
- 确保 accessKey 和 secretKey 正确
// 检查连接配置
NacosModule.forRoot({
server: 'http://nacos.example.com:8848', // 确保地址正确
accessKey: process.env.NACOS_ACCESS_KEY,
secretKey: process.env.NACOS_SECRET_KEY,
namespace: process.env.NACOS_NAMESPACE,
});2. 配置加载失败
错误信息:
Error: config must not be null!dataId:application.yaml,group:DEFAULT_GROUP解决方案:
- 检查 dataId 和 group 是否存在于 Nacos 中
- 验证命名空间是否正确
- 确保有读取配置的权限
// 添加错误处理
@Injectable()
export class ConfigService implements OnModuleInit {
constructor(private readonly nacos: NacosService) {}
async onModuleInit() {
try {
await this.nacos.getConfig();
console.log('配置加载成功');
} catch (error) {
console.error('配置加载失败:', error.message);
// 可以使用默认配置或退出应用
}
}
}3. 环境变量替换不生效
问题: 配置中的 ${VARIABLE_NAME} 没有被替换
解决方案:
- 确保
enableEnvVars设置为true(默认值) - 检查环境变量是否正确设置
- 验证环境变量名称是否匹配
# 检查环境变量
echo $DB_HOST
echo $DB_PORT
# 或在 Node.js 中
console.log(process.env.DB_HOST);调试技巧
1. 启用调试日志
# 启用 Nacos 相关的调试日志
DEBUG=nacos* npm start
# 或者只启用特定模块的日志
DEBUG=nacos:config-parser npm start2. 检查配置内容
@Injectable()
export class DebugService implements OnModuleInit {
constructor(private readonly nacos: NacosService) {}
async onModuleInit() {
// 打印完整配置用于调试
const config = await this.nacos.getConfig();
console.log('当前配置:', JSON.stringify(config, null, 2));
}
}3. 监控配置变更
@Injectable()
export class ConfigMonitorService implements OnModuleInit {
constructor(private readonly nacos: NacosService) {}
onModuleInit() {
this.nacos.on('ready', () => {
console.log('✅ Nacos 配置已加载');
});
// 定期检查配置状态
setInterval(async () => {
try {
const config = await this.nacos.getConfig();
console.log('配置检查正常', Object.keys(config));
} catch (error) {
console.error('❌ 配置检查失败:', error.message);
}
}, 30000); // 每30秒检查一次
}
}性能优化建议
1. 减少配置获取频率
@Injectable()
export class CachedConfigService {
private configCache = new Map<string, { value: any; timestamp: number }>();
private readonly CACHE_TTL = 30000; // 30秒缓存
constructor(private readonly nacos: NacosService) {}
async getConfig<T>(key?: string): Promise<T> {
const cacheKey = key || 'full';
const cached = this.configCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
return cached.value;
}
const config = await this.nacos.getConfig<T>(key);
this.configCache.set(cacheKey, {
value: config,
timestamp: Date.now(),
});
return config;
}
}2. 延迟加载非关键配置
@Injectable()
export class LazyConfigService {
constructor(private readonly nacos: NacosService) {}
async getCriticalConfig() {
// 立即加载关键配置
return await this.nacos.getConfig('database');
}
async getNonCriticalConfig() {
// 延迟加载非关键配置
setTimeout(async () => {
const config = await this.nacos.getConfig('features');
// 处理非关键配置
}, 5000);
}
}向后兼容性
- ✅ 现有的 YAML 配置无需修改,继续正常工作
- ✅ 解析失败时会自动回退到 YAML 格式
- ✅ 如果 YAML 也解析失败,会返回原始文本内容
- ✅ 支持渐进式迁移到新的配置格式
贡献指南
欢迎贡献代码!请遵循以下步骤:
- Fork 本仓库
- 创建功能分支 (
git checkout -b feature/amazing-feature) - 提交更改 (
git commit -m 'Add some amazing feature') - 推送到分支 (
git push origin feature/amazing-feature) - 创建 Pull Request
开发环境设置
# 克隆仓库
git clone https://github.com/chaslui/cl-nestjs-nacos.git
cd nestjs-nacos
# 安装依赖
pnpm install
# 运行测试
pnpm test
# 构建项目
pnpm build
# 代码检查
pnpm lint许可证
本项目采用 MIT 许可证。
更新日志
查看 CHANGELOG.md 了解详细的更新历史。
如果您觉得这个项目有用,请给它一个 ⭐️!
