vite-plugin-assets-manager 0.0.1

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/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # vite-plugin-assets-manager
2
+
3
+ 一个 Vite 插件,通过虚拟模块 `virtual:assets` 管理图片资源,并在构建时支持压缩、打包 ZIP、上传。
4
+
5
+ ## 1. 当前实现能力
6
+
7
+ - 开发环境:`virtual:assets` 导出本地路径(以 `/` 开头)
8
+ - 生产构建:`virtual:assets` 导出 CDN URL(固定规则:`defaultDomain/activity/cdnPrefix/文件名`)
9
+ - 支持具名导出和默认导出对象
10
+ - 自动扫描图片目录并生成导出名(驼峰)
11
+ - 导出名冲突自动处理并告警
12
+ - 构建前读取 Git 变更文件(已暂存 + 未暂存 + 未跟踪)
13
+ - 构建时压缩图片并打包为单个 ZIP 上传
14
+ - 上传结果输出彩色日志与表格
15
+ - 在缓存目录生成 HTML 预览报告
16
+ - 开发环境监听图片变更并触发全量刷新
17
+
18
+ ## 2. 安装与使用
19
+
20
+ ```bash
21
+ npm i -D vite-plugin-assets-manager
22
+ ```
23
+
24
+ ```ts
25
+ // vite.config.ts
26
+ import vitePluginAssets from 'vite-plugin-assets-manager'
27
+
28
+ export default {
29
+ plugins: [
30
+ vitePluginAssets({
31
+ defaultDomain: 'https://cdn.example.com',
32
+ }),
33
+ ],
34
+ }
35
+ ```
36
+
37
+ ```ts
38
+ // 任意业务代码
39
+ import { logo } from 'virtual:assets'
40
+ import assets from 'virtual:assets'
41
+
42
+ console.log(logo)
43
+ console.log(assets.logo)
44
+ ```
45
+
46
+ ## 3. 配置参数(完整)
47
+
48
+ `vitePluginAssets(options)` 的参数定义来源于 [src/index.ts](src/index.ts) 与 [types/index.d.ts](types/index.d.ts)。
49
+
50
+ | 参数 | 类型 | 必填 | 默认值 | 用途 |
51
+ |---|---|---|---|---|
52
+ | `defaultDomain` | `string` | 是 | - | 生产构建时 CDN 域名前缀 |
53
+ | `cdnPrefix` | `string` | 否 | `vite 项目根目录名` | CDN 路径前缀 |
54
+ | `imageDir` | `string[]` | 否 | `['src/assets']` | 扫描/监听/过滤图片目录 |
55
+ | `extensions` | `string[]` | 否 | `['png','jpg','jpeg','gif','webp','svg','avif']` | 允许处理的扩展名(不含点) |
56
+ | `compressOptions` | `CompressOptions` | 否 | 见下表 | 构建压缩参数 |
57
+ | `uploadOptions` | `UploadOptions` | 否 | - | 构建上传参数;未配置 `uploadUrl` 时不做压缩/上传 |
58
+ | `cleanCache` | `boolean` | 否 | `true` | 构建开始时是否清理缓存目录 |
59
+
60
+ `CompressOptions`:
61
+
62
+ | 参数 | 类型 | 默认值 | 用途 |
63
+ |---|---|---|---|
64
+ | `quality` | `number` | `80` | JPEG/WebP/AVIF 质量 |
65
+ | `progressive` | `boolean` | `true` | JPEG 渐进式 |
66
+ | `mozjpeg` | `boolean` | `true` | JPEG 使用 mozjpeg |
67
+
68
+ `UploadOptions`:
69
+
70
+ | 参数 | 类型 | 默认值 | 用途 |
71
+ |---|---|---|---|
72
+ | `uploadUrl` | `string` | - | 上传接口地址 |
73
+ | `skipUpload` | `boolean` | `false` | 跳过真实上传 |
74
+ | `headers` | `Record<string,string>` | `{}` | 自定义请求头 |
75
+
76
+ ## 4. 虚拟模块行为
77
+
78
+ ### 4.1 导出名规则
79
+
80
+ 由 [src/scan.ts](src/scan.ts) 的 `pathToExportName()` 处理:
81
+
82
+ - 去扩展名
83
+ - 按 `/` 和 `-` 拆分
84
+ - 组装为驼峰
85
+
86
+ 示例:
87
+
88
+ - `logo.png` -> `logo`
89
+ - `icons/arrow-up.svg` -> `iconsArrowUp`
90
+ - `backgrounds/dark-mode.svg` -> `backgroundsDarkMode`
91
+
92
+ 如果导出名冲突,`scanImages()` 会追加父目录前缀并打印警告。
93
+
94
+ ### 4.2 开发环境导出
95
+
96
+ 由 [src/virtual.ts](src/virtual.ts) 的 `generateDevCode()` 生成:
97
+
98
+ - 具名导出值:`/相对项目根目录路径`
99
+ - 默认导出:所有资源键值对象
100
+
101
+ 示例:
102
+
103
+ ```ts
104
+ export const viteLogo = '/src/assets/vite-logo.svg'*
105
+ export default { viteLogo: '/src/assets/vite-logo.svg' }
106
+ ```
107
+
108
+ ### 4.3 生产构建导出
109
+
110
+ 由 [src/virtual.ts](src/virtual.ts) 的 `generateProdCode()` 生成:
111
+ *
112
+ - URL 规则:`defaultDomain + '/activity/' + cdnPrefix + '/' + 文件名`
113
+ - 仅使用文件名(`path.basename`),不保留原始子目录
114
+
115
+ 示例:
116
+
117
+ ```ts
118
+ export const iconsArrowUp = 'https://cdn.example.com/activity/playground/arrow-up.svg'
119
+ ```
120
+
121
+ ## 5. 构建时压缩与上传流程
122
+
123
+ 实现位于 [src/index.ts](src/index.ts)、[src/git.ts](src/git.ts)、[src/compress.ts](src/compress.ts)、[src/upload.ts](src/upload.ts)。
124
+
125
+ 1. `buildStart` 触发(仅当配置了 `uploadOptions.uploadUrl`)
126
+ 2. `getModifiedFiles(root)` 读取 Git 变更(三类,均排除已删除文件):
127
+ - `git diff --cached --name-only --diff-filter=ACMRT`(已暂存未提交)
128
+ - `git diff --name-only --diff-filter=ACMRT`(未暂存的修改)
129
+ - `git ls-files --others --exclude-standard --full-name`(未跟踪的新文件)
130
+ 3. `filterModifiedImages()` 过滤出:
131
+ - 位于 `imageDir`
132
+ - 扩展名在 `extensions`
133
+ - 文件实际存在(已删除文件会跳过)
134
+ 4. `compressImages()` 压缩到缓存目录
135
+ 5. `zipImages()` 打包为 `${cdnPrefix}.zip`
136
+ 6. `uploadZipFile()` 上传 ZIP,并打印成功/失败表格
137
+ 7. 若存在失败文件,抛错中断构建
138
+
139
+ ### 5.1 上传请求格式
140
+
141
+ 当前 ZIP 上传实际发送字段(见 [src/upload.ts](src/upload.ts)):
142
+
143
+ - Method: `POST`
144
+ - Body: `multipart/form-data`
145
+ - Field: `files`(ZIP 文件)
146
+ - Headers: `uploadOptions.headers`
147
+
148
+ ### 5.2 上传响应处理
149
+
150
+ `uploadZipFile()` 期望响应里有 `data.urls: string[]`,通过 URL 文件名判断成功项。
151
+
152
+ 特殊处理:
153
+
154
+ - 若 `message` 包含 `file exists`,按警告处理并继续,不抛错
155
+
156
+ ### 5.3 压缩策略
157
+
158
+ `compressImage()` 按扩展名处理:
159
+
160
+ - `jpg/jpeg`: JPEG 压缩
161
+ - `png`: PNG 压缩
162
+ - `webp`: WebP 压缩
163
+ - `avif`: AVIF 压缩
164
+ - `svg/gif`: 直接复制
165
+ - 其他格式:直接复制
166
+
167
+ ## 6. Vite Hook 与方法用途
168
+
169
+ 对应 [src/index.ts](src/index.ts):
170
+
171
+ | Hook/方法 | 用途 |
172
+ |---|---|
173
+ | `configResolved` | 保存 `resolvedConfig`,确定 `cdnPrefix` |
174
+ | `buildStart` | 构建前执行变更扫描、压缩、打包、上传 |
175
+ | `buildEnd` | 当前不做清理(保留最新缓存) |
176
+ | `resolveId` | 处理 `virtual:assets`;并拦截 `imageDir` 里的图片直接导入 |
177
+ | `load` | 生成虚拟模块代码;处理拦截资源为 `export default ""` |
178
+ | `configureServer` | 监听图片目录变更,更新类型并触发 full-reload |
179
+
180
+ ## 7. 缓存与产物
181
+
182
+ 缓存目录:`node_modules/vite-plugin-assets-manager/.cache`
183
+
184
+ - 构建开始时:若 `cleanCache=true`,先清理旧缓存
185
+ - 构建结束后:保留缓存
186
+ - 上传阶段会生成 `vite-plugin-assets-preview-时间戳.html`
187
+
188
+ ## 8. TypeScript 类型
189
+
190
+ 类型入口:
191
+
192
+ - [types/index.d.ts](types/index.d.ts)
193
+ - [types/virtual-assets.d.ts](types/virtual-assets.d.ts)
194
+
195
+ 在业务项目中加入:
196
+
197
+ ```ts
198
+ /// <reference types="vite/client" />
199
+ /// <reference types="vite-plugin-assets/types/virtual-assets" />
200
+ ```
201
+
202
+ 说明:插件运行时会写入插件包的 `types/virtual-assets.d.ts`(见 [src/types.ts](src/types.ts))。
203
+
204
+ ## 9. 已知实现细节
205
+
206
+ - 生产 URL 使用文件名,不保留目录层级
207
+ - ZIP 打包使用文件名作为条目名,不保留目录层级
208
+ - 同名文件来自不同目录时,可能发生覆盖风险
209
+ - `resolveId` 对扩展名匹配大小写敏感(`id.endsWith('.ext')`)
210
+
211
+ ## 10. 测试
212
+
213
+ 可用脚本(见 [package.json](package.json)):
214
+
215
+ ```bash
216
+ npm run build
217
+ npm run test
218
+ npm run test:upload
219
+ npm run test:build
220
+ ```
221
+
222
+ 测试文档见 [test/README.md](test/README.md),示例项目见 [playground/README.md](playground/README.md)。
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "vite-plugin-assets-manager",
3
+ "version": "0.0.1",
4
+ "description": "A Vite plugin for managing image assets via a virtual module",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./types/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "types"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "tsc --watch",
21
+ "typecheck": "tsc --noEmit",
22
+ "test": "node test/feature.mjs",
23
+ "test:upload": "node test/upload-server.mjs",
24
+ "test:build": "node test/build-guide.mjs"
25
+ },
26
+ "dependencies": {
27
+ "jszip": "^3.10.1",
28
+ "sharp": "^0.32.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/archiver": "^7.0.0",
32
+ "@types/node": "^25.6.2",
33
+ "typescript": "^5.4.0",
34
+ "vite": "^5.0.0"
35
+ },
36
+ "peerDependencies": {
37
+ "vite": ">=4.0.0"
38
+ },
39
+ "keywords": [
40
+ "vite",
41
+ "plugin",
42
+ "assets",
43
+ "cdn",
44
+ "virtual-module"
45
+ ],
46
+ "license": "MIT"
47
+ }
@@ -0,0 +1,99 @@
1
+ import type { Plugin } from 'vite'
2
+
3
+ export interface CompressOptions {
4
+ /**
5
+ * JPEG/WebP 图片质量 (0-100)
6
+ * @default 80
7
+ */
8
+ quality?: number
9
+
10
+ /**
11
+ * 是否生成渐进式 JPEG
12
+ * @default true
13
+ */
14
+ progressive?: boolean
15
+
16
+ /**
17
+ * 是否使用 mozjpeg 编码(更好的压缩率)
18
+ * @default true
19
+ */
20
+ mozjpeg?: boolean
21
+ }
22
+
23
+ export interface UploadOptions {
24
+ /**
25
+ * 上传接口的完整 URL
26
+ * 接口应接收 multipart/form-data 格式的请求
27
+ * 必需字段:
28
+ * - file: 图片文件(二进制)
29
+ * - path: 图片相对 imageDir 的相对路径,如 'logo.png' 或 'icons/arrow.svg'
30
+ */
31
+ uploadUrl: string
32
+
33
+ /**
34
+ * 是否跳过上传(仅用于测试/开发)
35
+ * @default false
36
+ */
37
+ skipUpload?: boolean
38
+
39
+ /**
40
+ * 自定义 Headers(如认证 token)
41
+ */
42
+ headers?: Record<string, string>
43
+ }
44
+
45
+ export interface VitePluginAssetsOptions {
46
+ /**
47
+ * 生产构建时使用的默认 CDN 域名(必填)
48
+ * 项目可在运行时通过自己的替换函数覆盖此域名
49
+ */
50
+ defaultDomain: string
51
+
52
+ /**
53
+ * CDN 上的存储路径前缀
54
+ * 默认为 vite 项目根目录的 basename(如 'yallapay')
55
+ */
56
+ cdnPrefix?: string
57
+
58
+ /**
59
+ * 需要扫描的图片目录
60
+ * 支持两种格式:
61
+ * 1. 相对路径:相对于项目根目录,如 'src/assets'
62
+ * 2. 绝对路径:完整的系统路径,如 '/d/project/my-app/src/assets'
63
+ *
64
+ * 只有在配置的目录下(包括子目录)的文件才会被处理
65
+ * 目录外的文件会被忽略
66
+ *
67
+ * 默认 ['src/assets']
68
+ */
69
+ imageDir?: string[]
70
+
71
+ /**
72
+ * 需要处理的图片扩展名
73
+ * 默认 ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'avif']
74
+ */
75
+ extensions?: string[]
76
+
77
+ /**
78
+ * 图片压缩选项(仅在 build 时生效)
79
+ * 若不提供 uploadUrl,则压缩功能被跳过
80
+ */
81
+ compressOptions?: CompressOptions
82
+
83
+ /**
84
+ * CDN 上传选项(仅在 build 时生效)
85
+ * 若不提供,则跳过压缩和上传
86
+ */
87
+ uploadOptions?: UploadOptions
88
+
89
+ /**
90
+ * 构建完成后是否删除缓存目录
91
+ * 缓存目录位置:node_modules/.vite-plugin-assets-tmp
92
+ * @default true
93
+ */
94
+ cleanCache?: boolean
95
+ }
96
+
97
+ declare function vitePluginAssets(options: VitePluginAssetsOptions): Plugin
98
+
99
+ export default vitePluginAssets
@@ -0,0 +1,28 @@
1
+ // This file is auto-generated by vite-plugin-assets. Do not edit manually.
2
+ declare module 'virtual:assets' {
3
+ export const backgroundsDarkMode: string
4
+ export const iconsAlertIcon: string
5
+ export const iconsArrowUp: string
6
+ export const iconsCheckMark: string
7
+ export const newTest: string
8
+ export const openBtnDis: string
9
+ export const openBtnNew: string
10
+ export const openBtn: string
11
+ export const testFile: string
12
+ export const viteLogo: string
13
+
14
+ const assets: {
15
+ backgroundsDarkMode: string
16
+ iconsAlertIcon: string
17
+ iconsArrowUp: string
18
+ iconsCheckMark: string
19
+ newTest: string
20
+ openBtnDis: string
21
+ openBtnNew: string
22
+ openBtn: string
23
+ testFile: string
24
+ viteLogo: string
25
+ }
26
+
27
+ export default assets
28
+ }