jinbi-utils 1.0.20 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +19 -0
- package/.cz-config.js +55 -0
- package/.dockerignore +3 -0
- package/.editorconfig +12 -0
- package/.eslintignore +8 -0
- package/.eslintrc.js +54 -0
- package/.versionrc.json +9 -0
- package/CHANGELOG.md +49 -49
- package/CHUNK_OPTIMIZER_USAGE.md +132 -0
- package/Dockerfile +3 -0
- package/QUICK_RELEASE.md +85 -0
- package/README.md +189 -189
- package/RELEASE_GUIDE.md +243 -0
- package/api-extractor.json +15 -0
- package/commitlint.config.js +3 -0
- package/jest.config.js +15 -0
- package/package.json +82 -109
- package/rollup.config.chunk-optimizer.js +32 -0
- package/rollup.config.js +73 -0
- package/src/array/index.ts +85 -0
- package/src/build/chunk-optimizer/ARCHITECTURE.md +347 -0
- package/src/build/chunk-optimizer/QUICK_START.md +370 -0
- package/src/build/chunk-optimizer/README.md +240 -0
- package/src/build/chunk-optimizer/core/chunk-generator.ts +166 -0
- package/src/build/chunk-optimizer/core/classifier.ts +148 -0
- package/src/build/chunk-optimizer/core/dependency-reader.ts +138 -0
- package/src/build/chunk-optimizer/examples/basic-usage.ts +234 -0
- package/src/build/chunk-optimizer/index.ts +166 -0
- package/src/build/chunk-optimizer/rules/common-rules.ts +131 -0
- package/src/build/chunk-optimizer/rules/framework-rules.ts +93 -0
- package/src/build/chunk-optimizer/rules/index.ts +27 -0
- package/src/build/chunk-optimizer/test.ts +94 -0
- package/src/build/chunk-optimizer/types.ts +128 -0
- package/src/color/index.ts +58 -0
- package/src/common/index.ts +353 -0
- package/src/constant/common.constant.ts +13 -0
- package/src/date/index.ts +143 -0
- package/src/dom/index.ts +198 -0
- package/src/file/index.ts +319 -0
- package/src/http/apiBuilder/README.md +648 -0
- package/src/http/apiBuilder/api-builder.ts +502 -0
- package/src/http/apiBuilder/example.ts +243 -0
- package/src/http/apiBuilder/index.ts +1 -0
- package/src/http/apiBuilder//345/277/253/351/200/237/345/217/202/350/200/203.md +199 -0
- package/src/http/http.ts +79 -0
- package/src/http/httpEnums.ts +61 -0
- package/src/iam/index.ts +46 -0
- package/src/index.ts +20 -0
- package/src/middleware/requestLogger.middware.ts +371 -0
- package/src/middleware/requestLoggerUnified.ts +371 -0
- package/src/number/index.ts +362 -0
- package/src/object/index.ts +54 -0
- package/src/print/index.ts +102 -0
- package/src/string/index.ts +189 -0
- package/src/utils/curl.ts +108 -0
- package/src/validate/index.ts +100 -0
- package/src/websocket/emitter.ts +39 -0
- package/src/websocket/index.ts +6 -0
- package/src/websocket/manager.ts +151 -0
- package/src/websocket/pinia-store.ts +91 -0
- package/src/websocket/service.ts +34 -0
- package/src/websocket/types.ts +45 -0
- package/test/common/index.test.ts +19 -0
- package/test/date/index.test.ts +107 -0
- package/test/file/index.test.ts +104 -0
- package/test/number/index.test.ts +108 -0
- package/test/object/index.test.ts +20 -0
- package/test/string/index.test.ts +82 -0
- package/tsconfig.json +39 -0
- package/typedoc.json +12 -0
- package/types/file/index.d.ts +7 -0
- package/types/index.d.ts +1 -0
- package/types/websocket/emitter.d.ts +16 -0
- package/types/websocket/index.d.ts +6 -0
- package/types/websocket/manager.d.ts +36 -0
- package/types/websocket/pinia-store.d.ts +25 -0
- package/types/websocket/service.d.ts +13 -0
- package/types/websocket/types.d.ts +34 -0
- package/dist/chunk-optimizer.cjs +0 -703
- package/dist/index.esm.js +0 -2791
- package/dist/index.esm.min.js +0 -15
- package/dist/index.umd.js +0 -2899
- package/dist/index.umd.min.js +0 -16
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Vite Chunk Optimizer
|
|
2
|
+
|
|
3
|
+
🚀 智能分包优化工具 - 自动分析项目依赖并生成最优的 Vite `manualChunks` 配置
|
|
4
|
+
|
|
5
|
+
## ✨ 特性
|
|
6
|
+
|
|
7
|
+
- 🎯 **零配置** - 开箱即用,自动检测框架和依赖
|
|
8
|
+
- 🧠 **智能分类** - 基于预定义规则库自动分类依赖
|
|
9
|
+
- 🔧 **高度可定制** - 支持自定义规则和分包策略
|
|
10
|
+
- 📊 **可视化分析** - 提供详细的依赖分析报告
|
|
11
|
+
- 🎨 **多框架支持** - 支持 Vue、React、Angular、Svelte
|
|
12
|
+
- 📦 **TypeScript** - 完整的类型定义支持
|
|
13
|
+
|
|
14
|
+
## 📦 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 本项目已内置,无需安装
|
|
18
|
+
# 如需在其他项目使用,可以复制 build/chunk-optimizer 目录
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 🚀 快速开始
|
|
22
|
+
|
|
23
|
+
### 基础用法(零配置)
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// vite.config.ts
|
|
27
|
+
import { createChunkOptimizer } from './build/chunk-optimizer';
|
|
28
|
+
|
|
29
|
+
const optimizer = createChunkOptimizer();
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
build: {
|
|
33
|
+
rollupOptions: {
|
|
34
|
+
output: {
|
|
35
|
+
manualChunks: optimizer.generate()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 高级用法(自定义配置)
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { createChunkOptimizer } from './build/chunk-optimizer';
|
|
46
|
+
|
|
47
|
+
const optimizer = createChunkOptimizer({
|
|
48
|
+
// 框架类型(默认自动检测)
|
|
49
|
+
framework: 'vue', // 'vue' | 'react' | 'angular' | 'svelte' | 'auto'
|
|
50
|
+
|
|
51
|
+
// 分包策略(未来支持)
|
|
52
|
+
strategy: 'balanced', // 'balanced' | 'aggressive' | 'conservative'
|
|
53
|
+
|
|
54
|
+
// 自定义规则(会覆盖默认规则)
|
|
55
|
+
customRules: {
|
|
56
|
+
'my-custom-lib': 'vendor-custom',
|
|
57
|
+
'@company/*': 'vendor-company',
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// 排除某些包
|
|
61
|
+
exclude: ['@types/*', 'vite'],
|
|
62
|
+
|
|
63
|
+
// 最小 chunk 大小(KB)
|
|
64
|
+
minChunkSize: 20,
|
|
65
|
+
|
|
66
|
+
// 调试模式(会打印详细信息)
|
|
67
|
+
debug: true,
|
|
68
|
+
|
|
69
|
+
// 业务代码分包策略
|
|
70
|
+
sourceCodeStrategy: {
|
|
71
|
+
views: true, // 按 views 模块分包
|
|
72
|
+
components: true, // 公共组件单独分包
|
|
73
|
+
utils: true, // 工具函数分包
|
|
74
|
+
store: true, // store 分包
|
|
75
|
+
custom: [ // 自定义业务代码规则
|
|
76
|
+
{
|
|
77
|
+
pattern: /\/src\/features\/([^/]+)\//,
|
|
78
|
+
chunkName: 'feature-$1'
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export default {
|
|
85
|
+
build: {
|
|
86
|
+
rollupOptions: {
|
|
87
|
+
output: {
|
|
88
|
+
manualChunks: optimizer.generate()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 📊 分析报告
|
|
96
|
+
|
|
97
|
+
启用调试模式查看详细的依赖分析:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const optimizer = createChunkOptimizer({ debug: true });
|
|
101
|
+
|
|
102
|
+
// 或者手动打印报告
|
|
103
|
+
optimizer.printReport();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
输出示例:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
============================================================
|
|
110
|
+
📦 Vite Chunk Optimizer - Analysis Report
|
|
111
|
+
============================================================
|
|
112
|
+
|
|
113
|
+
Framework: vue
|
|
114
|
+
Strategy: balanced
|
|
115
|
+
Total Dependencies: 45
|
|
116
|
+
|
|
117
|
+
Chunk Distribution:
|
|
118
|
+
vendor-vue 15 (33.3%)
|
|
119
|
+
vendor-utils 12 (26.7%)
|
|
120
|
+
vendor-element 8 (17.8%)
|
|
121
|
+
vendor-icons 5 (11.1%)
|
|
122
|
+
vendor-libs 5 (11.1%)
|
|
123
|
+
|
|
124
|
+
============================================================
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## 🎯 默认分包规则
|
|
128
|
+
|
|
129
|
+
### Vendor 分包
|
|
130
|
+
|
|
131
|
+
| Chunk 名称 | 包含的库 | 说明 |
|
|
132
|
+
|-----------|---------|------|
|
|
133
|
+
| `vendor-vue` | vue, pinia, @vue/* | Vue 核心框架 |
|
|
134
|
+
| `vendor-router` | vue-router | 路由库 |
|
|
135
|
+
| `vendor-element` | element-plus | Element Plus UI |
|
|
136
|
+
| `vendor-ui` | ant-design-vue, naive-ui 等 | 其他 UI 框架 |
|
|
137
|
+
| `vendor-utils` | axios, dayjs, lodash 等 | 工具库 |
|
|
138
|
+
| `vendor-icons` | @iconify/*, unplugin-icons | 图标库 |
|
|
139
|
+
| `vendor-i18n` | vue-i18n, @intlify/* | 国际化 |
|
|
140
|
+
| `vendor-vueuse` | @vueuse/* | VueUse 工具集 |
|
|
141
|
+
| `vendor-libs` | 其他第三方库 | 兜底分类 |
|
|
142
|
+
|
|
143
|
+
### 业务代码分包
|
|
144
|
+
|
|
145
|
+
| Chunk 名称 | 路径 | 说明 |
|
|
146
|
+
|-----------|------|------|
|
|
147
|
+
| `chunk-{模块名}` | /src/views/{模块}/ | 按页面模块分包 |
|
|
148
|
+
| `chunk-components` | /src/components/ | 公共组件 |
|
|
149
|
+
| `chunk-common` | /src/utils/, /src/config/ | 工具和配置 |
|
|
150
|
+
| `chunk-store` | /src/store/ | 状态管理 |
|
|
151
|
+
|
|
152
|
+
## 🔧 自定义规则
|
|
153
|
+
|
|
154
|
+
### 添加自定义分类
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const optimizer = createChunkOptimizer({
|
|
158
|
+
customRules: {
|
|
159
|
+
// 精确匹配
|
|
160
|
+
'my-lib': 'vendor-custom',
|
|
161
|
+
|
|
162
|
+
// 通配符匹配
|
|
163
|
+
'@company/*': 'vendor-company',
|
|
164
|
+
|
|
165
|
+
// 覆盖默认规则
|
|
166
|
+
'element-plus': 'vendor-ui', // 将 element-plus 归类到 vendor-ui
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 排除某些包
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const optimizer = createChunkOptimizer({
|
|
175
|
+
exclude: [
|
|
176
|
+
'@types/*', // 排除所有类型定义
|
|
177
|
+
'vite', // 排除 vite
|
|
178
|
+
'rollup', // 排除 rollup
|
|
179
|
+
]
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## 📚 API 文档
|
|
184
|
+
|
|
185
|
+
### `createChunkOptimizer(options?)`
|
|
186
|
+
|
|
187
|
+
创建 Chunk 优化器实例。
|
|
188
|
+
|
|
189
|
+
**参数:**
|
|
190
|
+
- `options` - 配置选项(可选)
|
|
191
|
+
|
|
192
|
+
**返回:**
|
|
193
|
+
- `ChunkOptimizer` 实例
|
|
194
|
+
|
|
195
|
+
### `ChunkOptimizer.generate()`
|
|
196
|
+
|
|
197
|
+
生成 `manualChunks` 函数。
|
|
198
|
+
|
|
199
|
+
**返回:**
|
|
200
|
+
- `ManualChunksFunction` - 可直接用于 Vite 配置
|
|
201
|
+
|
|
202
|
+
### `ChunkOptimizer.analyze()`
|
|
203
|
+
|
|
204
|
+
分析项目依赖。
|
|
205
|
+
|
|
206
|
+
**返回:**
|
|
207
|
+
- `AnalysisResult` - 包含依赖信息和统计数据
|
|
208
|
+
|
|
209
|
+
### `ChunkOptimizer.printReport()`
|
|
210
|
+
|
|
211
|
+
打印详细的分析报告到控制台。
|
|
212
|
+
|
|
213
|
+
## 🎨 分包策略(未来支持)
|
|
214
|
+
|
|
215
|
+
### Balanced(平衡)- 默认
|
|
216
|
+
|
|
217
|
+
- 合理的 chunk 数量
|
|
218
|
+
- 平衡首屏加载和缓存利用率
|
|
219
|
+
- 适合大多数项目
|
|
220
|
+
|
|
221
|
+
### Aggressive(激进)
|
|
222
|
+
|
|
223
|
+
- 更细粒度的分包
|
|
224
|
+
- 最大化并行加载
|
|
225
|
+
- 适合大型项目
|
|
226
|
+
|
|
227
|
+
### Conservative(保守)
|
|
228
|
+
|
|
229
|
+
- 较少的 chunk 数量
|
|
230
|
+
- 减少 HTTP 请求
|
|
231
|
+
- 适合小型项目
|
|
232
|
+
|
|
233
|
+
## 🤝 贡献
|
|
234
|
+
|
|
235
|
+
欢迎提交 Issue 和 Pull Request!
|
|
236
|
+
|
|
237
|
+
## 📄 License
|
|
238
|
+
|
|
239
|
+
MIT
|
|
240
|
+
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunk 生成器 - 生成 manualChunks 函数
|
|
3
|
+
*/
|
|
4
|
+
import type {
|
|
5
|
+
ManualChunksFunction,
|
|
6
|
+
DependencyInfo,
|
|
7
|
+
SourceCodeStrategy,
|
|
8
|
+
} from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Chunk 生成器
|
|
12
|
+
*/
|
|
13
|
+
export class ChunkGenerator {
|
|
14
|
+
private dependencyMap: Map<string, string>;
|
|
15
|
+
private sourceCodeStrategy: SourceCodeStrategy;
|
|
16
|
+
private debug: boolean;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
dependencies: DependencyInfo[],
|
|
20
|
+
sourceCodeStrategy: SourceCodeStrategy = {},
|
|
21
|
+
debug: boolean = false
|
|
22
|
+
) {
|
|
23
|
+
// 构建依赖名称到分类的映射
|
|
24
|
+
this.dependencyMap = new Map();
|
|
25
|
+
dependencies.forEach(dep => {
|
|
26
|
+
if (dep.category) {
|
|
27
|
+
this.dependencyMap.set(dep.name, dep.category);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
this.sourceCodeStrategy = {
|
|
32
|
+
views: true,
|
|
33
|
+
components: true,
|
|
34
|
+
utils: true,
|
|
35
|
+
store: true,
|
|
36
|
+
...sourceCodeStrategy,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
this.debug = debug;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 从模块 ID 中提取包名
|
|
44
|
+
*/
|
|
45
|
+
private extractPackageName(id: string): string | null {
|
|
46
|
+
const match = id.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)/);
|
|
47
|
+
return match ? match[1] : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 处理 node_modules 依赖
|
|
52
|
+
*/
|
|
53
|
+
private handleNodeModules(id: string): string | undefined {
|
|
54
|
+
const packageName = this.extractPackageName(id);
|
|
55
|
+
|
|
56
|
+
if (!packageName) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 查找依赖分类
|
|
61
|
+
const category = this.dependencyMap.get(packageName);
|
|
62
|
+
|
|
63
|
+
if (this.debug && category) {
|
|
64
|
+
console.log(`[Chunk] ${packageName} -> ${category}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return category;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 处理业务代码
|
|
72
|
+
*/
|
|
73
|
+
private handleSourceCode(id: string): string | undefined {
|
|
74
|
+
// 处理 views 目录
|
|
75
|
+
if (this.sourceCodeStrategy.views && id.includes('/src/views/')) {
|
|
76
|
+
const match = id.match(/\/src\/views\/([^/]+)\//);
|
|
77
|
+
if (match) {
|
|
78
|
+
return `chunk-${match[1]}`;
|
|
79
|
+
}
|
|
80
|
+
return 'chunk-views';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 处理 components 目录
|
|
84
|
+
if (this.sourceCodeStrategy.components && id.includes('/src/components/')) {
|
|
85
|
+
return 'chunk-components';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 处理 utils 和 config 目录
|
|
89
|
+
if (this.sourceCodeStrategy.utils) {
|
|
90
|
+
if (id.includes('/src/utils/') || id.includes('/src/config/')) {
|
|
91
|
+
return 'chunk-common';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 处理 store 目录
|
|
96
|
+
if (this.sourceCodeStrategy.store && id.includes('/src/store/')) {
|
|
97
|
+
return 'chunk-store';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 处理自定义规则
|
|
101
|
+
if (this.sourceCodeStrategy.custom) {
|
|
102
|
+
for (const rule of this.sourceCodeStrategy.custom) {
|
|
103
|
+
if (rule.pattern.test(id)) {
|
|
104
|
+
return rule.chunkName;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 生成 manualChunks 函数
|
|
114
|
+
*/
|
|
115
|
+
generate(): ManualChunksFunction {
|
|
116
|
+
return (id: string) => {
|
|
117
|
+
// 处理 node_modules 依赖
|
|
118
|
+
if (id.includes('node_modules')) {
|
|
119
|
+
return this.handleNodeModules(id);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 处理业务代码
|
|
123
|
+
return this.handleSourceCode(id);
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 获取依赖统计信息
|
|
129
|
+
*/
|
|
130
|
+
getStats(): {
|
|
131
|
+
totalDependencies: number;
|
|
132
|
+
categorizedDependencies: number;
|
|
133
|
+
categories: string[];
|
|
134
|
+
} {
|
|
135
|
+
const categories = new Set<string>();
|
|
136
|
+
this.dependencyMap.forEach(category => {
|
|
137
|
+
categories.add(category);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
totalDependencies: this.dependencyMap.size,
|
|
142
|
+
categorizedDependencies: this.dependencyMap.size,
|
|
143
|
+
categories: Array.from(categories).sort(),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 打印统计信息
|
|
149
|
+
*/
|
|
150
|
+
printStats(): void {
|
|
151
|
+
const stats = this.getStats();
|
|
152
|
+
|
|
153
|
+
console.log('\n🎯 Chunk Optimizer - Statistics\n');
|
|
154
|
+
console.log(`Total dependencies: ${stats.totalDependencies}`);
|
|
155
|
+
console.log(`Categorized: ${stats.categorizedDependencies}`);
|
|
156
|
+
console.log(`\nChunk categories (${stats.categories.length}):`);
|
|
157
|
+
stats.categories.forEach(category => {
|
|
158
|
+
const count = Array.from(this.dependencyMap.values()).filter(
|
|
159
|
+
c => c === category
|
|
160
|
+
).length;
|
|
161
|
+
console.log(` - ${category}: ${count} packages`);
|
|
162
|
+
});
|
|
163
|
+
console.log('');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 分类引擎 - 根据规则对依赖进行分类
|
|
3
|
+
*/
|
|
4
|
+
import type { Rule, DependencyInfo, DependencyCategory } from '../types';
|
|
5
|
+
import { getAllRules, sortRulesByPriority } from '../rules';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 依赖分类器
|
|
9
|
+
*/
|
|
10
|
+
export class DependencyClassifier {
|
|
11
|
+
private rules: Rule[];
|
|
12
|
+
private customRules: Record<string, DependencyCategory>;
|
|
13
|
+
private exclude: string[];
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
framework: string = 'auto',
|
|
17
|
+
customRules: Record<string, DependencyCategory> = {},
|
|
18
|
+
exclude: string[] = []
|
|
19
|
+
) {
|
|
20
|
+
// 获取并排序规则(按优先级)
|
|
21
|
+
this.rules = sortRulesByPriority(getAllRules(framework as any));
|
|
22
|
+
this.customRules = customRules;
|
|
23
|
+
this.exclude = exclude;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 检查是否应该排除某个包
|
|
28
|
+
*/
|
|
29
|
+
private shouldExclude(packageName: string): boolean {
|
|
30
|
+
return this.exclude.some(pattern => {
|
|
31
|
+
if (pattern.includes('*')) {
|
|
32
|
+
// 支持通配符匹配
|
|
33
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
34
|
+
return regex.test(packageName);
|
|
35
|
+
}
|
|
36
|
+
return packageName === pattern;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 匹配规则
|
|
42
|
+
*/
|
|
43
|
+
private matchRule(packageName: string, rule: Rule): boolean {
|
|
44
|
+
if (typeof rule.match === 'string') {
|
|
45
|
+
return packageName === rule.match;
|
|
46
|
+
} else {
|
|
47
|
+
return rule.match.test(packageName);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 对单个依赖进行分类
|
|
53
|
+
*/
|
|
54
|
+
classifyDependency(dep: DependencyInfo): DependencyCategory | null {
|
|
55
|
+
// 检查是否应该排除
|
|
56
|
+
if (this.shouldExclude(dep.name)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 优先使用自定义规则
|
|
61
|
+
if (this.customRules[dep.name]) {
|
|
62
|
+
return this.customRules[dep.name];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 检查自定义规则中的通配符
|
|
66
|
+
for (const [pattern, category] of Object.entries(this.customRules)) {
|
|
67
|
+
if (pattern.includes('*')) {
|
|
68
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
69
|
+
if (regex.test(dep.name)) {
|
|
70
|
+
return category;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 使用预定义规则
|
|
76
|
+
for (const rule of this.rules) {
|
|
77
|
+
if (this.matchRule(dep.name, rule)) {
|
|
78
|
+
return rule.category;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 默认分类
|
|
83
|
+
return 'vendor-libs';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 对所有依赖进行分类
|
|
88
|
+
*/
|
|
89
|
+
classifyAll(dependencies: DependencyInfo[]): DependencyInfo[] {
|
|
90
|
+
return dependencies.map(dep => ({
|
|
91
|
+
...dep,
|
|
92
|
+
category: this.classifyDependency(dep) || undefined,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 获取分类统计
|
|
98
|
+
*/
|
|
99
|
+
getCategoryStats(dependencies: DependencyInfo[]): Record<DependencyCategory, number> {
|
|
100
|
+
const stats: Record<string, number> = {};
|
|
101
|
+
|
|
102
|
+
dependencies.forEach(dep => {
|
|
103
|
+
if (dep.category) {
|
|
104
|
+
stats[dep.category] = (stats[dep.category] || 0) + 1;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return stats;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 打印分类信息(调试用)
|
|
113
|
+
*/
|
|
114
|
+
printClassification(dependencies: DependencyInfo[]): void {
|
|
115
|
+
console.log('\n📦 Chunk Optimizer - Dependency Classification\n');
|
|
116
|
+
|
|
117
|
+
const categorized = this.classifyAll(dependencies);
|
|
118
|
+
const stats = this.getCategoryStats(categorized);
|
|
119
|
+
|
|
120
|
+
// 按分类分组
|
|
121
|
+
const grouped: Record<string, string[]> = {};
|
|
122
|
+
categorized.forEach(dep => {
|
|
123
|
+
if (dep.category) {
|
|
124
|
+
if (!grouped[dep.category]) {
|
|
125
|
+
grouped[dep.category] = [];
|
|
126
|
+
}
|
|
127
|
+
grouped[dep.category].push(dep.name);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// 打印每个分类
|
|
132
|
+
Object.entries(grouped)
|
|
133
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
134
|
+
.forEach(([category, packages]) => {
|
|
135
|
+
console.log(`\n${category} (${packages.length}):`);
|
|
136
|
+
packages.forEach(pkg => {
|
|
137
|
+
console.log(` - ${pkg}`);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
console.log('\n📊 Summary:');
|
|
142
|
+
console.log(` Total dependencies: ${dependencies.length}`);
|
|
143
|
+
console.log(` Categorized: ${categorized.filter(d => d.category).length}`);
|
|
144
|
+
console.log(` Categories: ${Object.keys(stats).length}`);
|
|
145
|
+
console.log('');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 依赖读取器 - 读取和解析 package.json
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from 'fs';
|
|
5
|
+
import { resolve } from 'path';
|
|
6
|
+
import type { DependencyInfo, Framework } from '../types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Package.json 结构
|
|
10
|
+
*/
|
|
11
|
+
interface PackageJson {
|
|
12
|
+
dependencies?: Record<string, string>;
|
|
13
|
+
devDependencies?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 依赖读取器类
|
|
18
|
+
*/
|
|
19
|
+
export class DependencyReader {
|
|
20
|
+
private packageJsonPath: string;
|
|
21
|
+
private packageJson: PackageJson | null = null;
|
|
22
|
+
|
|
23
|
+
constructor(packageJsonPath?: string) {
|
|
24
|
+
// 默认使用项目根目录的 package.json
|
|
25
|
+
this.packageJsonPath = packageJsonPath || resolve(process.cwd(), 'package.json');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 读取 package.json
|
|
30
|
+
*/
|
|
31
|
+
private readPackageJson(): PackageJson {
|
|
32
|
+
if (this.packageJson) {
|
|
33
|
+
return this.packageJson;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!existsSync(this.packageJsonPath)) {
|
|
37
|
+
throw new Error(`package.json not found at: ${this.packageJsonPath}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const content = readFileSync(this.packageJsonPath, 'utf-8');
|
|
42
|
+
this.packageJson = JSON.parse(content);
|
|
43
|
+
return this.packageJson!;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new Error(`Failed to parse package.json: ${error}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 获取所有依赖信息
|
|
51
|
+
*/
|
|
52
|
+
getDependencies(): DependencyInfo[] {
|
|
53
|
+
const pkg = this.readPackageJson();
|
|
54
|
+
const dependencies: DependencyInfo[] = [];
|
|
55
|
+
|
|
56
|
+
// 读取生产依赖
|
|
57
|
+
if (pkg.dependencies) {
|
|
58
|
+
Object.entries(pkg.dependencies).forEach(([name, version]) => {
|
|
59
|
+
dependencies.push({
|
|
60
|
+
name,
|
|
61
|
+
version,
|
|
62
|
+
isDev: false,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 读取开发依赖
|
|
68
|
+
if (pkg.devDependencies) {
|
|
69
|
+
Object.entries(pkg.devDependencies).forEach(([name, version]) => {
|
|
70
|
+
dependencies.push({
|
|
71
|
+
name,
|
|
72
|
+
version,
|
|
73
|
+
isDev: true,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return dependencies;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 检测项目使用的框架
|
|
83
|
+
*/
|
|
84
|
+
detectFramework(): Framework {
|
|
85
|
+
const pkg = this.readPackageJson();
|
|
86
|
+
const allDeps = {
|
|
87
|
+
...pkg.dependencies,
|
|
88
|
+
...pkg.devDependencies,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// 检测 Vue
|
|
92
|
+
if (allDeps['vue']) {
|
|
93
|
+
return 'vue';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 检测 React
|
|
97
|
+
if (allDeps['react']) {
|
|
98
|
+
return 'react';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 检测 Angular
|
|
102
|
+
if (allDeps['@angular/core']) {
|
|
103
|
+
return 'angular';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 检测 Svelte
|
|
107
|
+
if (allDeps['svelte']) {
|
|
108
|
+
return 'svelte';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 默认返回 auto
|
|
112
|
+
return 'auto';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 检查是否存在某个依赖
|
|
117
|
+
*/
|
|
118
|
+
hasDependency(name: string): boolean {
|
|
119
|
+
const pkg = this.readPackageJson();
|
|
120
|
+
return !!(
|
|
121
|
+
pkg.dependencies?.[name] ||
|
|
122
|
+
pkg.devDependencies?.[name]
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 获取依赖版本
|
|
128
|
+
*/
|
|
129
|
+
getDependencyVersion(name: string): string | null {
|
|
130
|
+
const pkg = this.readPackageJson();
|
|
131
|
+
return (
|
|
132
|
+
pkg.dependencies?.[name] ||
|
|
133
|
+
pkg.devDependencies?.[name] ||
|
|
134
|
+
null
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|