镜像自地址
https://github.com/lhly/search-syntax-pro.git
已同步 2026-10-20 22:37:48 +08:00
feat(build): 添加扩展打包脚本和文档
添加自动打包脚本 package.js,支持从 dist 目录生成发布包 新增 PACKAGING.md 文档说明打包流程 更新 .gitignore 忽略 releases 目录 优化 manifest.json 主机权限配置 新增 CHANGELOG-PERMISSIONS.md 记录权限变更
这个提交包含在:
父节点
c6b48167bb
当前提交
f32329ce19
4
.gitignore
vendored
4
.gitignore
vendored
@ -99,6 +99,10 @@ temp/
|
||||
extension-temp/
|
||||
crx/
|
||||
|
||||
# Release packages (generated by npm run package)
|
||||
releases/
|
||||
*.zip
|
||||
|
||||
# Test files
|
||||
test-results/
|
||||
playwright-report/
|
||||
|
||||
236
CHANGELOG-PERMISSIONS.md
普通文件
236
CHANGELOG-PERMISSIONS.md
普通文件
@ -0,0 +1,236 @@
|
||||
# 🔒 权限配置优化记录
|
||||
|
||||
## 📅 优化时间
|
||||
2025-11-07
|
||||
|
||||
## 🎯 优化目标
|
||||
提高 Chrome Web Store 审核通过率,符合最小权限原则。
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 优化前配置(高风险)
|
||||
|
||||
### 问题:使用了广泛的主机权限
|
||||
|
||||
```json
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
]
|
||||
```
|
||||
|
||||
### 风险分析
|
||||
|
||||
| 风险级别 | 说明 |
|
||||
|---------|------|
|
||||
| 🔴 **审核风险** | `<all_urls>` 是高敏感权限,会触发严格审核 |
|
||||
| 🔴 **信任问题** | 用户看到"可访问所有网站"会担心隐私 |
|
||||
| 🔴 **政策违规** | 不符合 Chrome Web Store 最小权限原则 |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 优化后配置(低风险)
|
||||
|
||||
### 解决方案:明确列出需要的域名
|
||||
|
||||
```json
|
||||
"host_permissions": [
|
||||
"https://www.baidu.com/*",
|
||||
"https://www.google.com/*",
|
||||
"https://www.bing.com/*",
|
||||
"https://www.sogou.com/*",
|
||||
"https://www.so.com/*"
|
||||
]
|
||||
```
|
||||
|
||||
### 优势分析
|
||||
|
||||
| 优势 | 说明 |
|
||||
|------|------|
|
||||
| ✅ **审核友好** | 明确的权限范围,降低审核难度 |
|
||||
| ✅ **用户信任** | 用户清楚知道扩展只在 5 个搜索引擎工作 |
|
||||
| ✅ **政策合规** | 符合最小权限原则,透明化权限申请 |
|
||||
| ✅ **功能无损** | 完全满足现有功能需求 |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 技术验证
|
||||
|
||||
### 配置一致性检查
|
||||
|
||||
✅ **host_permissions 与 content_scripts.matches 完全一致**
|
||||
|
||||
```json
|
||||
// host_permissions(主机权限)
|
||||
"host_permissions": [
|
||||
"https://www.baidu.com/*",
|
||||
"https://www.google.com/*",
|
||||
"https://www.bing.com/*",
|
||||
"https://www.sogou.com/*",
|
||||
"https://www.so.com/*"
|
||||
]
|
||||
|
||||
// content_scripts.matches(内容脚本匹配)
|
||||
"matches": [
|
||||
"https://www.baidu.com/*",
|
||||
"https://www.google.com/*",
|
||||
"https://www.bing.com/*",
|
||||
"https://www.sogou.com/*",
|
||||
"https://www.so.com/*"
|
||||
]
|
||||
```
|
||||
|
||||
### 权限统计
|
||||
|
||||
| 配置项 | 数量 | 说明 |
|
||||
|--------|------|------|
|
||||
| 基础权限 (permissions) | 4 | storage, activeTab, scripting, contextMenus |
|
||||
| 主机权限 (host_permissions) | 5 | 5 个搜索引擎域名 |
|
||||
| Content Scripts 域名 | 5 | 与主机权限一致 ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 📦 打包验证
|
||||
|
||||
### 新版本包信息
|
||||
|
||||
```
|
||||
文件名: releases/ssp-v1.0.0.zip
|
||||
版本号: v1.0.0
|
||||
文件大小: 101.59 KB
|
||||
```
|
||||
|
||||
### ZIP 包内容验证
|
||||
|
||||
✅ `manifest.json` 已包含优化后的权限配置
|
||||
✅ 所有域名列表一致
|
||||
✅ JSON 格式正确
|
||||
✅ 符合 Manifest V3 规范
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Chrome Web Store 审核材料
|
||||
|
||||
### 更新后的主机权限理由
|
||||
|
||||
```
|
||||
本扩展请求主机权限的理由:
|
||||
|
||||
1. 多搜索引擎支持
|
||||
- 在百度、谷歌、必应、搜狗、360搜索 5 个主流搜索引擎上提供功能
|
||||
- 为每个搜索引擎提供适配的高级语法生成
|
||||
|
||||
2. 功能实现
|
||||
- 在搜索结果页面注入内容脚本
|
||||
- 读取搜索框内容以实现"优化当前搜索"功能
|
||||
- 将生成的搜索语法自动填入搜索框
|
||||
|
||||
3. 权限范围
|
||||
- 仅限于明确列出的 5 个搜索引擎域名
|
||||
- 不访问其他网站,不收集用户浏览数据
|
||||
- 符合最小权限原则
|
||||
|
||||
此权限是扩展核心功能的必要条件。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 影响分析
|
||||
|
||||
### ✅ 正面影响
|
||||
|
||||
1. **审核通过率提升**
|
||||
- 从高风险配置改为低风险配置
|
||||
- 减少审核人员的疑虑和问询
|
||||
|
||||
2. **用户信任度提升**
|
||||
- 权限申请更透明
|
||||
- 用户清楚扩展的工作范围
|
||||
|
||||
3. **政策合规性**
|
||||
- 符合 Chrome Web Store 最佳实践
|
||||
- 遵循最小权限原则
|
||||
|
||||
### ⚠️ 注意事项
|
||||
|
||||
1. **未来扩展搜索引擎支持**
|
||||
- 如需支持新搜索引擎,需更新 `host_permissions`
|
||||
- 同时更新 `content_scripts.matches`
|
||||
- 重新发布版本并说明权限变更原因
|
||||
|
||||
2. **版本更新流程**
|
||||
- 添加新域名前评估必要性
|
||||
- 在更新说明中解释权限变更
|
||||
- 提前准备审核材料
|
||||
|
||||
---
|
||||
|
||||
## 🚀 后续维护
|
||||
|
||||
### 添加新搜索引擎的流程
|
||||
|
||||
1. **评估必要性**
|
||||
- 确认用户需求
|
||||
- 评估搜索引擎市场份额
|
||||
|
||||
2. **更新配置**
|
||||
```json
|
||||
// 在两个地方同时添加
|
||||
"host_permissions": [
|
||||
// 现有域名...
|
||||
"https://www.new-search-engine.com/*" // 新增
|
||||
],
|
||||
"content_scripts": [{
|
||||
"matches": [
|
||||
// 现有域名...
|
||||
"https://www.new-search-engine.com/*" // 新增
|
||||
]
|
||||
}]
|
||||
```
|
||||
|
||||
3. **更新审核材料**
|
||||
- 在权限理由中说明新增搜索引擎
|
||||
- 更新"支持的搜索引擎数量"
|
||||
- 准备详细的功能说明
|
||||
|
||||
4. **版本发布**
|
||||
- 更新 `package.json` 版本号
|
||||
- 在 CHANGELOG.md 记录变更
|
||||
- 重新打包发布
|
||||
|
||||
---
|
||||
|
||||
## ✅ 最佳实践总结
|
||||
|
||||
### Chrome Web Store 审核最佳实践
|
||||
|
||||
1. **最小权限原则**
|
||||
- ✅ 只申请必要的权限
|
||||
- ✅ 使用具体域名代替通配符
|
||||
- ✅ 避免 `<all_urls>` 等广泛权限
|
||||
|
||||
2. **透明度原则**
|
||||
- ✅ 清晰说明每个权限的用途
|
||||
- ✅ 提供具体的使用场景
|
||||
- ✅ 强调隐私保护措施
|
||||
|
||||
3. **一致性原则**
|
||||
- ✅ 确保 `host_permissions` 与实际使用一致
|
||||
- ✅ 与 `content_scripts.matches` 保持同步
|
||||
- ✅ 权限理由与实际功能匹配
|
||||
|
||||
4. **用户信任原则**
|
||||
- ✅ 让用户了解扩展的工作范围
|
||||
- ✅ 说明数据处理方式(本地存储)
|
||||
- ✅ 提供隐私政策链接
|
||||
|
||||
---
|
||||
|
||||
## 📝 相关文档
|
||||
|
||||
- [Chrome 扩展权限文档](https://developer.chrome.com/docs/extensions/mv3/declare_permissions/)
|
||||
- [Chrome Web Store 政策](https://developer.chrome.com/docs/webstore/program_policies/)
|
||||
- [最小权限原则](https://developer.chrome.com/docs/extensions/mv3/security/#permissions)
|
||||
|
||||
---
|
||||
|
||||
**SearchSyntax Pro** - 遵循最佳实践,保护用户隐私
|
||||
258
PACKAGING.md
普通文件
258
PACKAGING.md
普通文件
@ -0,0 +1,258 @@
|
||||
# 📦 Chrome扩展打包指南
|
||||
|
||||
本文档说明如何将 SearchSyntax Pro 打包为可发布到 Chrome Web Store 的 ZIP 文件。
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 方式一:完整打包流程(推荐)
|
||||
|
||||
```bash
|
||||
npm run package
|
||||
```
|
||||
|
||||
这个命令会:
|
||||
1. 执行 TypeScript 编译
|
||||
2. 运行 Vite 构建
|
||||
3. 复制静态资源(manifest.json、icons、国际化文件)
|
||||
4. 将 dist 目录打包为 ZIP 文件
|
||||
|
||||
### 方式二:仅打包(快速迭代)
|
||||
|
||||
如果你已经运行过 `npm run build`,可以直接打包:
|
||||
|
||||
```bash
|
||||
npm run package:only
|
||||
```
|
||||
|
||||
## 📁 输出文件
|
||||
|
||||
打包后会在项目根目录生成:
|
||||
|
||||
```
|
||||
releases/
|
||||
└── ssp-v{version}.zip
|
||||
```
|
||||
|
||||
例如:`releases/ssp-v1.0.0.zip`
|
||||
|
||||
版本号自动从 `package.json` 读取。
|
||||
|
||||
## 🔍 打包内容
|
||||
|
||||
ZIP 包会包含 `dist/` 目录的所有必要文件:
|
||||
|
||||
- ✅ `manifest.json` - Chrome 扩展配置
|
||||
- ✅ `background.js` - Service Worker(后台脚本)
|
||||
- ✅ `popup.js` - 弹窗界面脚本
|
||||
- ✅ `options.js` - 设置页面脚本
|
||||
- ✅ `content.js` - 内容脚本
|
||||
- ✅ `icons/` - 所有尺寸的图标(16x16, 32x32, 48x48, 128x128)
|
||||
- ✅ `_locales/` - 国际化资源文件
|
||||
- ✅ 其他构建产物(CSS、资源文件等)
|
||||
|
||||
### 自动排除的文件
|
||||
|
||||
打包脚本会自动排除以下文件:
|
||||
- ❌ `.DS_Store` - macOS 系统文件
|
||||
- ❌ `*.backup`, `*.bak` - 备份文件
|
||||
- ❌ `*.tmp` - 临时文件
|
||||
- ❌ `__MACOSX/` - macOS 压缩产物
|
||||
- ❌ `.git/` - Git 版本控制文件
|
||||
- ❌ `node_modules/` - 依赖文件
|
||||
|
||||
## 📊 打包信息
|
||||
|
||||
打包完成后,脚本会显示:
|
||||
|
||||
- ✅ 文件名称和路径
|
||||
- ✅ 版本号
|
||||
- ✅ 文件大小(压缩后)
|
||||
- ✅ 文件质量评估(<5MB优秀)
|
||||
|
||||
## 🌐 上传到 Chrome Web Store
|
||||
|
||||
### 前提条件
|
||||
|
||||
1. **开发者账户**:注册 Chrome Web Store 开发者账户($5 一次性费用)
|
||||
2. **准备材料**:
|
||||
- 扩展 ZIP 包(通过 `npm run package` 生成)
|
||||
- 至少 1 张截图(1280x800 或 640x400)
|
||||
- 详细描述和简短描述
|
||||
- 隐私政策 URL(如果使用权限)
|
||||
|
||||
### 上传步骤
|
||||
|
||||
1. **访问开发者控制台**
|
||||
```
|
||||
https://chrome.google.com/webstore/devconsole
|
||||
```
|
||||
|
||||
2. **创建新项**
|
||||
- 点击"新增项"
|
||||
- 上传生成的 ZIP 文件(`releases/ssp-v1.0.0.zip`)
|
||||
|
||||
3. **填写商店列表信息**
|
||||
- 详细描述(推荐 800-1000 字)
|
||||
- 简短描述(<132 字符)
|
||||
- 上传截图(至少 1 张,推荐 3-5 张)
|
||||
- 选择分类:生产力工具
|
||||
|
||||
4. **配置隐私设置**
|
||||
- 说明权限用途
|
||||
- 提供隐私政策链接
|
||||
- 说明数据处理方式
|
||||
|
||||
5. **提交审核**
|
||||
- 检查所有必填项
|
||||
- 点击"提交审核"
|
||||
- 等待审核结果(通常 24-48 小时)
|
||||
|
||||
## 🔄 版本更新流程
|
||||
|
||||
### 1. 更新版本号
|
||||
|
||||
在 `package.json` 中更新版本号:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
建议同步更新 `public/manifest.json` 的版本号(虽然打包脚本会使用 package.json 的版本)。
|
||||
|
||||
### 2. 构建和打包
|
||||
|
||||
```bash
|
||||
npm run package
|
||||
```
|
||||
|
||||
### 3. 上传新版本
|
||||
|
||||
- 在 Chrome Web Store 开发者控制台
|
||||
- 选择现有扩展
|
||||
- 上传新的 ZIP 包
|
||||
- 填写更新说明
|
||||
- 提交审核
|
||||
|
||||
## 🛠️ 故障排查
|
||||
|
||||
### 问题:找不到 zip 命令
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
❌ 未找到系统zip命令
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
|
||||
- **macOS**:已预装 zip,无需操作
|
||||
- **Linux**:
|
||||
```bash
|
||||
sudo apt-get install zip
|
||||
```
|
||||
- **Windows**:
|
||||
- 安装 [Git for Windows](https://git-scm.com/download/win)(包含 Git Bash)
|
||||
- 或使用 [WSL (Windows Subsystem for Linux)](https://docs.microsoft.com/en-us/windows/wsl/install)
|
||||
|
||||
### 问题:dist 目录不存在
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
❌ dist目录不存在
|
||||
请先运行构建命令:npm run build
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 问题:文件大小过大
|
||||
|
||||
**警告信息**:
|
||||
```
|
||||
⚠️ ZIP文件超过10MB,可能影响审核速度
|
||||
```
|
||||
|
||||
**优化建议**:
|
||||
1. 检查是否包含不必要的资源文件
|
||||
2. 压缩图片资源
|
||||
3. 移除未使用的依赖
|
||||
4. 使用 Vite 构建优化
|
||||
|
||||
### 问题:版本号格式无效
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
❌ 无效的版本号格式
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
确保 `package.json` 中的版本号遵循语义化版本规范(SemVer):
|
||||
- ✅ 正确:`1.0.0`, `1.2.3`, `2.0.0-beta.1`
|
||||
- ❌ 错误:`v1.0`, `1.0`, `latest`
|
||||
|
||||
## 📝 最佳实践
|
||||
|
||||
### 1. 版本管理
|
||||
|
||||
- 遵循[语义化版本](https://semver.org/lang/zh-CN/)规范
|
||||
- 主版本号(Major):不兼容的 API 修改
|
||||
- 次版本号(Minor):向下兼容的功能新增
|
||||
- 修订号(Patch):向下兼容的问题修正
|
||||
|
||||
### 2. 发布前检查清单
|
||||
|
||||
- [ ] 运行所有测试:`npm test`
|
||||
- [ ] 代码质量检查:`npm run lint`
|
||||
- [ ] 类型检查:`npm run type-check`
|
||||
- [ ] 本地功能测试(加载 dist 到浏览器)
|
||||
- [ ] 更新版本号
|
||||
- [ ] 更新 CHANGELOG.md
|
||||
- [ ] 创建 Git 标签
|
||||
|
||||
### 3. Git 工作流
|
||||
|
||||
```bash
|
||||
# 1. 更新版本号
|
||||
npm version patch # 或 minor, major
|
||||
|
||||
# 2. 提交更改
|
||||
git add .
|
||||
git commit -m "chore: release v1.0.1"
|
||||
|
||||
# 3. 创建标签
|
||||
git tag v1.0.1
|
||||
|
||||
# 4. 推送到远程
|
||||
git push origin main --tags
|
||||
|
||||
# 5. 打包发布
|
||||
npm run package
|
||||
```
|
||||
|
||||
### 4. 保留历史版本
|
||||
|
||||
`releases/` 目录已在 `.gitignore` 中排除,但建议:
|
||||
- 在本地保留最近 3-5 个版本的 ZIP 包
|
||||
- 使用 GitHub Releases 存档重要版本
|
||||
- 记录每个版本的发布日期和审核通过日期
|
||||
|
||||
## 🔗 相关资源
|
||||
|
||||
- [Chrome 扩展开发者文档](https://developer.chrome.com/docs/extensions/)
|
||||
- [Chrome Web Store 政策](https://developer.chrome.com/docs/webstore/program_policies/)
|
||||
- [发布检查清单](https://developer.chrome.com/docs/webstore/publish/)
|
||||
- [语义化版本规范](https://semver.org/lang/zh-CN/)
|
||||
|
||||
## 💡 提示
|
||||
|
||||
- 首次发布审核可能需要更长时间(1-7 天)
|
||||
- 后续更新通常更快(24-48 小时)
|
||||
- 保持良好的代码质量和文档可以加速审核
|
||||
- 及时响应审核人员的问题和反馈
|
||||
|
||||
---
|
||||
|
||||
**SearchSyntax Pro** - 让高级搜索变得简单易用
|
||||
@ -6,6 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build && node scripts/post-build.js",
|
||||
"package": "npm run build && node scripts/package.js",
|
||||
"package:only": "node scripts/package.js",
|
||||
"preview": "vite preview",
|
||||
"test": "jest",
|
||||
"test:e2e": "playwright test",
|
||||
|
||||
@ -13,7 +13,11 @@
|
||||
],
|
||||
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
"https://www.baidu.com/*",
|
||||
"https://www.google.com/*",
|
||||
"https://www.bing.com/*",
|
||||
"https://www.sogou.com/*",
|
||||
"https://www.so.com/*"
|
||||
],
|
||||
|
||||
"background": {
|
||||
|
||||
380
scripts/package.js
普通文件
380
scripts/package.js
普通文件
@ -0,0 +1,380 @@
|
||||
/**
|
||||
* Chrome扩展自动打包脚本
|
||||
*
|
||||
* 功能:
|
||||
* - 自动从package.json读取版本号
|
||||
* - 将dist目录打包为ZIP格式
|
||||
* - 生成带版本号的发布包:releases/ssp-v{version}.zip
|
||||
* - 提供详细的错误提示和成功信息
|
||||
*
|
||||
* 架构设计:
|
||||
* - 单一职责原则:每个函数只做一件事
|
||||
* - 错误优先:完善的错误处理和用户提示
|
||||
* - 零依赖:仅使用Node.js内置模块
|
||||
*
|
||||
* @author SearchSyntax Pro Team
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, mkdirSync, readFileSync, statSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// ============================================================================
|
||||
// 配置常量
|
||||
// ============================================================================
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const rootDir = join(__dirname, '..');
|
||||
|
||||
const CONFIG = {
|
||||
distDir: join(rootDir, 'dist'),
|
||||
releasesDir: join(rootDir, 'releases'),
|
||||
packageName: 'ssp',
|
||||
packageJsonPath: join(rootDir, 'package.json'),
|
||||
};
|
||||
|
||||
// 颜色输出(在终端中更友好)
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
bright: '\x1b[1m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
cyan: '\x1b[36m',
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 工具函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 格式化文件大小为人类可读格式
|
||||
* @param {number} bytes - 字节数
|
||||
* @returns {string} 格式化后的大小(如 "1.23 MB")
|
||||
*/
|
||||
function formatFileSize(bytes) {
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印带颜色的消息
|
||||
* @param {string} message - 消息内容
|
||||
* @param {string} color - 颜色代码
|
||||
*/
|
||||
function log(message, color = colors.reset) {
|
||||
console.log(`${color}${message}${colors.reset}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印成功消息
|
||||
*/
|
||||
function logSuccess(message) {
|
||||
log(`✅ ${message}`, colors.green);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印错误消息
|
||||
*/
|
||||
function logError(message) {
|
||||
log(`❌ ${message}`, colors.red);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印警告消息
|
||||
*/
|
||||
function logWarning(message) {
|
||||
log(`⚠️ ${message}`, colors.yellow);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印信息消息
|
||||
*/
|
||||
function logInfo(message) {
|
||||
log(`ℹ️ ${message}`, colors.cyan);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印标题
|
||||
*/
|
||||
function logHeader(message) {
|
||||
log(`\n${'='.repeat(60)}`, colors.blue);
|
||||
log(` ${message}`, colors.bright + colors.blue);
|
||||
log(`${'='.repeat(60)}\n`, colors.blue);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 环境验证层
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 检查系统是否安装了zip命令
|
||||
* @throws {Error} 如果zip命令不存在
|
||||
*/
|
||||
function checkZipCommand() {
|
||||
try {
|
||||
execSync('which zip', { stdio: 'ignore' });
|
||||
logSuccess('检测到系统zip命令');
|
||||
} catch (error) {
|
||||
logError('未找到系统zip命令');
|
||||
console.log('\n请安装zip工具:');
|
||||
console.log(' macOS: 已预装');
|
||||
console.log(' Linux: sudo apt-get install zip');
|
||||
console.log(' Windows: 安装 Git Bash 或使用 WSL\n');
|
||||
throw new Error('缺少zip命令');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查dist目录是否存在
|
||||
* @throws {Error} 如果dist目录不存在
|
||||
*/
|
||||
function checkDistDirectory() {
|
||||
if (!existsSync(CONFIG.distDir)) {
|
||||
logError('dist目录不存在');
|
||||
console.log('\n请先运行构建命令:');
|
||||
console.log(' npm run build\n');
|
||||
throw new Error('缺少dist目录');
|
||||
}
|
||||
logSuccess(`找到构建目录: ${CONFIG.distDir}`);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 版本管理层
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 从package.json读取版本号
|
||||
* @returns {string} 版本号(如 "1.0.0")
|
||||
* @throws {Error} 如果读取失败或版本号无效
|
||||
*/
|
||||
function getVersion() {
|
||||
try {
|
||||
const packageJson = JSON.parse(
|
||||
readFileSync(CONFIG.packageJsonPath, 'utf-8')
|
||||
);
|
||||
|
||||
const version = packageJson.version;
|
||||
if (!version) {
|
||||
throw new Error('package.json中未找到version字段');
|
||||
}
|
||||
|
||||
// 验证版本号格式(语义化版本)
|
||||
if (!/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(version)) {
|
||||
throw new Error(`无效的版本号格式: ${version}`);
|
||||
}
|
||||
|
||||
logSuccess(`读取版本号: v${version}`);
|
||||
return version;
|
||||
} catch (error) {
|
||||
logError(`读取版本号失败: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文件系统层
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 创建releases目录(如果不存在)
|
||||
*/
|
||||
function prepareReleasesDir() {
|
||||
if (!existsSync(CONFIG.releasesDir)) {
|
||||
mkdirSync(CONFIG.releasesDir, { recursive: true });
|
||||
logSuccess(`创建输出目录: ${CONFIG.releasesDir}`);
|
||||
} else {
|
||||
logInfo(`使用现有输出目录: ${CONFIG.releasesDir}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查同名包文件是否已存在
|
||||
* @param {string} zipFileName - ZIP文件名
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
function checkExistingPackage(zipFileName) {
|
||||
const zipPath = join(CONFIG.releasesDir, zipFileName);
|
||||
if (existsSync(zipPath)) {
|
||||
const stats = statSync(zipPath);
|
||||
const size = formatFileSize(stats.size);
|
||||
logWarning(`发现同名文件: ${zipFileName} (${size})`);
|
||||
logInfo('将覆盖现有文件');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 打包执行层
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 执行ZIP打包命令
|
||||
* @param {string} version - 版本号
|
||||
* @returns {string} 生成的ZIP文件路径
|
||||
* @throws {Error} 如果打包失败
|
||||
*/
|
||||
function createZipPackage(version) {
|
||||
const zipFileName = `${CONFIG.packageName}-v${version}.zip`;
|
||||
const zipPath = join(CONFIG.releasesDir, zipFileName);
|
||||
|
||||
logInfo('开始打包...');
|
||||
|
||||
try {
|
||||
// 切换到dist目录并打包所有内容
|
||||
// -r: 递归打包目录
|
||||
// -q: 安静模式(不显示进度)
|
||||
// -9: 最高压缩率
|
||||
// -x: 排除不必要的文件
|
||||
const excludePatterns = [
|
||||
'*.DS_Store', // macOS系统文件
|
||||
'__MACOSX/*', // macOS压缩产物
|
||||
'*.backup', // 备份文件
|
||||
'*.bak', // 备份文件
|
||||
'*.tmp', // 临时文件
|
||||
'.git/*', // Git文件
|
||||
'node_modules/*', // 依赖文件(不应该在dist中)
|
||||
].map(p => `"${p}"`).join(' ');
|
||||
|
||||
const command = `cd "${CONFIG.distDir}" && zip -r -q -9 "${zipPath}" . -x ${excludePatterns}`;
|
||||
|
||||
execSync(command, { stdio: 'inherit' });
|
||||
|
||||
// 验证ZIP文件是否成功创建
|
||||
if (!existsSync(zipPath)) {
|
||||
throw new Error('ZIP文件创建失败');
|
||||
}
|
||||
|
||||
return zipPath;
|
||||
} catch (error) {
|
||||
logError(`打包失败: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算并返回文件大小信息
|
||||
* @param {string} filePath - 文件路径
|
||||
* @returns {Object} 包含size和formattedSize的对象
|
||||
*/
|
||||
function getFileInfo(filePath) {
|
||||
const stats = statSync(filePath);
|
||||
return {
|
||||
size: stats.size,
|
||||
formattedSize: formatFileSize(stats.size),
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 报告层
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 显示打包成功信息
|
||||
* @param {string} zipPath - ZIP文件路径
|
||||
* @param {string} version - 版本号
|
||||
*/
|
||||
function displaySuccess(zipPath, version) {
|
||||
const fileInfo = getFileInfo(zipPath);
|
||||
const fileName = join('releases', `${CONFIG.packageName}-v${version}.zip`);
|
||||
|
||||
logHeader('打包成功!');
|
||||
|
||||
console.log(`${colors.bright}📦 扩展包信息${colors.reset}`);
|
||||
console.log(` 文件名称: ${colors.cyan}${fileName}${colors.reset}`);
|
||||
console.log(` 版本号: ${colors.green}v${version}${colors.reset}`);
|
||||
console.log(` 文件大小: ${colors.yellow}${fileInfo.formattedSize}${colors.reset}`);
|
||||
console.log(` 完整路径: ${colors.blue}${zipPath}${colors.reset}`);
|
||||
|
||||
console.log(`\n${colors.bright}🚀 下一步操作${colors.reset}`);
|
||||
console.log(' 1. 访问 Chrome Web Store Developer Console');
|
||||
console.log(' https://chrome.google.com/webstore/devconsole');
|
||||
console.log(' 2. 上传生成的ZIP文件');
|
||||
console.log(' 3. 填写商店列表信息');
|
||||
console.log(' 4. 提交审核\n');
|
||||
|
||||
// 文件大小警告
|
||||
if (fileInfo.size > 10 * 1024 * 1024) {
|
||||
logWarning('ZIP文件超过10MB,可能影响审核速度');
|
||||
logInfo('建议优化资源文件大小');
|
||||
} else if (fileInfo.size > 5 * 1024 * 1024) {
|
||||
logInfo('ZIP文件大小适中(5-10MB)');
|
||||
} else {
|
||||
logSuccess('ZIP文件大小优秀(<5MB)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误信息并退出
|
||||
* @param {Error} error - 错误对象
|
||||
*/
|
||||
function displayError(error) {
|
||||
logHeader('打包失败');
|
||||
logError(error.message);
|
||||
|
||||
if (error.stack) {
|
||||
console.log(`\n${colors.yellow}详细错误信息:${colors.reset}`);
|
||||
console.log(error.stack);
|
||||
}
|
||||
|
||||
console.log(`\n${colors.cyan}需要帮助?${colors.reset}`);
|
||||
console.log(' - 查看项目文档: README.md');
|
||||
console.log(' - 提交Issue: https://github.com/lhly/search-syntax-pro/issues\n');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 主流程
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 主函数:执行完整的打包流程
|
||||
*/
|
||||
async function main() {
|
||||
try {
|
||||
logHeader('🎁 Chrome扩展自动打包工具');
|
||||
|
||||
// 第1步:环境验证
|
||||
console.log(`${colors.bright}第1步: 环境验证${colors.reset}`);
|
||||
checkZipCommand();
|
||||
checkDistDirectory();
|
||||
|
||||
// 第2步:读取版本号
|
||||
console.log(`\n${colors.bright}第2步: 版本管理${colors.reset}`);
|
||||
const version = getVersion();
|
||||
|
||||
// 第3步:准备输出目录
|
||||
console.log(`\n${colors.bright}第3步: 文件系统准备${colors.reset}`);
|
||||
prepareReleasesDir();
|
||||
|
||||
const zipFileName = `${CONFIG.packageName}-v${version}.zip`;
|
||||
checkExistingPackage(zipFileName);
|
||||
|
||||
// 第4步:执行打包
|
||||
console.log(`\n${colors.bright}第4步: 执行打包${colors.reset}`);
|
||||
const zipPath = createZipPackage(version);
|
||||
|
||||
// 第5步:显示结果
|
||||
logSuccess('打包完成');
|
||||
displaySuccess(zipPath, version);
|
||||
|
||||
} catch (error) {
|
||||
displayError(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行主流程
|
||||
main();
|
||||
正在加载...
在新工单中引用
屏蔽一个用户