这么久了终于耐心学习了rust并且产出了小工具
用Rust写前端CLI,秒级编译、 fearless并发,一库打包跨平台。告别Node黑箱,性能直追C,生态虽稚但 crates 日增。写CLI如拼乐高,typed config + clap,爽!
首先用到的依赖 项目我已经发布到了crates.io/crates/fron...
js
项目依赖
[package]
name = "frontend-toolkit"
version = "0.1.0"
edition = "2021"
description = "🚀 All-in-one CLI toolkit for frontend developers"
license = "MIT"
authors = ["Your Name <you@example.com>"]
[dependencies]
clap = { version = "4.5", features = ["derive"] }
anyhow = "1.0"
colored = "2.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
warp = "0.3"
minify = "1.3"
regex = "1.10"
walkdir = "2.4"
indicatif = "0.17"
架构设计文档
本文档详细介绍 Frontend Toolkit 的整体架构设计、技术选型和设计原理。
🎯 设计目标
Frontend Toolkit 的架构设计围绕以下核心目标:
- 高性能:毫秒级命令响应时间
- 可扩展性:易于添加新命令和功能
- 可维护性:清晰的模块分离和职责划分
- 安全性:内存安全和文件操作安全
- 易用性:直观的 CLI 交互体验
🏗️ 整体架构
分层架构图
scss
┌─────────────────────────────────────────────────────────────┐
│ 用户接口层 (CLI) │
│ main.rs │
└─────────────────────┬───────────────────────────────────────┘
│ clap 命令解析
┌─────────────────────▼───────────────────────────────────────┐
│ 命令路由层 │
│ Commands enum + match 分发 │
└─────────────────────┬───────────────────────────────────────┘
│ 异步调用
┌─────────────────────▼───────────────────────────────────────┐
│ 业务逻辑层 │
│ commands/ │
│ ├── create_vue3.rs (Vue3 脚手架) │
│ ├── component.rs (组件生成) │
│ ├── minify.rs (文件压缩) │
│ ├── json2ts.rs (JSON 转换) │
│ ├── deps.rs (依赖分析) │
│ └── serve.rs (静态服务) │
└─────────────────────┬───────────────────────────────────────┘
│ 调用工具函数
┌─────────────────────▼───────────────────────────────────────┐
│ 工具服务层 │
│ utils/ │
│ ├── templates.rs (模板引擎) │
│ └── mod.rs (模块声明) │
└─────────────────────┬───────────────────────────────────────┘
│ 系统调用
┌─────────────────────▼───────────────────────────────────────┐
│ 基础设施层 │
│ ├── 文件系统操作 (std::fs) │
│ ├── 网络服务 (warp) │
│ ├── 异步运行时 (tokio) │
│ └── 错误处理 (anyhow) │
└─────────────────────────────────────────────────────────────┘
核心组件
1. 用户接口层 (main.rs)
rust
#[derive(Parser)]
#[command(
name = "frontend-toolkit",
about = "🚀 All-in-one CLI toolkit for frontend developers",
version,
author
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
职责:
- CLI 参数解析和验证
- 版本信息和帮助文档展示
- 全局错误处理和日志输出
2. 命令路由层
rust
#[derive(Subcommand)]
enum Commands {
CreateVue3 { /* 参数定义 */ },
Component { /* 参数定义 */ },
// ... 其他命令
}
match cli.command {
Commands::CreateVue3 { name, template, typescript, output } => {
commands::create_vue3::run(name, template, typescript, output).await?;
}
// ... 其他路由
}
职责:
- 命令到模块的路由分发
- 参数类型转换和验证
- 统一的异步调用管理
3. 业务逻辑层 (commands/)
每个命令模块都遵循统一的结构:
rust
pub async fn run(/* 命令参数 */) -> Result<()> {
// 1. 参数验证和预处理
// 2. 业务逻辑执行
// 3. 结果输出和反馈
}
// 私有辅助函数
async fn helper_function() -> Result<()> {
// 具体实现
}
设计原则:
- 单一职责:每个模块只负责一个主要功能
- 错误传播 :使用
anyhow::Result
统一错误处理 - 异步优先:所有 I/O 操作都是异步的
- 进度反馈:提供用户友好的执行反馈
4. 工具服务层 (utils/)
rust
// templates.rs - 模板引擎
pub fn vue3_package_json(name: &str, template: &str, typescript: bool) -> String {
// 动态生成模板内容
}
pub fn react_component(name: &str) -> String {
// 组件模板生成
}
特性:
- 模板参数化:支持动态参数替换
- 多框架支持:统一的模板接口
- 内容验证:生成内容的格式验证
🔧 技术选型
核心依赖分析
依赖 | 版本 | 用途 | 选择理由 |
---|---|---|---|
clap |
4.5 | CLI 参数解析 | 功能完善、derive 宏支持、类型安全 |
tokio |
1.0 | 异步运行时 | Rust 生态标准、性能优秀 |
anyhow |
1.0 | 错误处理 | 简化错误链、上下文信息丰富 |
serde |
1.0 | 序列化 | JSON 处理标准库 |
colored |
2.1 | 彩色输出 | 提升用户体验 |
walkdir |
2.4 | 目录遍历 | 跨平台目录操作 |
regex |
1.10 | 正则表达式 | 文本处理和模板替换 |
indicatif |
0.17 | 进度条 | 长时间操作的用户反馈 |
技术选型原则
- 性能优先:选择零成本抽象和高性能的库
- 生态兼容:优先选择 Rust 生态中的主流库
- API 稳定:避免使用不稳定或频繁变更的库
- 最小依赖:减少依赖树的复杂度
🎨 设计模式
1. 命令模式 (Command Pattern)
每个 CLI 命令都实现统一的接口:
rust
pub trait Command {
async fn execute(&self) -> Result<()>;
}
// 实际实现中简化为函数形式
pub async fn run(/* 参数 */) -> Result<()> {
// 命令执行逻辑
}
优势:
- 易于添加新命令
- 统一的错误处理
- 可测试性强
2. 工厂模式 (Factory Pattern)
模板生成使用工厂模式:
rust
pub struct TemplateFactory;
impl TemplateFactory {
pub fn create_vue3_project(config: &ProjectConfig) -> Vec<(PathBuf, String)> {
let mut files = Vec::new();
// 根据配置生成不同的文件
files.push(("package.json".into(), vue3_package_json(&config)));
if config.typescript {
files.push(("tsconfig.json".into(), vue3_tsconfig()));
}
files
}
}
优势:
- 集中的创建逻辑
- 易于扩展新模板
- 配置驱动的生成
3. 策略模式 (Strategy Pattern)
文件压缩支持不同策略:
rust
pub trait CompressStrategy {
fn compress(&self, content: &str) -> Result<String>;
}
pub struct CssCompressor;
impl CompressStrategy for CssCompressor {
fn compress(&self, content: &str) -> Result<String> {
// CSS 压缩逻辑
}
}
pub struct JsCompressor;
impl CompressStrategy for JsCompressor {
fn compress(&self, content: &str) -> Result<String> {
// JS 压缩逻辑
}
}
🔄 数据流设计
典型命令执行流程
graph TD
A[用户输入命令] --> B[clap 解析参数]
B --> C[参数验证]
C --> D{参数有效?}
D -->|否| E[显示错误信息]
D -->|是| F[路由到对应模块]
F --> G[执行业务逻辑]
G --> H[调用工具函数]
H --> I[文件系统操作]
I --> J[显示执行结果]
J --> K[命令完成]
E --> K
Vue3 项目创建流程
graph TD
A[create-vue3 命令] --> B[解析项目配置]
B --> C[创建项目目录]
C --> D[生成 package.json]
D --> E{启用 TypeScript?}
E -->|是| F[生成 TS 配置文件]
E -->|否| G[生成 JS 配置文件]
F --> H[生成源码文件]
G --> H
H --> I{包含路由?}
I -->|是| J[生成路由配置]
I -->|否| K[生成基础组件]
J --> L[生成页面组件]
L --> M[生成样式文件]
K --> M
M --> N[显示成功信息]
🛡️ 安全设计
文件操作安全
rust
pub fn validate_path(path: &Path) -> Result<()> {
// 1. 检查路径穿越
if path.components().any(|c| c.as_os_str() == "..") {
return Err(anyhow::anyhow!("Path traversal detected"));
}
// 2. 检查路径长度
if path.as_os_str().len() > 255 {
return Err(anyhow::anyhow!("Path too long"));
}
// 3. 检查非法字符
let path_str = path.to_string_lossy();
if path_str.contains('\0') {
return Err(anyhow::anyhow!("Null character in path"));
}
Ok(())
}
pub async fn write_file_safe(path: &Path, content: &str) -> Result<()> {
validate_path(path)?;
// 确保父目录存在
if let Some(parent) = path.parent() {
tokio::fs::create_dir_all(parent).await?;
}
tokio::fs::write(path, content).await?;
Ok(())
}
输入验证
rust
pub fn validate_project_name(name: &str) -> Result<()> {
// 1. 长度检查
if name.is_empty() || name.len() > 100 {
return Err(anyhow::anyhow!("Invalid project name length"));
}
// 2. 字符检查
if !name.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
return Err(anyhow::anyhow!("Project name contains invalid characters"));
}
// 3. 保留名称检查
let reserved = ["con", "prn", "aux", "nul"];
if reserved.contains(&name.to_lowercase().as_str()) {
return Err(anyhow::anyhow!("Reserved project name"));
}
Ok(())
}
📊 性能优化
异步并发
rust
pub async fn generate_multiple_files(
files: Vec<(PathBuf, String)>
) -> Result<()> {
// 并发写入多个文件
let tasks = files.into_iter().map(|(path, content)| {
tokio::spawn(async move {
write_file_safe(&path, &content).await
})
});
// 等待所有任务完成
for task in tasks {
task.await??;
}
Ok(())
}
内存优化
rust
pub fn process_large_file(path: &Path) -> Result<()> {
use std::io::{BufRead, BufReader};
let file = std::fs::File::open(path)?;
let reader = BufReader::new(file);
// 流式处理,避免加载整个文件到内存
for line in reader.lines() {
let line = line?;
// 处理每一行
process_line(&line)?;
}
Ok(())
}
缓存策略
rust
use std::sync::Mutex;
use std::collections::HashMap;
lazy_static! {
static ref TEMPLATE_CACHE: Mutex<HashMap<String, String>> =
Mutex::new(HashMap::new());
}
pub fn get_template_cached(key: &str) -> Result<String> {
let mut cache = TEMPLATE_CACHE.lock().unwrap();
if let Some(template) = cache.get(key) {
return Ok(template.clone());
}
// 生成模板
let template = generate_template(key)?;
cache.insert(key.to_string(), template.clone());
Ok(template)
}
🧪 测试架构
测试策略分层
scss
测试金字塔
┌─────────────────┐
│ E2E Tests │ ← 少量端到端测试
│ (CLI 集成) │
├─────────────────┤
│ Integration │ ← 中等数量集成测试
│ Tests │
├─────────────────┤
│ Unit Tests │ ← 大量单元测试
│ (函数级别) │
└─────────────────┘
单元测试
rust
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[tokio::test]
async fn test_create_basic_vue3_project() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path().join("test-project");
let result = run(
"test-project".to_string(),
"basic".to_string(),
false,
temp_dir.path().to_string_lossy().to_string(),
).await;
assert!(result.is_ok());
assert!(project_path.join("package.json").exists());
assert!(project_path.join("src/main.js").exists());
}
}
集成测试
rust
// tests/cli_integration.rs
use std::process::Command;
use tempfile::TempDir;
#[test]
fn test_full_vue3_creation_flow() {
let temp_dir = TempDir::new().unwrap();
// 测试项目创建
let output = Command::new("cargo")
.args(&["run", "--", "create-vue3", "integration-test", "--template", "full"])
.current_dir(&temp_dir)
.output()
.expect("Failed to execute command");
assert!(output.status.success());
// 验证生成的文件
let project_dir = temp_dir.path().join("integration-test");
assert!(project_dir.join("package.json").exists());
assert!(project_dir.join("src/router/index.js").exists());
assert!(project_dir.join("src/stores/counter.js").exists());
}
🔮 扩展性设计
插件架构 (未来计划)
rust
pub trait Plugin {
fn name(&self) -> &str;
fn version(&self) -> &str;
async fn execute(&self, args: Vec<String>) -> Result<()>;
}
pub struct PluginManager {
plugins: HashMap<String, Box<dyn Plugin>>,
}
impl PluginManager {
pub fn register_plugin(&mut self, plugin: Box<dyn Plugin>) {
self.plugins.insert(plugin.name().to_string(), plugin);
}
pub async fn execute_plugin(&self, name: &str, args: Vec<String>) -> Result<()> {
if let Some(plugin) = self.plugins.get(name) {
plugin.execute(args).await
} else {
Err(anyhow::anyhow!("Plugin not found: {}", name))
}
}
}
配置系统
rust
#[derive(Serialize, Deserialize)]
pub struct GlobalConfig {
pub default_template: String,
pub auto_install_deps: bool,
pub preferred_package_manager: String,
pub custom_templates: HashMap<String, String>,
}
impl GlobalConfig {
pub fn load() -> Result<Self> {
let config_path = Self::config_path()?;
if config_path.exists() {
let content = std::fs::read_to_string(config_path)?;
Ok(serde_json::from_str(&content)?)
} else {
Ok(Self::default())
}
}
pub fn save(&self) -> Result<()> {
let config_path = Self::config_path()?;
let content = serde_json::to_string_pretty(self)?;
std::fs::write(config_path, content)?;
Ok(())
}
}
📈 监控和日志
性能监控
rust
use std::time::Instant;
pub struct PerformanceMonitor {
start_time: Instant,
checkpoints: Vec<(String, Instant)>,
}
impl PerformanceMonitor {
pub fn new() -> Self {
Self {
start_time: Instant::now(),
checkpoints: Vec::new(),
}
}
pub fn checkpoint(&mut self, label: &str) {
self.checkpoints.push((label.to_string(), Instant::now()));
}
pub fn report(&self) {
let total_duration = self.start_time.elapsed();
println!("Total execution time: {:?}", total_duration);
let mut last_time = self.start_time;
for (label, time) in &self.checkpoints {
let duration = time.duration_since(last_time);
println!(" {}: {:?}", label, duration);
last_time = *time;
}
}
}
结构化日志
rust
use serde_json::json;
pub fn log_command_execution(
command: &str,
args: &[String],
duration: std::time::Duration,
success: bool,
) {
let log_entry = json!({
"timestamp": chrono::Utc::now().to_rfc3339(),
"command": command,
"args": args,
"duration_ms": duration.as_millis(),
"success": success,
});
println!("{}", log_entry);
}
🎯 总结
Frontend Toolkit 的架构设计体现了以下核心原则:
- 模块化:清晰的职责分离,易于维护和扩展
- 性能优先:异步设计和零成本抽象
- 安全性:全面的输入验证和文件操作安全
- 可测试性:分层测试策略和依赖注入
- 用户体验:直观的 CLI 设计和友好的错误提示
这种架构为项目的长期发展奠定了坚实的基础,支持快速添加新功能的同时保持代码质量和性能。🚀
✨ 核心特性
- 🚀 高性能:基于 Rust 编写,运行速度快,内存安全
- 🛠️ 多功能:支持组件生成、项目脚手架、文件压缩等多种功能
- 🎯 易用性:直观的命令行交互界面,上手简单
- 🔧 可扩展:模块化设计,便于新增命令和功能
- 📦 开箱即用:无需复杂配置,安装即可使用
🛠️ 主要功能
功能 | 命令 | 描述 |
---|---|---|
Vue3 项目脚手架 | create-vue3 |
创建 Vue3 全家桶项目 (Vue + Router + Pinia + TypeScript) |
组件生成器 | component |
快速生成 React/Vue 组件模板 |
文件压缩 | minify |
压缩 CSS/JS 文件,减小文件体积 |
JSON 转 TypeScript | json2ts |
将 JSON 数据转换为 TypeScript 接口定义 |
依赖分析 | deps |
分析 package.json 依赖结构 |
静态文件服务 | serve |
启动本地静态文件服务器 |
规划中的功能
- 🔄 React 项目脚手架
- 🔄 更多模板支持
- 🔄 代码格式化工具
- 🔄 打包和发布工具