@meng-xi/vite-plugin 0.0.1 → 0.0.2

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-en.md ADDED
@@ -0,0 +1,150 @@
1
+ **English** | [中文](./README.md)
2
+
3
+ <div align="center">
4
+ <a href="https://github.com/MengXi-Studio/vite-plugin">
5
+ <img alt="梦曦工作室 Logo" width="215" src="https://github.com/MengXi-Studio/vite-plugin/blob/master/packages/docs/src/public/logo.svg">
6
+ </a>
7
+ <br>
8
+ <h1>@meng-xi/vite-plugin</h1>
9
+
10
+ [![license](https://img.shields.io/github/license/MengXi-Studio/vite-plugin.svg)](LICENSE) [![npm](https://img.shields.io/npm/v/@meng-xi/vite-plugin?color=blue)](https://www.npmjs.com/package/@meng-xi/vite-plugin)
11
+ ![npm](https://img.shields.io/npm/dt/@meng-xi/vite-plugin?color=green)
12
+
13
+ </div>
14
+
15
+ > - This is a toolkit that provides practical plugins for Vite, helping developers simplify the build process and improve development efficiency.
16
+ > - Extends Vite build process functionality, providing automated processing solutions for common build tasks.
17
+ > - All plugins support detailed configuration options, allowing customization based on project needs to meet different usage scenarios.
18
+ > - Plugins provide error handling mechanisms to ensure build processes can catch errors, improving build reliability.
19
+ > - Adopts modular design, plugins can be used individually or in combination, flexibly responding to different project needs.
20
+
21
+ ---
22
+
23
+ Start reading the [documentation](https://mengxi-studio.github.io/vite-plugin/).
24
+
25
+ ## Quick Start
26
+
27
+ ### Installation
28
+
29
+ Install `@meng-xi/vite-plugin` using a package manager:
30
+
31
+ ```bash
32
+ # Using npm
33
+ npm install @meng-xi/vite-plugin --save-dev
34
+
35
+ # Using yarn
36
+ yarn add @meng-xi/vite-plugin --save-dev
37
+
38
+ # Using pnpm
39
+ pnpm add @meng-xi/vite-plugin --save-dev
40
+ ```
41
+
42
+ ### Basic Usage
43
+
44
+ Import and use the plugins in your Vite configuration file:
45
+
46
+ ```typescript
47
+ import { defineConfig } from 'vite'
48
+ import { copyFile, injectIco } from '@meng-xi/vite-plugin'
49
+
50
+ export default defineConfig({
51
+ plugins: [
52
+ // Copy file plugin
53
+ copyFile({
54
+ sourceDir: 'src/assets',
55
+ targetDir: 'dist/assets'
56
+ }),
57
+
58
+ // Inject icon plugin
59
+ injectIco({
60
+ base: '/assets'
61
+ })
62
+ ]
63
+ })
64
+ ```
65
+
66
+ ### Import All
67
+
68
+ You can also use default import to import all plugins:
69
+
70
+ ```typescript
71
+ import { defineConfig } from 'vite'
72
+ import vitePlugin from '@meng-xi/vite-plugin'
73
+
74
+ export default defineConfig({
75
+ plugins: [
76
+ // Copy file plugin
77
+ vitePlugin.copyFile({
78
+ sourceDir: 'src/assets',
79
+ targetDir: 'dist/assets'
80
+ }),
81
+
82
+ // Inject icon plugin
83
+ vitePlugin.injectIco({
84
+ base: '/assets'
85
+ })
86
+ ]
87
+ })
88
+ ```
89
+
90
+ ## Plugin Details
91
+
92
+ ### copyFile Plugin
93
+
94
+ Used to copy files or directories to specified locations after Vite build is completed.
95
+
96
+ **Configuration Options**:
97
+
98
+ - `sourceDir`: Source directory path (required)
99
+ - `targetDir`: Target directory path (required)
100
+ - `overwrite`: Whether to overwrite existing files, default is `true`
101
+ - `recursive`: Whether to recursively copy subdirectories, default is `true`
102
+ - `verbose`: Whether to output detailed logs, default is `true`
103
+ - `enabled`: Whether to enable the plugin, default is `true`
104
+
105
+ ### injectIco Plugin
106
+
107
+ Used to inject website icon links into the head of HTML files during the Vite build process.
108
+
109
+ **Configuration Options**:
110
+
111
+ - `base`: Base path for icon files
112
+ - `url`: Complete URL for the icon
113
+ - `link`: Custom complete link tag HTML
114
+ - `icons`: Custom icon array
115
+ - `verbose`: Whether to output detailed logs, default is `true`
116
+ - `enabled`: Whether to enable the plugin, default is `true`
117
+ - `copyOptions`: Icon file copying configuration
118
+
119
+ ## Contribution
120
+
121
+ Welcome to contribute to `@meng-xi/vite-plugin`. Here are the steps to contribute code:
122
+
123
+ 1. Fork the project: Fork this project on GitHub.
124
+ 2. Clone the code: Clone the forked project to your local machine.
125
+
126
+ ```bash
127
+ git clone https://github.com/your-username/vite-plugin.git
128
+ cd vite-plugin
129
+ ```
130
+
131
+ 3. Create a new branch: Create a new feature branch based on the `master` branch.
132
+
133
+ ```bash
134
+ git checkout -b feature/your-feature
135
+ ```
136
+
137
+ 4. Commit changes: Ensure your code passes tests and commit your changes with clear commit messages.
138
+
139
+ ```bash
140
+ git add .
141
+ git commit -m "feat: add your feature description"
142
+ ```
143
+
144
+ 5. Push changes: Push your local branch to GitHub.
145
+
146
+ ```bash
147
+ git push origin feature/your-feature
148
+ ```
149
+
150
+ 6. Create a PR: Create a Pull Request on GitHub and wait for review.
package/README.md CHANGED
@@ -1,18 +1,37 @@
1
- # @meng-xi/vite-plugin [![npm](https://img.shields.io/npm/v/@meng-xi/vite-plugin?color=blue)](https://www.npmjs.com/package/@meng-xi/vite-plugin) ![GitHub](https://img.shields.io/github/license/MengXi-Studio/vite-plugin?color=orange)
1
+ **中文** | [English](./README-en.md)
2
2
 
3
- > - 这是一个为 Vite 提供实用插件的工具包,帮助开发者简化构建流程,提高开发效率。
4
- > - 扩展 Vite 构建流程的功能,提供常见构建任务的自动化处理方案。
5
- > - 所有插件都支持详细的配置选项,可根据项目需求自定义行为,满足不同场景的使用需求。
6
- > - 插件提供错误处理机制,确保构建流程能捕获到错误,提高构建的可靠性。
7
- > - 采用模块化设计,插件可以单独使用,也可以组合使用,灵活应对不同项目的需求。
3
+ <div align="center">
4
+ <a href="https://github.com/MengXi-Studio/vite-plugin">
5
+ <img alt="梦曦工作室 Logo" width="215" src="https://github.com/MengXi-Studio/vite-plugin/blob/master/packages/docs/src/public/logo.svg">
6
+ </a>
7
+ <br>
8
+ <h1>@meng-xi/vite-plugin</h1>
8
9
 
9
- ---
10
+ [![license](https://img.shields.io/github/license/MengXi-Studio/vite-plugin.svg)](LICENSE) [![npm](https://img.shields.io/npm/v/@meng-xi/vite-plugin?color=blue)](https://www.npmjs.com/package/@meng-xi/vite-plugin)
11
+ ![npm](https://img.shields.io/npm/dt/@meng-xi/vite-plugin?color=green)
10
12
 
11
- 开始阅读[文档](https://mengxi-studio.github.io/vite-plugin/)。
13
+ </div>
12
14
 
13
- ## 快速入门
15
+ ## 简介
14
16
 
15
- ### 安装
17
+ `@meng-xi/vite-plugin` 是一个为 Vite 提供实用插件的工具包,帮助开发者简化构建流程,提高开发效率。
18
+
19
+ ## 特性
20
+
21
+ - **增强 Vite 构建流程**:提供实用插件集合,扩展 Vite 功能,简化构建过程中的常见任务,提高开发效率
22
+ - **模块化插件架构**:采用模块化设计,插件可单独使用或组合使用,灵活应对不同项目需求
23
+ - **高度可配置**:所有功能支持详细配置选项,可根据项目需求自定义行为,满足多样化场景
24
+ - **环境感知能力**:支持根据构建环境条件执行插件功能,智能控制不同环境下的行为
25
+ - **详细日志输出**:提供可选的详细日志,帮助开发者了解执行过程,便于调试和问题排查
26
+ - **类型安全**:完全使用 TypeScript 开发,提供完整类型定义,确保使用过程中的类型安全
27
+ - **无缝集成**:与 Vite 构建流程无缝集成,无需复杂配置即可快速启用
28
+ - **优化开发体验**:简化常见构建任务,减少手动操作,让开发者专注于核心业务逻辑
29
+
30
+ ## 文档
31
+
32
+ 开始阅读[文档地址](https://mengxi-studio.github.io/vite-plugin/)。
33
+
34
+ ## 安装
16
35
 
17
36
  使用包管理器安装 `@meng-xi/vite-plugin`:
18
37
 
@@ -27,84 +46,11 @@ yarn add @meng-xi/vite-plugin --save-dev
27
46
  pnpm add @meng-xi/vite-plugin --save-dev
28
47
  ```
29
48
 
30
- ### 基本使用
31
-
32
- 在 Vite 配置文件中引入并使用插件:
33
-
34
- ```typescript
35
- import { defineConfig } from 'vite'
36
- import { copyFile, injectIco } from '@meng-xi/vite-plugin'
37
-
38
- export default defineConfig({
39
- plugins: [
40
- // 复制文件插件
41
- copyFile({
42
- sourceDir: 'src/assets',
43
- targetDir: 'dist/assets'
44
- }),
45
-
46
- // 注入图标插件
47
- injectIco({
48
- base: '/assets'
49
- })
50
- ]
51
- })
52
- ```
53
-
54
- ### 全部引入
55
-
56
- 也可以使用默认导入的方式全部引入:
57
-
58
- ```typescript
59
- import { defineConfig } from 'vite'
60
- import vitePlugin from '@meng-xi/vite-plugin'
61
-
62
- export default defineConfig({
63
- plugins: [
64
- // 复制文件插件
65
- vitePlugin.copyFile({
66
- sourceDir: 'src/assets',
67
- targetDir: 'dist/assets'
68
- }),
69
-
70
- // 注入图标插件
71
- vitePlugin.injectIco({
72
- base: '/assets'
73
- })
74
- ]
75
- })
76
- ```
77
-
78
- ## 插件详情
79
-
80
- ### copyFile 插件
81
-
82
- 用于在 Vite 构建完成后复制文件或目录到指定位置。
83
-
84
- **配置选项**:
85
-
86
- - `sourceDir`:源目录路径(必填)
87
- - `targetDir`:目标目录路径(必填)
88
- - `overwrite`:是否覆盖已存在的文件,默认为 `true`
89
- - `recursive`:是否递归复制子目录,默认为 `true`
90
- - `verbose`:是否输出详细日志,默认为 `true`
91
- - `enabled`:是否启用插件,默认为 `true`
92
-
93
- ### injectIco 插件
94
-
95
- 用于在 Vite 构建过程中注入网站图标链接到 HTML 文件的头部。
96
-
97
- **配置选项**:
49
+ ## 更新日志
98
50
 
99
- - `base`:图标文件的基础路径
100
- - `url`:图标的完整 URL
101
- - `link`:自定义的完整 link 标签 HTML
102
- - `icons`:自定义图标数组
103
- - `verbose`:是否输出详细日志,默认为 `true`
104
- - `enabled`:是否启用插件,默认为 `true`
105
- - `copyOptions`:图标文件复制配置
51
+ [CHANGELOG](https://github.com/MengXi-Studio/vite-plugin/releases)
106
52
 
107
- ## 贡献
53
+ ## 如何贡献
108
54
 
109
55
  欢迎为 `@meng-xi/vite-plugin` 贡献代码。以下是贡献代码的步骤:
110
56
 
package/dist/index.cjs CHANGED
@@ -1,3 +1,5 @@
1
- "use strict";const i=require("fs");function _interopDefaultCompat(u){return u&&typeof u=="object"&&"default"in u?u.default:u}const i__default=_interopDefaultCompat(i);async function checkSourceExists(u){try{await i__default.promises.access(u,i__default.constants.F_OK)}catch(r){const e=r;throw e.code==="ENOENT"?new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728 - ${u}`):e.code==="EACCES"?new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u6E90\u6587\u4EF6 - ${u}`):new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u68C0\u67E5\u6E90\u6587\u4EF6\u65F6\u51FA\u9519 - ${u}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function ensureTargetDir(u){try{await i__default.promises.mkdir(u,{recursive:!0})}catch(r){const e=r;throw e.code==="EACCES"?new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u521B\u5EFA\u76EE\u6807\u76EE\u5F55 - ${u}`):new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u521B\u5EFA\u76EE\u6807\u76EE\u5F55\u65F6\u51FA\u9519 - ${u}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function copySourceToTarget(u,r,e){try{await i__default.promises.cp(u,r,{recursive:e.recursive,force:e.overwrite})}catch(o){const F=o;throw F.code==="ENOENT"?new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u590D\u5236\u8FC7\u7A0B\u4E2D\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728 - ${u}`):F.code==="EACCES"?new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u590D\u5236\u8FC7\u7A0B\u4E2D\u6743\u9650\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u8BBF\u95EE\u6E90\u6587\u4EF6\u6216\u5199\u5165\u76EE\u6807\u76EE\u5F55 - ${u} -> ${r}`):F.code==="EPERM"?new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u590D\u5236\u8FC7\u7A0B\u4E2D\u64CD\u4F5C\u88AB\u62D2\u7EDD - ${u} -> ${r}`):new Error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u590D\u5236\u6587\u4EF6\u65F6\u51FA\u9519 - ${u} -> ${r}\uFF0C\u9519\u8BEF\uFF1A${F.message}`)}}function generateIconTags(u){const r=[];if(u.link)return r.push(u.link),r;if(u.icons&&u.icons.length>0)r.push(...u.icons.map(e=>{let o=`<link rel="${e.rel}" href="${e.href}"`;return e.sizes&&(o+=` sizes="${e.sizes}"`),e.type&&(o+=` type="${e.type}"`),o+=" />",o}));else if(u.url)r.push(`<link rel="icon" href="${u.url}" />`);else{const e=u.base||"/",o=e.endsWith("/")?`${e}favicon.ico`:`${e}/favicon.ico`;r.push(`<link rel="icon" href="${o}" />`)}return r}function copyFile(u){const{sourceDir:r,targetDir:e,overwrite:o=!0,recursive:F=!0,verbose:n=!0,enabled:t=!0}=u;return{name:"copy-file",enforce:"post",async writeBundle(){if(!t){n&&console.log(`\u2139 \u590D\u5236\u6587\u4EF6\u529F\u80FD\u5DF2\u7981\u7528\uFF0C\u8DF3\u8FC7\u6267\u884C\uFF1A\u4ECE ${r} \u5230 ${e}`);return}try{await checkSourceExists(r),await ensureTargetDir(e),await copySourceToTarget(r,e,{recursive:F,overwrite:o}),n&&console.log(`\u2705 \u590D\u5236\u6587\u4EF6\u6210\u529F\uFF1A\u4ECE ${r} \u5230 ${e}`)}catch(c){throw n&&(c instanceof Error?console.error(c.message):console.error(`\u274C \u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u672A\u77E5\u9519\u8BEF - ${r} -> ${e}`,c)),c}}}}function injectIco(u){const r=typeof u=="string"?{base:u}:u||{},{verbose:e=!0,enabled:o=!0}=r;return{name:"inject-ico",transformIndexHtml(F){if(!o)return e&&console.log("\u2139 inject-ico: \u63D2\u4EF6\u5DF2\u7981\u7528\uFF0C\u8DF3\u8FC7\u56FE\u6807\u6CE8\u5165"),F;const n=generateIconTags(r);if(n.length===0)return e&&console.log("\u2139 inject-ico: \u6CA1\u6709\u751F\u6210\u56FE\u6807\u6807\u7B7E\uFF0C\u8DF3\u8FC7\u6CE8\u5165"),F;let t=F;const c=t.indexOf("</head>");if(c!==-1){const s=n.join(`
1
+ "use strict";const s=require("fs"),l=require("path");function _interopDefaultCompat(r){return r&&typeof r=="object"&&"default"in r?r.default:r}const s__default=_interopDefaultCompat(s),l__default=_interopDefaultCompat(l);class Logger{libName="@meng-xi/vite-plugin";name;enabled;logTypes={info:{method:console.log,icon:"",color:"",reset:""},success:{method:console.log,icon:"\u2705",color:"\x1B[32m",reset:"\x1B[0m"},warn:{method:console.warn,icon:"\u26A0\uFE0F",color:"\x1B[33m",reset:"\x1B[0m"},error:{method:console.error,icon:"\u274C",color:"\x1B[31m",reset:"\x1B[0m"}};constructor(u){this.name=u.name,this.enabled=u.enabled??!1}formatPrefix(){let u=`[${this.libName}:${this.name}]`;return u=`[${new Date().toLocaleString()}] ${u}`,u}log(u,e,t){if(!this.enabled)return;const i=this.formatPrefix(),o=this.logTypes[u],{method:n,icon:c,color:h,reset:d}=o,g=c?`${c} ${i}`:i;n("=================================="),t!=null?n(h+g+d,h+e+d,t):n(h+g+d,h+e+d),n("==================================")}success(u,e){this.log("success",u,e)}info(u,e){this.log("info",u,e)}warn(u,e){this.log("warn",u,e)}error(u,e){this.log("error",u,e)}}async function checkSourceExists(r){try{await s__default.promises.access(r,s__default.constants.F_OK)}catch(u){const e=u;throw e.code==="ENOENT"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728 - ${r}`):e.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u6E90\u6587\u4EF6 - ${r}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u68C0\u67E5\u6E90\u6587\u4EF6\u65F6\u51FA\u9519 - ${r}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function ensureTargetDir(r){try{await s__default.promises.mkdir(r,{recursive:!0})}catch(u){const e=u;throw e.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u521B\u5EFA\u76EE\u6807\u76EE\u5F55 - ${r}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u521B\u5EFA\u76EE\u6807\u76EE\u5F55\u65F6\u51FA\u9519 - ${r}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function readDirRecursive(r,u){const e=await s__default.promises.readdir(r,{withFileTypes:!0});let t=[];for(const i of e){const o=l__default.join(r,i.name);if(i.isDirectory()){if(t.push(o),u){const n=await readDirRecursive(o,u);t=[...t,...n]}}else t.push(o)}return t}async function shouldUpdateFile(r,u){try{const e=await s__default.promises.stat(r),t=await s__default.promises.stat(u);return e.mtimeMs>t.mtimeMs||e.size!==t.size}catch{return!0}}async function copySourceToTarget(r,u,e){const t=Date.now(),{recursive:i,overwrite:o,incremental:n=!1}=e;let c=0,h=0,d=0;if((await s__default.promises.stat(r)).isDirectory()){await ensureTargetDir(u);const F=await readDirRecursive(r,i),C=(await Promise.all(F.map(async a=>{const E=await s__default.promises.stat(a);return{path:a,isFile:E.isFile()}}))).filter(a=>a.isFile).map(a=>a.path);for(const a of C){const E=l__default.relative(r,a),B=l__default.join(u,E);await ensureTargetDir(l__default.dirname(B));let f=o;if(!f)try{await s__default.promises.access(B,s__default.constants.F_OK),f=!1}catch{f=!0}n&&f&&(f=await shouldUpdateFile(a,B)),f?(await s__default.promises.copyFile(a,B),c++):h++}d=(await Promise.all(F.map(async a=>{const E=await s__default.promises.stat(a);return{path:a,isDirectory:E.isDirectory()}}))).filter(a=>a.isDirectory).length}else{await ensureTargetDir(l__default.dirname(u));let F=o;if(!F)try{await s__default.promises.access(u,s__default.constants.F_OK),F=!1}catch{F=!0}n&&F&&(F=await shouldUpdateFile(r,u)),F?(await s__default.promises.copyFile(r,u),c++):h++}const g=Date.now()-t;return{copiedFiles:c,skippedFiles:h,copiedDirs:d,executionTime:g}}function deepMerge(...r){const u={};for(const e of r)if(e)for(const t in e){const i=e[t],o=u[t];typeof i=="object"&&i!==null&&!Array.isArray(i)&&typeof o=="object"&&o!==null&&!Array.isArray(o)?u[t]=deepMerge(o,i):u[t]=i}return u}class Validator{options;currentField=null;errors=[];constructor(u){this.options=u}field(u){return this.currentField=u,this}required(){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");return this.options[this.currentField]==null&&this.errors.push(`${this.currentField} \u662F\u5FC5\u586B\u5B57\u6BB5`),this}string(){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");const u=this.options[this.currentField];return u!=null&&typeof u!="string"&&this.errors.push(`${this.currentField} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u7C7B\u578B`),this}boolean(){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");const u=this.options[this.currentField];return u!=null&&typeof u!="boolean"&&this.errors.push(`${this.currentField} \u5FC5\u987B\u662F\u5E03\u5C14\u7C7B\u578B`),this}number(){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");const u=this.options[this.currentField];return u!=null&&typeof u!="number"&&this.errors.push(`${this.currentField} \u5FC5\u987B\u662F\u6570\u5B57\u7C7B\u578B`),this}array(){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");const u=this.options[this.currentField];return u!=null&&!Array.isArray(u)&&this.errors.push(`${this.currentField} \u5FC5\u987B\u662F\u6570\u7EC4\u7C7B\u578B`),this}object(){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");const u=this.options[this.currentField];return u!=null&&typeof u!="object"&&!Array.isArray(u)&&this.errors.push(`${this.currentField} \u5FC5\u987B\u662F\u5BF9\u8C61\u7C7B\u578B`),this}default(u){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");return this.options[this.currentField]==null&&(this.options[this.currentField]=u),this}custom(u,e){if(!this.currentField)throw new Error("\u5FC5\u987B\u5148\u8C03\u7528 field() \u65B9\u6CD5\u6307\u5B9A\u8981\u9A8C\u8BC1\u7684\u5B57\u6BB5");const t=this.options[this.currentField];return t!=null&&!u(t)&&this.errors.push(e),this}validate(){if(this.errors.length>0)throw new Error(`\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25\uFF1A
2
+ ${this.errors.map(u=>`- ${u}`).join(`
3
+ `)}`);return this.options}}class BasePlugin{options;logger;validator;viteConfig=null;constructor(u,e){this.options=this.mergeOptions(u),this.logger=this.initLogger(e),this.validator=new Validator(this.options),this.validateOptions()}mergeOptions(u){return deepMerge({enabled:!0,verbose:!0,errorStrategy:"throw"},u)}initLogger(u){return u instanceof Logger?u:new Logger({name:this.getPluginName(),enabled:this.options.verbose,...u})}validateOptions(){}getEnforce(){}onConfigResolved(u){this.viteConfig=u,this.logger.info("\u914D\u7F6E\u89E3\u6790\u5B8C\u6210\uFF0C\u63D2\u4EF6\u5DF2\u521D\u59CB\u5316")}async safeExecute(u,e){try{return await u()}catch(t){return this.handleError(t,e)}}handleError(u,e){let t=`${e}: `;switch(u instanceof Error?t+=u.message:typeof u=="string"?t+=u:t+=String(u),this.options.errorStrategy){case"throw":throw this.logger.error(t),u;case"log":case"ignore":this.logger.error(t);return;default:throw this.logger.error(t),u}}toPlugin(){const u={name:this.getPluginName(),enforce:this.getEnforce(),configResolved:e=>{this.options.enabled&&this.onConfigResolved(e)}};return this.addPluginHooks(u),u}}function createPluginFactory(r){return u=>{const e=u,t=new r(e),i=t.toPlugin();return i.pluginInstance=t,i}}let p$1=class extends BasePlugin{validateOptions(){this.validator.field("sourceDir").required().string().custom(u=>u.trim()!=="","sourceDir \u4E0D\u80FD\u4E3A\u7A7A\u5B57\u7B26\u4E32").field("targetDir").required().string().custom(u=>u.trim()!=="","targetDir \u4E0D\u80FD\u4E3A\u7A7A\u5B57\u7B26\u4E32").field("overwrite").boolean().default(!0).field("recursive").boolean().default(!0).field("incremental").boolean().default(!0).validate()}getPluginName(){return"copy-file"}getEnforce(){return"post"}async copyFiles(){const{sourceDir:u,targetDir:e,overwrite:t=!0,recursive:i=!0,incremental:o=!0,enabled:n=!0}=this.options;if(!n){this.logger.info(`\u63D2\u4EF6\u5DF2\u7981\u7528\uFF0C\u8DF3\u8FC7\u6267\u884C\uFF1A\u4ECE ${u} \u590D\u5236\u5230 ${e}`);return}await checkSourceExists(u);const c=await copySourceToTarget(u,e,{recursive:i,overwrite:t,incremental:o});this.logger.success(`\u590D\u5236\u6587\u4EF6\u6210\u529F\uFF1A\u4ECE ${u} \u5230 ${e}`,`\u590D\u5236\u4E86 ${c.copiedFiles} \u4E2A\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u4E86 ${c.skippedFiles} \u4E2A\u6587\u4EF6\uFF0C\u8017\u65F6 ${c.executionTime}ms`)}addPluginHooks(u){u.writeBundle=async()=>{await this.safeExecute(()=>this.copyFiles(),"\u590D\u5236\u6587\u4EF6")}}};const copyFile=createPluginFactory(p$1);function generateIconTags(r){const u=[];if(r.link)return u.push(r.link),u;if(r.icons&&r.icons.length>0)u.push(...r.icons.map(e=>{let t=`<link rel="${e.rel}" href="${e.href}"`;return e.sizes&&(t+=` sizes="${e.sizes}"`),e.type&&(t+=` type="${e.type}"`),t+=" />",t}));else if(r.url)u.push(`<link rel="icon" href="${r.url}" />`);else{const e=r.base||"/",t=e.endsWith("/")?`${e}favicon.ico`:`${e}/favicon.ico`;u.push(`<link rel="icon" href="${t}" />`)}return u}class p extends BasePlugin{constructor(u){const e=typeof u=="string"?{base:u}:u||{};super(e)}validateOptions(){this.validator.field("base").string().default("/").field("url").string().field("link").string().field("icons").array(),this.options?.copyOptions&&(this.validator.field("copyOptions").object(),new Validator(this.options.copyOptions).field("sourceDir").required().string().field("targetDir").required().string().field("overwrite").boolean().default(!0).field("recursive").boolean().default(!0).validate()),this.validator.validate()}getPluginName(){return"inject-ico"}injectIcoTags(u){if(!this.options.enabled)return this.logger.info("\u63D2\u4EF6\u5DF2\u7981\u7528\uFF0C\u8DF3\u8FC7\u56FE\u6807\u6CE8\u5165"),u;const e=generateIconTags(this.options);if(e.length===0)return this.logger.info("\u6CA1\u6709\u751F\u6210\u56FE\u6807\u6807\u7B7E\uFF0C\u8DF3\u8FC7\u6CE8\u5165"),u;let t=u;const i=t.indexOf("</head>");if(i!==-1){const o=e.join(`
2
4
  `)+`
3
- `;t=t.substring(0,c)+s+t.substring(c),e&&(console.log(`\u2705 inject-ico: \u6210\u529F\u6CE8\u5165 ${n.length} \u4E2A\u56FE\u6807\u6807\u7B7E\u5230 HTML \u6587\u4EF6`),n.forEach(E=>{console.log(` - ${E}`)}))}else e&&console.warn("\u26A0 inject-ico: \u672A\u627E\u5230 </head> \u6807\u7B7E\uFF0C\u8DF3\u8FC7\u56FE\u6807\u6CE8\u5165");return t},async writeBundle(){if(!o){e&&console.log("\u2139 inject-ico: \u63D2\u4EF6\u5DF2\u7981\u7528\uFF0C\u8DF3\u8FC7\u6587\u4EF6\u590D\u5236");return}const{copyOptions:F}=r;if(!F)return;const{sourceDir:n,targetDir:t,overwrite:c=!0,recursive:s=!0}=F;try{await checkSourceExists(n),await ensureTargetDir(t),await copySourceToTarget(n,t,{recursive:s,overwrite:c}),e&&console.log(`\u2705 inject-ico: \u56FE\u6807\u6587\u4EF6\u590D\u5236\u6210\u529F\uFF1A\u4ECE ${n} \u5230 ${t}`)}catch(E){throw e&&(E instanceof Error?console.error(E.message):console.error(`\u274C inject-ico: \u56FE\u6807\u6587\u4EF6\u590D\u5236\u5931\u8D25\uFF1A\u672A\u77E5\u9519\u8BEF - ${n} -> ${t}`,E)),E}}}}exports.copyFile=copyFile,exports.injectIco=injectIco;
5
+ `;t=t.substring(0,i)+o+t.substring(i),this.logger.success(`\u6210\u529F\u6CE8\u5165 ${e.length} \u4E2A\u56FE\u6807\u6807\u7B7E\u5230 HTML \u6587\u4EF6`),e.forEach(n=>{this.logger.info(` - ${n}`)})}else this.logger.warn("\u672A\u627E\u5230 </head> \u6807\u7B7E\uFF0C\u8DF3\u8FC7\u56FE\u6807\u6CE8\u5165");return t}async copyFiles(){if(!this.options.enabled){this.logger.info("\u63D2\u4EF6\u5DF2\u7981\u7528\uFF0C\u8DF3\u8FC7\u6587\u4EF6\u590D\u5236");return}const{copyOptions:u}=this.options;if(!u)return;const{sourceDir:e,targetDir:t,overwrite:i=!0,recursive:o=!0}=u;await checkSourceExists(e);const n=await copySourceToTarget(e,t,{recursive:o,overwrite:i,incremental:!0});this.logger.success(`\u56FE\u6807\u6587\u4EF6\u590D\u5236\u6210\u529F\uFF1A\u4ECE ${e} \u5230 ${t}`,`\u590D\u5236\u4E86 ${n.copiedFiles} \u4E2A\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u4E86 ${n.skippedFiles} \u4E2A\u6587\u4EF6\uFF0C\u8017\u65F6 ${n.executionTime}ms`)}addPluginHooks(u){u.transformIndexHtml=e=>this.injectIcoTags(e),u.writeBundle=async()=>{await this.safeExecute(()=>this.copyFiles(),"\u56FE\u6807\u6587\u4EF6\u590D\u5236")}}}function injectIco(r){return new p(r).toPlugin()}exports.copyFile=copyFile,exports.injectIco=injectIco;
package/dist/index.d.cts CHANGED
@@ -1,11 +1,43 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
+ /**
4
+ * 基础插件配置
5
+ *
6
+ * @interface BasePluginOptions
7
+ */
8
+ interface BasePluginOptions {
9
+ /**
10
+ * 是否启用插件
11
+ *
12
+ * @default true
13
+ */
14
+ enabled?: boolean;
15
+ /**
16
+ * 是否启用日志
17
+ *
18
+ * @default true
19
+ */
20
+ verbose?: boolean;
21
+ /**
22
+ * 错误处理策略
23
+ *
24
+ * @default 'throw'
25
+ */
26
+ errorStrategy?: 'throw' | 'log' | 'ignore';
27
+ }
28
+ /**
29
+ * 插件工厂函数类型
30
+ *
31
+ * @template T 插件配置类型,默认继承自 BasePluginOptions
32
+ */
33
+ type PluginFactory<T extends BasePluginOptions = BasePluginOptions> = (options?: T) => Plugin;
34
+
3
35
  /**
4
36
  * 复制文件插件的配置选项接口
5
37
  *
6
38
  * @interface CopyFileOptions
7
39
  */
8
- interface CopyFileOptions {
40
+ interface CopyFileOptions extends BasePluginOptions {
9
41
  /**
10
42
  * 源文件目录的路径
11
43
  *
@@ -21,38 +53,28 @@ interface CopyFileOptions {
21
53
  /**
22
54
  * 是否覆盖同名文件
23
55
  *
24
- * @defaultValue true
25
- * @example false
56
+ * @default true
26
57
  */
27
58
  overwrite?: boolean;
28
59
  /**
29
60
  * 是否支持递归复制
30
61
  *
31
- * @defaultValue true
32
- * @example false
62
+ * @default true
33
63
  */
34
64
  recursive?: boolean;
35
65
  /**
36
- * 是否显示详细日志
37
- *
38
- * @defaultValue true
39
- * @example false
40
- */
41
- verbose?: boolean;
42
- /**
43
- * 是否启用复制功能
66
+ * 是否启用增量复制
44
67
  *
45
- * @defaultValue true
46
- * @example false
68
+ * @default true
47
69
  */
48
- enabled?: boolean;
70
+ incremental?: boolean;
49
71
  }
50
72
 
51
73
  /**
52
74
  * 复制文件插件
53
75
  *
54
- * @param options - 配置参数
55
- * @returns 一个 Vite 插件实例
76
+ * @param {CopyFileOptions} options - 插件配置选项
77
+ * @returns {Plugin} 一个 Vite 插件实例
56
78
  *
57
79
  * @example
58
80
  * ```typescript
@@ -62,34 +84,24 @@ interface CopyFileOptions {
62
84
  * targetDir: 'dist/assets'
63
85
  * })
64
86
  *
65
- * // 自定义配置
87
+ * // 高级配置
66
88
  * copyFile({
67
89
  * sourceDir: 'src/static',
68
90
  * targetDir: 'dist/static',
69
91
  * overwrite: false,
92
+ * recursive: true,
93
+ * incremental: true,
94
+ * enabled: true,
70
95
  * verbose: true,
71
- * recursive: false
72
- * })
73
- *
74
- * // 根据环境启用
75
- * copyFile({
76
- * sourceDir: 'src/assets',
77
- * targetDir: 'dist/assets',
78
- * enabled: process.env.NODE_ENV === 'production'
79
- * })
80
- *
81
- * // 禁用复制功能
82
- * copyFile({
83
- * sourceDir: 'src/assets',
84
- * targetDir: 'dist/assets',
85
- * enabled: false
96
+ * errorStrategy: 'throw'
86
97
  * })
87
98
  * ```
88
99
  *
89
100
  * @remarks
90
- * 该插件会在 Vite 构建完成后执行,将指定源目录的所有文件和子目录复制到目标目录
101
+ * 该插件会在 Vite 构建完成后执行,将指定源目录的所有文件和子目录复制到目标目录。
102
+ * 支持增量复制、递归复制和覆盖控制等功能。
91
103
  */
92
- declare function copyFile(options: CopyFileOptions): Plugin;
104
+ declare const copyFile: PluginFactory<CopyFileOptions>;
93
105
 
94
106
  /**
95
107
  * 图标配置项接口
@@ -135,15 +147,13 @@ interface CopyOptions {
135
147
  /**
136
148
  * 是否覆盖同名文件
137
149
  *
138
- * @defaultValue true
139
- * @example false
150
+ * @default true
140
151
  */
141
152
  overwrite?: boolean;
142
153
  /**
143
154
  * 是否支持递归复制
144
155
  *
145
- * @defaultValue true
146
- * @example false
156
+ * @default true
147
157
  */
148
158
  recursive?: boolean;
149
159
  }
@@ -152,11 +162,11 @@ interface CopyOptions {
152
162
  *
153
163
  * @interface InjectIcoOptions
154
164
  */
155
- interface InjectIcoOptions {
165
+ interface InjectIcoOptions extends BasePluginOptions {
156
166
  /**
157
- * 图标文件的基础路径,默认为根路径 `/`
167
+ * 图标文件的基础路径,默认为根路径 '/'
158
168
  *
159
- * @defaultValue `/`
169
+ * @default '/'
160
170
  * @example '/assets'
161
171
  */
162
172
  base?: string;
@@ -183,20 +193,6 @@ interface InjectIcoOptions {
183
193
  * ]
184
194
  */
185
195
  icons?: Icon[];
186
- /**
187
- * 是否显示详细日志
188
- *
189
- * @defaultValue true
190
- * @example false
191
- */
192
- verbose?: boolean;
193
- /**
194
- * 是否启用图标注入和文件复制功能
195
- *
196
- * @defaultValue true
197
- * @example false
198
- */
199
- enabled?: boolean;
200
196
  /**
201
197
  * 图标文件复制配置选项
202
198
  *
@@ -207,76 +203,39 @@ interface InjectIcoOptions {
207
203
  }
208
204
 
209
205
  /**
210
- * 注入网站图标链接到 HTML 文件的头部
211
- *
212
- * @param options - 配置选项(字符串时视为 base)
213
- * @returns 一个 Vite 插件实例,用于在构建过程中修改 HTML 文件
206
+ * 创建注入图标插件实例
214
207
  *
208
+ * @export
209
+ * @param {string | InjectIcoOptions} [options] - 插件配置选项,可以是字符串形式的 base 路径或完整的配置对象
210
+ * @returns {Plugin} Vite 插件实例,用于在构建过程中注入图标链接到 HTML 文件
215
211
  * @example
216
212
  * ```typescript
217
213
  * // 基本使用
218
- * injectIco({ base: '/assets' })
219
- *
220
- * // 自定义图标
221
- * injectIco({
222
- * icons: [
223
- * { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
224
- * { rel: 'icon', href: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
225
- * { rel: 'icon', href: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' }
226
- * ]
227
- * })
228
- *
229
- * // 带文件复制功能(适用于 uni-app 等框架)
230
- * injectIco({
231
- * base: '/assets',
232
- * copyOptions: {
233
- * sourceDir: 'src/assets/icons',
234
- * targetDir: 'dist/assets/icons'
235
- * }
236
- * })
237
- *
238
- * // 带完整复制配置的使用
239
- * injectIco({
240
- * base: '/assets',
241
- * copyOptions: {
242
- * sourceDir: 'src/assets/icons',
243
- * targetDir: 'dist/assets/icons',
244
- * overwrite: false,
245
- * recursive: true
246
- * }
247
- * })
248
- *
249
- * // 关闭日志输出
250
- * injectIco({
251
- * base: '/assets',
252
- * verbose: false,
253
- * copyOptions: {
254
- * sourceDir: 'src/assets/icons',
255
- * targetDir: 'dist/assets/icons'
256
- * }
257
- * })
214
+ * injectIco() // 使用默认配置
258
215
  *
259
- * // 根据环境启用
260
- * injectIco({
261
- * base: '/assets',
262
- * enabled: process.env.NODE_ENV === 'production',
263
- * copyOptions: {
264
- * sourceDir: 'src/assets/icons',
265
- * targetDir: 'dist/assets/icons'
266
- * }
267
- * })
216
+ * // 使用字符串配置 base 路径
217
+ * injectIco('/assets')
268
218
  *
269
- * // 禁用插件
219
+ * // 使用完整配置
270
220
  * injectIco({
271
221
  * base: '/assets',
272
- * enabled: false,
222
+ * icons: [
223
+ * { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
224
+ * { rel: 'icon', href: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' }
225
+ * ],
273
226
  * copyOptions: {
274
227
  * sourceDir: 'src/assets/icons',
275
228
  * targetDir: 'dist/assets/icons'
276
229
  * }
277
230
  * })
278
231
  * ```
232
+ * @remarks
233
+ * 该函数创建并返回一个 Vite 插件实例,该实例会在构建过程中:
234
+ * 1. 将图标链接注入到 HTML 文件的 `<head>` 标签中
235
+ * 2. 如果配置了 copyOptions,将图标文件复制到目标目录
236
+ *
237
+ * 支持自定义图标链接、图标数组配置以及图标文件复制功能。
279
238
  */
280
- declare function injectIco(options?: InjectIcoOptions | string): Plugin;
239
+ declare function injectIco(options?: string | InjectIcoOptions): Plugin;
281
240
 
282
241
  export { copyFile, injectIco };