@meng-xi/vite-plugin 0.1.7 → 0.1.9
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 +43 -17
- package/README.md +56 -30
- package/dist/common/concurrency/index.cjs +1 -0
- package/dist/common/concurrency/index.d.cts +26 -0
- package/dist/common/concurrency/index.d.mts +26 -0
- package/dist/common/concurrency/index.d.ts +26 -0
- package/dist/common/concurrency/index.mjs +1 -0
- package/dist/common/format/index.cjs +1 -1
- package/dist/common/format/index.d.cts +19 -1
- package/dist/common/format/index.d.mts +19 -1
- package/dist/common/format/index.d.ts +19 -1
- package/dist/common/format/index.mjs +1 -1
- package/dist/common/fs/index.cjs +1 -1
- package/dist/common/fs/index.d.cts +19 -1
- package/dist/common/fs/index.d.mts +19 -1
- package/dist/common/fs/index.d.ts +19 -1
- package/dist/common/fs/index.mjs +1 -1
- package/dist/common/index.cjs +1 -1
- package/dist/common/index.d.cts +3 -2
- package/dist/common/index.d.mts +3 -2
- package/dist/common/index.d.ts +3 -2
- package/dist/common/index.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +1 -1
- package/dist/plugins/assetManifest/index.cjs +1 -1
- package/dist/plugins/assetManifest/index.mjs +1 -1
- package/dist/plugins/autoImport/index.cjs +13 -13
- package/dist/plugins/autoImport/index.mjs +8 -8
- package/dist/plugins/bundleAnalyzer/index.cjs +1 -1
- package/dist/plugins/bundleAnalyzer/index.mjs +1 -1
- package/dist/plugins/compressAssets/index.cjs +1 -1
- package/dist/plugins/compressAssets/index.mjs +1 -1
- package/dist/plugins/copyFile/index.cjs +1 -1
- package/dist/plugins/copyFile/index.mjs +1 -1
- package/dist/plugins/envGuard/index.cjs +5 -5
- package/dist/plugins/envGuard/index.mjs +8 -8
- package/dist/plugins/faviconManager/index.cjs +1 -1
- package/dist/plugins/faviconManager/index.mjs +1 -1
- package/dist/plugins/generateRouter/index.cjs +62 -5
- package/dist/plugins/generateRouter/index.d.cts +29 -1
- package/dist/plugins/generateRouter/index.d.mts +29 -1
- package/dist/plugins/generateRouter/index.d.ts +29 -1
- package/dist/plugins/generateRouter/index.mjs +62 -5
- package/dist/plugins/generateVersion/index.cjs +1 -1
- package/dist/plugins/generateVersion/index.mjs +1 -1
- package/dist/plugins/imageOptimizer/index.cjs +5 -0
- package/dist/plugins/imageOptimizer/index.d.cts +242 -0
- package/dist/plugins/imageOptimizer/index.d.mts +242 -0
- package/dist/plugins/imageOptimizer/index.d.ts +242 -0
- package/dist/plugins/imageOptimizer/index.mjs +5 -0
- package/dist/plugins/index.cjs +1 -1
- package/dist/plugins/index.d.cts +2 -1
- package/dist/plugins/index.d.mts +2 -1
- package/dist/plugins/index.d.ts +2 -1
- package/dist/plugins/index.mjs +1 -1
- package/package.json +15 -1
|
@@ -3,6 +3,30 @@ import 'vite';
|
|
|
3
3
|
import '../../shared/vite-plugin.B8FuZce1.js';
|
|
4
4
|
import '../../shared/vite-plugin.DRRlWY8P.js';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 导航动画类型
|
|
8
|
+
*
|
|
9
|
+
* 用于 uni.navigateTo / uni.navigateBack 的 animationType 参数,
|
|
10
|
+
* 仅 App 端生效,其他平台自动忽略。
|
|
11
|
+
*
|
|
12
|
+
* 显示动画(navigateTo):slide-in-right / slide-in-left / slide-in-top / slide-in-bottom / pop-in / fade-in / zoom-out / zoom-fade-out / none / auto
|
|
13
|
+
* 关闭动画(navigateBack):slide-out-right / slide-out-left / slide-out-top / slide-out-bottom / pop-out / fade-out / zoom-in / zoom-fade-in / none / auto
|
|
14
|
+
*
|
|
15
|
+
* @see https://en.uniapp.dcloud.io/api/router.html#animation
|
|
16
|
+
*/
|
|
17
|
+
type UniAnimationType = 'auto' | 'none' | 'slide-in-right' | 'slide-in-left' | 'slide-in-top' | 'slide-in-bottom' | 'slide-out-right' | 'slide-out-left' | 'slide-out-top' | 'slide-out-bottom' | 'fade-in' | 'fade-out' | 'zoom-out' | 'zoom-in' | 'zoom-fade-out' | 'zoom-fade-in' | 'pop-in' | 'pop-out';
|
|
18
|
+
/**
|
|
19
|
+
* 导航动画配置
|
|
20
|
+
*
|
|
21
|
+
* 仅 App 端生效,其他平台自动忽略。
|
|
22
|
+
* 优先级:push/replace 调用时传入 > meta.animation > uni 默认值
|
|
23
|
+
*/
|
|
24
|
+
interface NavigationAnimation {
|
|
25
|
+
/** 窗口动画类型 */
|
|
26
|
+
type: UniAnimationType;
|
|
27
|
+
/** 动画持续时间(ms),默认 300 */
|
|
28
|
+
duration?: number;
|
|
29
|
+
}
|
|
6
30
|
/**
|
|
7
31
|
* 路由元信息
|
|
8
32
|
*
|
|
@@ -17,6 +41,8 @@ interface RouteMeta {
|
|
|
17
41
|
isTab?: boolean;
|
|
18
42
|
/** 是否需要登录才能访问 */
|
|
19
43
|
requireAuth?: boolean;
|
|
44
|
+
/** 默认导航动画(仅 App 端生效),可被 push/replace 时的 animation 参数覆盖 */
|
|
45
|
+
animation?: NavigationAnimation;
|
|
20
46
|
/** 自定义扩展字段 */
|
|
21
47
|
[key: string]: unknown;
|
|
22
48
|
}
|
|
@@ -33,6 +59,8 @@ interface RouteConfig {
|
|
|
33
59
|
name?: string;
|
|
34
60
|
/** 路由元信息 */
|
|
35
61
|
meta?: RouteMeta;
|
|
62
|
+
/** 用户自定义扩展属性(如 beforeEnter、component 等) */
|
|
63
|
+
[key: string]: unknown;
|
|
36
64
|
}
|
|
37
65
|
/**
|
|
38
66
|
* uni-app pages.json 中的页面配置项
|
|
@@ -292,4 +320,4 @@ interface GenerateRouterOptions extends BasePluginOptions {
|
|
|
292
320
|
declare const generateRouter: PluginFactory<GenerateRouterOptions, GenerateRouterOptions>;
|
|
293
321
|
|
|
294
322
|
export { generateRouter };
|
|
295
|
-
export type { GenerateRouterOptions, NameStrategy, OutputFormat, RouteConfig, RouteMeta, UniAppPageConfig, UniAppPagesJson, UniAppTabBarConfig };
|
|
323
|
+
export type { GenerateRouterOptions, NameStrategy, NavigationAnimation, OutputFormat, RouteConfig, RouteMeta, UniAnimationType, UniAppPageConfig, UniAppPagesJson, UniAppTabBarConfig };
|
|
@@ -1,5 +1,55 @@
|
|
|
1
|
-
import{createPluginFactory as
|
|
2
|
-
`)}
|
|
1
|
+
import{createPluginFactory as b,BasePlugin as A}from"../../factory/index.mjs";import{writeFileContent as d,shouldUpdateFileContent as y}from"../../common/fs/index.mjs";import{resolve as h}from"path";import{existsSync as f,promises as E,watch as B}from"fs";import"../../logger/index.mjs";import"../../shared/vite-plugin.DcExl6jd.mjs";import"../../common/concurrency/index.mjs";function D(t,e=/[/-]/){return t.replace(/^\/+/,"").split(e).filter(Boolean).map((u,o)=>o===0?u.toLowerCase():u.charAt(0).toUpperCase()+u.slice(1).toLowerCase()).join("")}function $(t,e=/[/-]/){return t.replace(/^\/+/,"").split(e).filter(Boolean).map(u=>u.charAt(0).toUpperCase()+u.slice(1).toLowerCase()).join("")}function x(t){return t.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"")}function w(t,e){return typeof e=="string"?"string":typeof e=="boolean"?e?"true":"false":typeof e=="number"?"number":"unknown"}function j(t){const e=Object.entries(t);return e.length===0?"{}":`{ ${e.map(([u,o])=>`${u}: ${w(u,o)}`).join("; ")} }`}function R(t,e="@meng-xi/uni-router"){const u=[];u.push(`import '${e}'`),u.push(""),u.push(`declare module '${e}' {`),u.push(" interface RouteNameMap {");for(const o of t){const n=o.name||o.path;o.meta?.title&&u.push(` /** ${o.meta.title} */`);const r=o.meta?j(o.meta):"{}";u.push(` ${n}: { path: '${o.path}'; meta: ${r} }`)}return u.push(" }"),u.push("}"),u.join(`
|
|
2
|
+
`)}function P(t){return JSON.stringify(t,null," ").replace(/"(\w+)":/g,"$1:").replace(/: "([^"]+)"/g,": '$1'")}function F(t){return t===null?"null":t===void 0?"undefined":typeof t=="string"?`'${t.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`:typeof t=="number"||typeof t=="boolean"?String(t):Array.isArray(t)?"["+t.map(e=>F(e)).join(", ")+"]":typeof t=="object"?`{ ${Object.entries(t).map(([e,u])=>`${e}: ${F(u)}`).join(", ")} }`:String(t)}function O(t){const e=[];let u=0,o=-1,n=!1,r="";for(let s=0;s<t.length;s++){const c=t[s];if(n){c===r&&t[s-1]!=="\\"&&(n=!1);continue}if(c==="/"&&s+1<t.length&&t[s+1]==="/"){const p=t.indexOf(`
|
|
3
|
+
`,s);s=p===-1?t.length-1:p-1;continue}if(c==="/"&&s+1<t.length&&t[s+1]==="*"){const p=t.indexOf("*/",s+2);s=p===-1?t.length-1:p+1;continue}if(c==='"'||c==="'"||c==="`"){n=!0,r=c;continue}c==="{"?(u===0&&(o=s),u++):c==="}"&&(u--,u===0&&o>=0&&(e.push(t.substring(o,s+1)),o=-1))}return e}function m(t,e,u){const o=new RegExp(`(\\b${e}\\s*:\\s*)`),n=t.match(o);if(!n||n.index===void 0){const i=t.lastIndexOf("}"),a=t.substring(0,i),g=a.lastIndexOf(`
|
|
4
|
+
`),C=`
|
|
5
|
+
${g>=0?a.substring(g+1).match(/^(\s*)/)?.[1]??" ":" "}${e}: ${u},`;return t.substring(0,i)+C+`
|
|
6
|
+
`+t.substring(i)}const r=n.index+n[0].length;let s=0,c=!1,p="",l=r;for(let i=r;i<t.length;i++){const a=t[i];if(c){a===p&&t[i-1]!=="\\"&&(c=!1);continue}if(a==="/"&&i+1<t.length&&t[i+1]==="/"){const g=t.indexOf(`
|
|
7
|
+
`,i);i=g===-1?t.length-1:g-1;continue}if(a==="/"&&i+1<t.length&&t[i+1]==="*"){const g=t.indexOf("*/",i+2);i=g===-1?t.length-1:g+1;continue}if(a==='"'||a==="'"||a==="`"){c=!0,p=a;continue}if(a==="{"||a==="["||a==="(")s++;else if(a==="}"||a==="]"||a===")")if(s>0)s--;else{l=i;break}else if(s===0&&a===","){l=i;break}}return t.substring(0,n.index)+`${e}: ${u}`+t.substring(l)}function v(t,e){const u=new RegExp(`,?\\s*\\b${e}\\s*:\\s*`),o=t.match(u);if(!o||o.index===void 0)return t;const n=o.index,r=n+o[0].length;let s=0,c=!1,p="",l=r;for(let i=r;i<t.length;i++){const a=t[i];if(c){a===p&&t[i-1]!=="\\"&&(c=!1);continue}if(a==="/"&&i+1<t.length&&t[i+1]==="/"){const g=t.indexOf(`
|
|
8
|
+
`,i);i=g===-1?t.length-1:g-1;continue}if(a==="/"&&i+1<t.length&&t[i+1]==="*"){const g=t.indexOf("*/",i+2);i=g===-1?t.length-1:g+1;continue}if(a==='"'||a==="'"||a==="`"){c=!0,p=a;continue}if(a==="{"||a==="["||a==="("){s++;continue}if(a==="}"||a==="]"||a===")"){if(s>0){s--;continue}l=i;break}if(s===0&&a===","){l=i;break}}if(o[0].startsWith(","))return t.substring(0,n)+t.substring(l);{let i=l;return t[i]===","&&i++,t.substring(0,n)+t.substring(i)}}function k(t){const e=new Map,u=t.match(/export const routes[^=]*=\s*(\[[\s\S]*?\](?=\s*\n|\s*$|\s*\/\/))/);if(!u)return e;const o=O(u[1]);for(const n of o){const r=n.match(/path:\s*['"]([^'"]*)['"]/);r&&e.set(r[1],n.trim())}return e}function S(t){const e=new Map,u=t.match(/export const routes[^=]*=\s*(\[[\s\S]*?\](?=\s*\n|\s*$|\s*\/\/))/);if(!u)return e;try{let o=u[1].replace(/(\w+)(?=\s*:)/g,'"$1"').replace(/'([^']*)'/g,'"$1"').replace(/,\s*([\]\}])/g,"$1");const n=JSON.parse(o);for(const r of n)r.path&&e.set(r.path,r)}catch{}return e}class T extends A{projectRoot=process.cwd();tabBarPages=new Set;watcher=null;getDefaultOptions(){return{pagesJsonPath:"src/pages.json",outputPath:"src/router.config.ts",outputFormat:"ts",nameStrategy:"camelCase",includeSubPackages:!0,watch:!0,exportTypes:!0,preserveRouteChanges:!0,metaMapping:{navigationBarTitleText:"title",requireAuth:"requireAuth"},dts:!1}}validateOptions(){if(this.validator.field("pagesJsonPath").string().field("outputPath").string().field("outputFormat").enum(["ts","js"]).field("nameStrategy").enum(["path","camelCase","pascalCase","custom"]).validate(),this.options.nameStrategy==="custom"&&!this.options.customNameGenerator)throw new Error("\u5F53 nameStrategy \u4E3A custom \u65F6\uFF0C\u5FC5\u987B\u63D0\u4F9B customNameGenerator")}getPluginName(){return"generate-router"}generateRouteName(e){switch(this.options.nameStrategy){case"path":return e.replace(/\//g,"_").replace(/^_/,"");case"camelCase":return D(e);case"pascalCase":return $(e);case"custom":return this.options.customNameGenerator(e);default:return D(e)}}extractMeta(e,u){const o={},n=e.style||{},r=this.options.metaMapping||{};for(const[s,c]of Object.entries(r))n[s]!==void 0&&(o[c]=n[s]);return this.tabBarPages.has(u)&&(o.isTab=!0),o}parsePageToRoute(e,u=""){const o=u?`/${u}/${e.path}`:`/${e.path}`,n=this.generateRouteName(o),r=this.extractMeta(e,o.replace(/^\//,"")),s={path:o,name:n};return Object.keys(r).length>0&&(s.meta=r),s}parsePagesJson(e){const u=[];if(!e.pages||!Array.isArray(e.pages)||e.pages.length===0)return this.logger.warn("pages.json \u4E2D\u6CA1\u6709\u6709\u6548\u7684\u9875\u9762\u914D\u7F6E"),u;if(this.tabBarPages.clear(),e.tabBar?.list)for(const o of e.tabBar.list)this.tabBarPages.add(o.pagePath);for(const o of e.pages)u.push(this.parsePageToRoute(o));if(this.options.includeSubPackages&&e.subPackages){for(const o of e.subPackages)if(o.pages&&Array.isArray(o.pages))for(const n of o.pages)u.push(this.parsePageToRoute(n,o.root))}return u}generateTypeDefinitions(){return!this.options.exportTypes||this.options.outputFormat==="js"?"":`
|
|
9
|
+
/**
|
|
10
|
+
* \u5BFC\u822A\u52A8\u753B\u7C7B\u578B
|
|
11
|
+
*
|
|
12
|
+
* \u7528\u4E8E uni.navigateTo / uni.navigateBack \u7684 animationType \u53C2\u6570\uFF0C
|
|
13
|
+
* \u4EC5 App \u7AEF\u751F\u6548\uFF0C\u5176\u4ED6\u5E73\u53F0\u81EA\u52A8\u5FFD\u7565\u3002
|
|
14
|
+
*
|
|
15
|
+
* \u663E\u793A\u52A8\u753B\uFF08navigateTo\uFF09\uFF1Aslide-in-right / slide-in-left / slide-in-top / slide-in-bottom / pop-in / fade-in / zoom-out / zoom-fade-out / none / auto
|
|
16
|
+
* \u5173\u95ED\u52A8\u753B\uFF08navigateBack\uFF09\uFF1Aslide-out-right / slide-out-left / slide-out-top / slide-out-bottom / pop-out / fade-out / zoom-in / zoom-fade-in / none / auto
|
|
17
|
+
*
|
|
18
|
+
* @see https://en.uniapp.dcloud.io/api/router.html#animation
|
|
19
|
+
*/
|
|
20
|
+
export type UniAnimationType =
|
|
21
|
+
| 'auto'
|
|
22
|
+
| 'none'
|
|
23
|
+
| 'slide-in-right'
|
|
24
|
+
| 'slide-in-left'
|
|
25
|
+
| 'slide-in-top'
|
|
26
|
+
| 'slide-in-bottom'
|
|
27
|
+
| 'slide-out-right'
|
|
28
|
+
| 'slide-out-left'
|
|
29
|
+
| 'slide-out-top'
|
|
30
|
+
| 'slide-out-bottom'
|
|
31
|
+
| 'fade-in'
|
|
32
|
+
| 'fade-out'
|
|
33
|
+
| 'zoom-out'
|
|
34
|
+
| 'zoom-in'
|
|
35
|
+
| 'zoom-fade-out'
|
|
36
|
+
| 'zoom-fade-in'
|
|
37
|
+
| 'pop-in'
|
|
38
|
+
| 'pop-out'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* \u5BFC\u822A\u52A8\u753B\u914D\u7F6E
|
|
42
|
+
*
|
|
43
|
+
* \u4EC5 App \u7AEF\u751F\u6548\uFF0C\u5176\u4ED6\u5E73\u53F0\u81EA\u52A8\u5FFD\u7565\u3002
|
|
44
|
+
* \u4F18\u5148\u7EA7\uFF1Apush/replace \u8C03\u7528\u65F6\u4F20\u5165 > meta.animation > uni \u9ED8\u8BA4\u503C
|
|
45
|
+
*/
|
|
46
|
+
export interface NavigationAnimation {
|
|
47
|
+
/** \u7A97\u53E3\u52A8\u753B\u7C7B\u578B */
|
|
48
|
+
type: UniAnimationType
|
|
49
|
+
/** \u52A8\u753B\u6301\u7EED\u65F6\u95F4\uFF08ms\uFF09\uFF0C\u9ED8\u8BA4 300 */
|
|
50
|
+
duration?: number
|
|
51
|
+
}
|
|
52
|
+
|
|
3
53
|
/**
|
|
4
54
|
* \u8DEF\u7531\u5143\u4FE1\u606F
|
|
5
55
|
*/
|
|
@@ -10,6 +60,8 @@ export interface RouteMeta {
|
|
|
10
60
|
isTab?: boolean
|
|
11
61
|
/** \u662F\u5426\u9700\u8981\u767B\u5F55 */
|
|
12
62
|
requireAuth?: boolean
|
|
63
|
+
/** \u9ED8\u8BA4\u5BFC\u822A\u52A8\u753B\uFF08\u4EC5 App \u7AEF\u751F\u6548\uFF09\uFF0C\u53EF\u88AB push/replace \u65F6\u7684 animation \u53C2\u6570\u8986\u76D6 */
|
|
64
|
+
animation?: NavigationAnimation
|
|
13
65
|
/** \u81EA\u5B9A\u4E49\u6269\u5C55\u5B57\u6BB5 */
|
|
14
66
|
[key: string]: unknown
|
|
15
67
|
}
|
|
@@ -25,12 +77,17 @@ export interface RouteConfig {
|
|
|
25
77
|
/** \u8DEF\u7531\u5143\u4FE1\u606F */
|
|
26
78
|
meta?: RouteMeta
|
|
27
79
|
}
|
|
28
|
-
`}generateFileContent(
|
|
80
|
+
`}generateFileContent(e,u){const o=this.generateTypeDefinitions(),n=this.options.outputFormat==="ts"?": RouteConfig[]":"",r=e.map(s=>{const c=u?.get(s.path);if(c){let p=c;if(p=m(p,"path",`'${s.path}'`),s.name!==void 0&&(p=m(p,"name",`'${s.name}'`)),s.meta&&Object.keys(s.meta).length>0){const l=F(s.meta);p=m(p,"meta",l)}else s.meta&&Object.keys(s.meta).length===0&&(p=v(p,"meta"));return p}return P(s)}).map(s=>" "+s.split(`
|
|
81
|
+
`).join(`
|
|
82
|
+
`)).join(`,
|
|
83
|
+
`);return`${o}
|
|
29
84
|
/**
|
|
30
85
|
* \u8DEF\u7531\u914D\u7F6E\u5217\u8868
|
|
31
86
|
* @description \u7531 pages.json \u81EA\u52A8\u751F\u6210
|
|
32
87
|
*/
|
|
33
|
-
export const routes${
|
|
88
|
+
export const routes${n} = [
|
|
89
|
+
${r}
|
|
90
|
+
]
|
|
34
91
|
|
|
35
92
|
export default routes
|
|
36
|
-
`}async readPagesJson(){const
|
|
93
|
+
`}async readPagesJson(){const e=h(this.projectRoot,this.options.pagesJsonPath);if(!f(e))return this.logger.warn(`pages.json \u6587\u4EF6\u4E0D\u5B58\u5728: ${e}`),null;try{const u=await E.readFile(e,"utf-8"),o=x(u);return JSON.parse(o)}catch(u){return this.logger.error(`\u89E3\u6790 pages.json \u5931\u8D25: ${u.message}`),null}}mergeRoutes(e,u){return e.map(o=>{const n=u.get(o.path);if(!n)return o;const r={};return o.meta&&Object.assign(r,o.meta),n.meta&&Object.assign(r,n.meta),{...n,path:o.path,meta:Object.keys(r).length>0?r:void 0}})}async generateRouterConfig(){const e=await this.readPagesJson();if(!e)return;let u=this.parsePagesJson(e),o;const n=h(this.projectRoot,this.options.outputPath);if(this.options.preserveRouteChanges&&f(n))try{const s=await E.readFile(n,"utf-8"),c=S(s);o=k(s),c.size>0&&(u=this.mergeRoutes(u,c),this.logger.info("\u5DF2\u5408\u5E76\u7528\u6237\u5BF9\u8DEF\u7531\u914D\u7F6E\u7684\u4FEE\u6539"))}catch{}const r=this.generateFileContent(u,o);await d(n,r),this.logger.success(`\u8DEF\u7531\u914D\u7F6E\u6587\u4EF6\u5DF2\u751F\u6210: ${n}`),this.logger.info(`\u5171\u751F\u6210 ${u.length} \u6761\u8DEF\u7531\u914D\u7F6E`),await this.generateDtsFile(u)}async generateDtsFile(e){if(!this.options.dts)return;const u=h(this.projectRoot,typeof this.options.dts=="string"?this.options.dts:"src/router.d.ts"),o=R(e);y(u,o)&&(await d(u,o),this.logger.success(`\u8DEF\u7531\u7C7B\u578B\u58F0\u660E\u6587\u4EF6\u5DF2\u751F\u6210: ${u}`))}startWatching(){if(!this.options.watch)return;const e=h(this.projectRoot,this.options.pagesJsonPath);f(e)&&(this.watcher=B(e,async u=>{u==="change"&&(this.logger.info("\u68C0\u6D4B\u5230 pages.json \u53D8\u5316\uFF0C\u91CD\u65B0\u751F\u6210\u8DEF\u7531\u914D\u7F6E..."),await this.safeExecute(()=>this.generateRouterConfig(),"\u91CD\u65B0\u751F\u6210\u8DEF\u7531\u914D\u7F6E"))}),this.logger.info(`\u6B63\u5728\u76D1\u542C pages.json \u53D8\u5316: ${e}`))}stopWatching(){this.watcher&&(this.watcher.close(),this.watcher=null)}addPluginHooks(e){e.configResolved=async u=>{this.projectRoot=u.root,await this.safeExecute(()=>this.generateRouterConfig(),"\u751F\u6210\u8DEF\u7531\u914D\u7F6E"),u.command==="serve"&&this.startWatching()}}destroy(){super.destroy(),this.stopWatching()}}const J=b(T);export{J as generateRouter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const factory_index=require("../../factory/index.cjs"),crypto=require("crypto"),common_format_index=require("../../common/format/index.cjs"),common_fs_index=require("../../common/fs/index.cjs"),
|
|
1
|
+
"use strict";const factory_index=require("../../factory/index.cjs"),crypto=require("crypto"),common_format_index=require("../../common/format/index.cjs"),common_fs_index=require("../../common/fs/index.cjs"),o=require("path");require("../../logger/index.cjs"),require("../../shared/vite-plugin.Bcg6RW2N.cjs"),require("fs"),require("../../common/concurrency/index.cjs");function generateRandomHash(i=8){const s=Math.max(1,Math.min(64,i));return crypto.randomBytes(Math.ceil(s/2)).toString("hex").slice(0,s)}function parseTemplate(i,s){let e=i;for(const[n,a]of Object.entries(s))e=e.replace(new RegExp(`\\{${n}\\}`,"g"),a);return e}function generateVersionString(i){const{format:s,buildTime:e,hashLength:n,semverBase:a,customFormat:u,prefix:m,suffix:c}=i,t=common_format_index.getDateFormatParams(e),h=generateRandomHash(n);let r;switch(s){case"timestamp":r=`${t.YYYY}${t.MM}${t.DD}${t.HH}${t.mm}${t.ss}`;break;case"date":r=`${t.YYYY}.${t.MM}.${t.DD}`;break;case"datetime":r=`${t.YYYY}.${t.MM}.${t.DD}.${t.HH}${t.mm}${t.ss}`;break;case"semver":r=a||"1.0.0";break;case"hash":r=h;break;case"custom":r=parseCustomFormat(u||"",{...t,hash:h},a);break;default:r=t.timestamp}return`${m||""}${r}${c||""}`}function parseCustomFormat(i,s,e){const n={...s};if(e){const[a,u,m]=e.split(".");n.major=a||"1",n.minor=u||"0",n.patch=m||"0"}return parseTemplate(i,n)}function generateVersionInfoObject(i){return{version:i.version,buildTime:i.buildTime.toISOString(),timestamp:i.buildTime.getTime(),format:i.format,...i.extra}}class p extends factory_index.BasePlugin{version="";buildTime=new Date;getDefaultOptions(){return{format:"timestamp",semverBase:"1.0.0",outputType:"file",outputFile:"version.json",defineName:"__APP_VERSION__",hashLength:8,prefix:"",suffix:""}}validateOptions(){if(this.validator.field("format").enum(["timestamp","date","datetime","semver","hash","custom"]).field("outputType").enum(["file","define","both"]).field("hashLength").number().minValue(1).maxValue(32).validate(),this.options.format==="custom"&&!this.options.customFormat)throw new Error("\u5F53 format \u4E3A custom \u65F6\uFF0C\u5FC5\u987B\u63D0\u4F9B customFormat")}getPluginName(){return"generate-version"}generateVersion(){return generateVersionString({format:this.options.format,buildTime:this.buildTime,hashLength:this.options.hashLength||8,semverBase:this.options.semverBase,customFormat:this.options.customFormat,prefix:this.options.prefix,suffix:this.options.suffix})}generateVersionInfo(){return generateVersionInfoObject({version:this.version,buildTime:this.buildTime,format:this.options.format,extra:this.options.extra})}async writeVersionFile(s){const e=o.join(s,this.options.outputFile||"version.json"),n=this.generateVersionInfo();await common_fs_index.writeFileContent(e,JSON.stringify(n,null,2)),this.logger.success(`\u7248\u672C\u6587\u4EF6\u5DF2\u751F\u6210: ${e}`)}addPluginHooks(s){s.configResolved=()=>{this.buildTime=new Date,this.version=this.generateVersion(),this.logger.info(`\u751F\u6210\u7248\u672C\u53F7: ${this.version}`)},(this.options.outputType==="define"||this.options.outputType==="both")&&(s.config=()=>{this.version||(this.buildTime=new Date,this.version=this.generateVersion());const e=this.options.defineName||"__APP_VERSION__";return this.logger.info(`\u6CE8\u5165\u5168\u5C40\u53D8\u91CF: ${e} = "${this.version}"`),{define:{[e]:JSON.stringify(this.version),[`${e}_INFO`]:JSON.stringify(this.generateVersionInfo())}}}),(this.options.outputType==="file"||this.options.outputType==="both")&&(s.writeBundle=async()=>{this.viteConfig&&await this.safeExecute(async()=>{const e=this.viteConfig.build.outDir;await this.writeVersionFile(e)},"\u5199\u5165\u7248\u672C\u6587\u4EF6")})}}const generateVersion=factory_index.createPluginFactory(p);exports.generateVersion=generateVersion;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createPluginFactory as
|
|
1
|
+
import{createPluginFactory as f,BasePlugin as p}from"../../factory/index.mjs";import{randomBytes as l}from"crypto";import{getDateFormatParams as c}from"../../common/format/index.mjs";import{writeFileContent as g}from"../../common/fs/index.mjs";import{join as d}from"path";import"../../logger/index.mjs";import"../../shared/vite-plugin.DcExl6jd.mjs";import"fs";import"../../common/concurrency/index.mjs";function F(i=8){const s=Math.max(1,Math.min(64,i));return l(Math.ceil(s/2)).toString("hex").slice(0,s)}function v(i,s){let e=i;for(const[o,n]of Object.entries(s))e=e.replace(new RegExp(`\\{${o}\\}`,"g"),n);return e}function b(i){const{format:s,buildTime:e,hashLength:o,semverBase:n,customFormat:a,prefix:u,suffix:h}=i,t=c(e),m=F(o);let r;switch(s){case"timestamp":r=`${t.YYYY}${t.MM}${t.DD}${t.HH}${t.mm}${t.ss}`;break;case"date":r=`${t.YYYY}.${t.MM}.${t.DD}`;break;case"datetime":r=`${t.YYYY}.${t.MM}.${t.DD}.${t.HH}${t.mm}${t.ss}`;break;case"semver":r=n||"1.0.0";break;case"hash":r=m;break;case"custom":r=$(a||"",{...t,hash:m},n);break;default:r=t.timestamp}return`${u||""}${r}${h||""}`}function $(i,s,e){const o={...s};if(e){const[n,a,u]=e.split(".");o.major=n||"1",o.minor=a||"0",o.patch=u||"0"}return v(i,o)}function T(i){return{version:i.version,buildTime:i.buildTime.toISOString(),timestamp:i.buildTime.getTime(),format:i.format,...i.extra}}class x extends p{version="";buildTime=new Date;getDefaultOptions(){return{format:"timestamp",semverBase:"1.0.0",outputType:"file",outputFile:"version.json",defineName:"__APP_VERSION__",hashLength:8,prefix:"",suffix:""}}validateOptions(){if(this.validator.field("format").enum(["timestamp","date","datetime","semver","hash","custom"]).field("outputType").enum(["file","define","both"]).field("hashLength").number().minValue(1).maxValue(32).validate(),this.options.format==="custom"&&!this.options.customFormat)throw new Error("\u5F53 format \u4E3A custom \u65F6\uFF0C\u5FC5\u987B\u63D0\u4F9B customFormat")}getPluginName(){return"generate-version"}generateVersion(){return b({format:this.options.format,buildTime:this.buildTime,hashLength:this.options.hashLength||8,semverBase:this.options.semverBase,customFormat:this.options.customFormat,prefix:this.options.prefix,suffix:this.options.suffix})}generateVersionInfo(){return T({version:this.version,buildTime:this.buildTime,format:this.options.format,extra:this.options.extra})}async writeVersionFile(s){const e=d(s,this.options.outputFile||"version.json"),o=this.generateVersionInfo();await g(e,JSON.stringify(o,null,2)),this.logger.success(`\u7248\u672C\u6587\u4EF6\u5DF2\u751F\u6210: ${e}`)}addPluginHooks(s){s.configResolved=()=>{this.buildTime=new Date,this.version=this.generateVersion(),this.logger.info(`\u751F\u6210\u7248\u672C\u53F7: ${this.version}`)},(this.options.outputType==="define"||this.options.outputType==="both")&&(s.config=()=>{this.version||(this.buildTime=new Date,this.version=this.generateVersion());const e=this.options.defineName||"__APP_VERSION__";return this.logger.info(`\u6CE8\u5165\u5168\u5C40\u53D8\u91CF: ${e} = "${this.version}"`),{define:{[e]:JSON.stringify(this.version),[`${e}_INFO`]:JSON.stringify(this.generateVersionInfo())}}}),(this.options.outputType==="file"||this.options.outputType==="both")&&(s.writeBundle=async()=>{this.viteConfig&&await this.safeExecute(async()=>{const e=this.viteConfig.build.outDir;await this.writeVersionFile(e)},"\u5199\u5165\u7248\u672C\u6587\u4EF6")})}}const D=f(x);export{D as generateVersion};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";const factory_index=require("../../factory/index.cjs"),u=require("node:fs"),l$2=require("node:path"),common_fs_index=require("../../common/fs/index.cjs"),common_path_index=require("../../common/path/index.cjs"),common_format_index=require("../../common/format/index.cjs"),common_concurrency_index=require("../../common/concurrency/index.cjs");require("../../logger/index.cjs"),require("../../shared/vite-plugin.Bcg6RW2N.cjs"),require("fs"),require("path");function _interopDefaultCompat(e){return e&&typeof e=="object"&&"default"in e?e.default:e}const l__default=_interopDefaultCompat(l$2),l$1={".jpg":"jpeg",".jpeg":"jpeg",".png":"png",".webp":"webp",".avif":"avif",".gif":"gif",".tiff":"tiff",".tif":"tiff",".svg":"svg"};function getFormatByExtension(e){return l$1[e]??null}function getOutputExtension(e){return{jpeg:".jpg",png:".png",webp:".webp",avif:".avif",gif:".gif",tiff:".tiff",svg:".svg"}[e]}function shouldOptimizeFile(e,i,t,o){return!(!getFormatByExtension(i)||t<o.threshold||o.includeExtensions.length>0&&!o.includeExtensions.includes(i)||common_path_index.isPathExcluded(e,o.excludePaths))}async function scanImageFiles(e,i){return(await common_fs_index.scanDirectory(e,{filter:(t,o,s)=>{const a=l__default.relative(e,t);return shouldOptimizeFile(a,o,s,i)}})).map(t=>{const o=common_path_index.normalizePath(l__default.relative(e,t.filePath)),s=getFormatByExtension(t.extension);return{filePath:t.filePath,relativePath:o,size:t.size,ext:t.extension,format:s}})}let l=null,w$1=!1;async function z(){if(l)return l;if(w$1)throw new Error("sharp \u6A21\u5757\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5B89\u88C5: npm install sharp");w$1=!0;try{const e=await import("sharp");return l=e.default||e,l}catch{throw new Error(`sharp \u6A21\u5757\u52A0\u8F7D\u5931\u8D25\u3002\u56FE\u7247\u4F18\u5316\u63D2\u4EF6\u9700\u8981 sharp \u4F5C\u4E3A\u4F9D\u8D56\u3002
|
|
2
|
+
\u8BF7\u8FD0\u884C: npm install sharp
|
|
3
|
+
sharp \u662F\u53EF\u9009\u4F9D\u8D56\uFF0C\u5982\u4E0D\u9700\u8981\u56FE\u7247\u4F18\u5316\u529F\u80FD\u53EF\u5FFD\u7565\u6B64\u9519\u8BEF\u3002`)}}let g=null,d=!1;async function S(){if(g)return g;if(d)throw new Error("svgo \u6A21\u5757\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5B89\u88C5: npm install svgo");d=!0;try{const e=await import("svgo");return g=e.default||e,g}catch{throw new Error(`svgo \u6A21\u5757\u52A0\u8F7D\u5931\u8D25\u3002SVG \u4F18\u5316\u9700\u8981 svgo \u4F5C\u4E3A\u4F9D\u8D56\u3002
|
|
4
|
+
\u8BF7\u8FD0\u884C: npm install svgo
|
|
5
|
+
\u5982\u4E0D\u9700\u8981 SVG \u4F18\u5316\u529F\u80FD\u53EF\u5FFD\u7565\u6B64\u9519\u8BEF\u3002`)}}function E(e,i){const t={jpeg:80,png:6,webp:75,avif:50,gif:!0,tiff:"deflate"};return i[e]??t[e]}function b(e,i){const t=E(e,i);switch(e){case"jpeg":return{quality:t,mozjpeg:!0};case"png":return{palette:!0,compressionLevel:t};case"webp":return{quality:t,effort:4};case"avif":return{quality:t,effort:4};case"gif":return{effort:7,colours:t===!0?void 0:t};case"tiff":return{compression:t};default:return{}}}async function compressImage(e,i,t,o){const s=Date.now(),a=await z(),F=(await u.promises.stat(e)).size;let p=a(e,{limitInputPixels:o>0?o:void 0});p=p.rotate();const r=b(i,t);p=p.toFormat(i,r),await p.toFile(e+".tmp");const n=(await u.promises.stat(e+".tmp")).size;if(n<F)try{await u.promises.rename(e+".tmp",e)}catch{try{await u.promises.unlink(e+".tmp")}catch{}throw new Error(`\u538B\u7F29\u5931\u8D25: \u65E0\u6CD5\u5199\u5165\u8F93\u51FA\u6587\u4EF6 ${e}`)}else await u.promises.unlink(e+".tmp");const c=Date.now()-s,f=n<F?n:F;return{file:e,relativePath:"",originalSize:F,optimizedSize:f,ratio:common_format_index.calcRatio(F,f),sourceFormat:i,outputFormat:i,converted:!1,duration:c}}async function convertImage(e,i,t,o,s,a){const F=Date.now(),p=await z(),r=(await u.promises.stat(e)).size;let n=p(e,{limitInputPixels:s>0?s:void 0});n=n.rotate();const c=b(t,o);n=n.toFormat(t,c);const f=getOutputExtension(t),m=e.replace(/\.[^.]+$/,f);await n.toFile(m+".tmp");const h=(await u.promises.stat(m+".tmp")).size;try{await u.promises.rename(m+".tmp",m)}catch{try{await u.promises.unlink(m+".tmp")}catch{}throw new Error(`\u683C\u5F0F\u8F6C\u6362\u5931\u8D25: \u65E0\u6CD5\u5199\u5165\u8F93\u51FA\u6587\u4EF6 ${m}`)}if(!a&&m!==e)try{await u.promises.unlink(e)}catch{}const v=Date.now()-F;return{file:a?m:e,relativePath:"",originalSize:r,optimizedSize:h,ratio:common_format_index.calcRatio(r,h),sourceFormat:i,outputFormat:t,converted:!0,duration:v}}async function optimizeSvg(e,i){const t=Date.now(),{optimize:o}=await S(),s=(await u.promises.stat(e)).size,a=await u.promises.readFile(e,"utf-8"),F=o(a,{path:e,multipass:i.multipass??!1,plugins:i.plugins??[]}),p=Buffer.byteLength(F.data,"utf-8");p<s&&await u.promises.writeFile(e,F.data,"utf-8");const r=p<s?p:s,n=Date.now()-t;return{file:e,relativePath:"",originalSize:s,optimizedSize:r,ratio:common_format_index.calcRatio(s,r),sourceFormat:"svg",outputFormat:"svg",converted:!1,duration:n}}async function isSharpAvailable(){if(l)return!0;if(w$1)return!1;try{return await z(),!0}catch{return!1}}async function isSvgoAvailable(){if(g)return!0;if(d)return!1;try{return await S(),!0}catch{return!1}}function resolveConvertMapping(e){const i={};if(e.convertToWebp)for(const[t,o]of Object.entries(e.convertToWebp))o&&t!=="svg"&&t!=="webp"&&t!=="avif"&&(i[t]="webp");if(e.convertToAvif)for(const[t,o]of Object.entries(e.convertToAvif))o&&t!=="svg"&&t!=="webp"&&t!=="avif"&&(i[t]="avif");return e.convertMapping&&Object.assign(i,e.convertMapping),i}function getTargetFormat(e,i){return i[e]??e}function needsConversion(e,i){return e in i}function validateConvertMapping(e){const i=[];for(const[t,o]of Object.entries(e))t===o&&i.push(`\u6E90\u683C\u5F0F\u548C\u76EE\u6807\u683C\u5F0F\u4E0D\u80FD\u76F8\u540C: ${t}`),o==="svg"&&i.push(`\u4E0D\u652F\u6301\u5C06 ${t} \u8F6C\u6362\u4E3A svg \u683C\u5F0F`),t==="svg"&&i.push(`\u4E0D\u652F\u6301\u5C06 svg \u8F6C\u6362\u4E3A ${o} \u683C\u5F0F\uFF0CSVG \u5E94\u4F7F\u7528 svgo \u5355\u72EC\u4F18\u5316`);return i}function buildSummary(e,i,t,o){const s=e.reduce((n,c)=>n+c.originalSize,0),a=e.reduce((n,c)=>n+c.optimizedSize,0),F=common_format_index.calcRatio(s,a),p=e.filter(n=>n.converted).length,r={};for(const n of e){const c=n.outputFormat;r[c]||(r[c]={count:0,originalSize:0,optimizedSize:0,ratio:0}),r[c].count++,r[c].originalSize+=n.originalSize,r[c].optimizedSize+=n.optimizedSize}for(const n of Object.values(r))n.ratio=common_format_index.calcRatio(n.originalSize,n.optimizedSize);return{totalFiles:e.length,skippedFiles:i,failedFiles:t,totalOriginalSize:s,totalOptimizedSize:a,totalRatio:F,byFormat:r,convertedFiles:p,executionTime:o,stats:e}}async function writeReport(e,i,t){const o=common_fs_index.resolveReportPath(e,i);if(!o)return;const s={timestamp:new Date().toISOString(),summary:{totalFiles:t.totalFiles,skippedFiles:t.skippedFiles,failedFiles:t.failedFiles,totalOriginalSize:t.totalOriginalSize,totalOptimizedSize:t.totalOptimizedSize,totalRatio:t.totalRatio,convertedFiles:t.convertedFiles,executionTime:t.executionTime},byFormat:t.byFormat,files:t.stats.map(a=>({file:a.file,relativePath:a.relativePath,originalSize:a.originalSize,optimizedSize:a.optimizedSize,ratio:a.ratio,sourceFormat:a.sourceFormat,outputFormat:a.outputFormat,converted:a.converted,duration:a.duration}))};await common_fs_index.writeJsonReport(o,s)}class w extends factory_index.BasePlugin{allStats=[];summary=null;skippedCount=0;failedCount=0;sharpReady=!1;svgoReady=!1;dependencyChecked=!1;convertMapping={};getDefaultOptions(){return{quality:{jpeg:80,png:6,webp:75,avif:50,gif:!0,tiff:"deflate"},convertToWebp:{},convertToAvif:{},convertMapping:{},svgo:{plugins:[],multipass:!1},includeExtensions:[".jpg",".jpeg",".png",".webp",".avif",".gif",".tiff",".tif",".svg"],excludePaths:[],threshold:0,keepOriginal:!0,reportOutput:"image-optimize-report.json",parallelLimit:5,maxPixels:0}}validateOptions(){this.validator.field("threshold").number().minValue(0).field("keepOriginal").boolean().field("includeExtensions").array().field("excludePaths").array().field("reportOutput").custom(o=>o===!1||typeof o=="string","reportOutput \u5FC5\u987B\u4E3A false \u6216\u5B57\u7B26\u4E32\u8DEF\u5F84").field("parallelLimit").number().minValue(1).maxValue(50).field("maxPixels").number().minValue(0).validate();const i=resolveConvertMapping(this.options),t=validateConvertMapping(i);if(t.length>0)throw new Error(`\u56FE\u7247\u4F18\u5316\u914D\u7F6E\u9519\u8BEF: ${t.join("; ")}`)}getPluginName(){return"image-optimizer"}getEnforce(){return"post"}addPluginHooks(i){i.writeBundle=async()=>{await this.safeExecute(()=>this.optimizeAllImages(),"\u4F18\u5316\u56FE\u7247\u8D44\u6E90")}}onConfigResolved(i){super.onConfigResolved(i),this.convertMapping=resolveConvertMapping(this.options)}async checkDependencies(){this.sharpReady=await isSharpAvailable(),this.svgoReady=await isSvgoAvailable(),this.sharpReady||this.logger.warn("sharp \u6A21\u5757\u4E0D\u53EF\u7528\uFF0C\u4F4D\u56FE\u683C\u5F0F\uFF08JPEG/PNG/WebP/AVIF/GIF/TIFF\uFF09\u4F18\u5316\u5C06\u8DF3\u8FC7\u3002\u8BF7\u5B89\u88C5: npm install sharp"),this.svgoReady||this.logger.warn("svgo \u6A21\u5757\u4E0D\u53EF\u7528\uFF0CSVG \u4F18\u5316\u5C06\u8DF3\u8FC7\u3002\u8BF7\u5B89\u88C5: npm install svgo"),!this.sharpReady&&!this.svgoReady&&this.logger.warn("sharp \u548C svgo \u5747\u4E0D\u53EF\u7528\uFF0C\u56FE\u7247\u4F18\u5316\u63D2\u4EF6\u5C06\u4E0D\u6267\u884C\u4EFB\u4F55\u64CD\u4F5C")}async optimizeAllImages(){if(!this.viteConfig)return;if(this.dependencyChecked||(await this.checkDependencies(),this.dependencyChecked=!0),!this.sharpReady&&!this.svgoReady){this.logger.warn("\u4F9D\u8D56\u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7\u56FE\u7247\u4F18\u5316");return}const i=this.viteConfig.build.outDir,t=Date.now();this.logger.info(`\u5F00\u59CB\u626B\u63CF\u56FE\u7247\u6587\u4EF6: ${i}`);const o=await scanImageFiles(i,this.options);if(o.length===0){this.logger.info("\u672A\u627E\u5230\u9700\u8981\u4F18\u5316\u7684\u56FE\u7247\u6587\u4EF6");return}const s=o.filter(p=>p.format==="svg"),a=o.filter(p=>p.format!=="svg");if(this.logger.info(`\u53D1\u73B0 ${o.length} \u4E2A\u5F85\u4F18\u5316\u56FE\u7247 (\u4F4D\u56FE: ${a.length}, SVG: ${s.length})`),this.allStats=[],this.skippedCount=0,this.failedCount=0,a.length>0&&this.sharpReady){const p=await common_concurrency_index.runWithConcurrency(a,async r=>this.processBitmap({...r,format:r.format}),this.options.parallelLimit);for(const r of p)r.skipped?this.skippedCount++:r.error?(this.failedCount++,this.logger.warn(`\u4F18\u5316\u5931\u8D25: ${r.error}`)):r.stat&&this.allStats.push(r.stat)}else a.length>0&&!this.sharpReady&&(this.skippedCount+=a.length,this.logger.info(`\u8DF3\u8FC7 ${a.length} \u4E2A\u4F4D\u56FE\u6587\u4EF6\uFF08sharp \u4E0D\u53EF\u7528\uFF09`));if(s.length>0&&this.svgoReady){const p=await common_concurrency_index.runWithConcurrency(s,async r=>this.processSvg(r),this.options.parallelLimit);for(const r of p)r.skipped?this.skippedCount++:r.error?(this.failedCount++,this.logger.warn(`SVG \u4F18\u5316\u5931\u8D25: ${r.error}`)):r.stat&&this.allStats.push(r.stat)}else s.length>0&&!this.svgoReady&&(this.skippedCount+=s.length,this.logger.info(`\u8DF3\u8FC7 ${s.length} \u4E2A SVG \u6587\u4EF6\uFF08svgo \u4E0D\u53EF\u7528\uFF09`));const F=Date.now()-t;this.summary=buildSummary(this.allStats,this.skippedCount,this.failedCount,F),this.options.reportOutput&&(await writeReport(i,this.options.reportOutput,this.summary),this.logger.info(`\u4F18\u5316\u62A5\u544A\u5DF2\u751F\u6210: ${this.options.reportOutput}`)),this.logSummary()}async processBitmap(i){try{const t=getTargetFormat(i.format,this.convertMapping);let o;return needsConversion(i.format,this.convertMapping)?o=await convertImage(i.filePath,i.format,t,this.options.quality,this.options.maxPixels,this.options.keepOriginal):o=await compressImage(i.filePath,i.format,this.options.quality,this.options.maxPixels),o.relativePath=i.relativePath,{stat:o}}catch(t){const o=t instanceof Error?t.message:String(t);return{error:`${i.relativePath}: ${o}`}}}async processSvg(i){try{const t=await optimizeSvg(i.filePath,this.options.svgo);return t.relativePath=i.relativePath,{stat:t}}catch(t){const o=t instanceof Error?t.message:String(t);return{error:`${i.relativePath}: ${o}`}}}logSummary(){if(!this.summary)return;const{totalFiles:i,skippedFiles:t,failedFiles:o,totalOriginalSize:s,totalOptimizedSize:a,totalRatio:F,convertedFiles:p,executionTime:r}=this.summary;this.logger.success(`\u56FE\u7247\u4F18\u5316\u5B8C\u6210: ${i} \u4E2A\u6587\u4EF6\u5DF2\u4F18\u5316`+(t>0?`\uFF0C${t} \u4E2A\u8DF3\u8FC7`:"")+(o>0?`\uFF0C${o} \u4E2A\u5931\u8D25`:""),`\u539F\u59CB\u4F53\u79EF: ${common_format_index.formatFileSize(s)} \u2192 \u4F18\u5316\u540E: ${common_format_index.formatFileSize(a)}\uFF0C\u538B\u7F29\u7387: ${F}%`+(p>0?`\uFF0C\u683C\u5F0F\u8F6C\u6362: ${p} \u4E2A`:"")+`\uFF0C\u8017\u65F6: ${r}ms`);for(const[c,f]of Object.entries(this.summary.byFormat))this.logger.info(` ${c.toUpperCase().padEnd(5)} ${f.count} \u4E2A\u6587\u4EF6\uFF0C${common_format_index.formatFileSize(f.originalSize)} \u2192 ${common_format_index.formatFileSize(f.optimizedSize)}\uFF0C\u538B\u7F29\u7387: ${f.ratio}%`);const n=[...this.allStats].sort((c,f)=>f.ratio-c.ratio).slice(0,5);if(n.length>0){this.logger.info("\u538B\u7F29\u7387 Top 5:");for(const c of n){const f=c.converted?` (${c.sourceFormat}\u2192${c.outputFormat})`:"";this.logger.info(` ${c.ratio}% ${common_format_index.formatFileSize(c.originalSize)} \u2192 ${common_format_index.formatFileSize(c.optimizedSize)}${f} ${c.relativePath}`)}}}getStats(){return[...this.allStats]}getSummary(){return this.summary}}const imageOptimizer=factory_index.createPluginFactory(w);exports.imageOptimizer=imageOptimizer;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { BasePluginOptions, PluginFactory } from '../../factory/index.cjs';
|
|
2
|
+
import 'vite';
|
|
3
|
+
import '../../shared/vite-plugin.B8FuZce1.cjs';
|
|
4
|
+
import '../../shared/vite-plugin.DRRlWY8P.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 支持的图片格式类型
|
|
8
|
+
*
|
|
9
|
+
* @description 插件支持的所有图片格式:
|
|
10
|
+
* - `jpeg`: JPEG/JPG 格式
|
|
11
|
+
* - `png`: PNG 格式
|
|
12
|
+
* - `webp`: WebP 格式
|
|
13
|
+
* - `avif`: AVIF 格式
|
|
14
|
+
* - `gif`: GIF 格式
|
|
15
|
+
* - `tiff`: TIFF 格式
|
|
16
|
+
* - `svg`: SVG 矢量格式
|
|
17
|
+
*/
|
|
18
|
+
type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif' | 'gif' | 'tiff' | 'svg';
|
|
19
|
+
/**
|
|
20
|
+
* 格式转换映射配置
|
|
21
|
+
*
|
|
22
|
+
* @description 定义从源格式到目标格式的转换规则。
|
|
23
|
+
* 键为源格式,值为目标格式。仅配置需要转换的格式。
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const convertMap: ConvertMapping = {
|
|
28
|
+
* png: 'webp', // PNG → WebP
|
|
29
|
+
* jpeg: 'avif' // JPEG → AVIF
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
type ConvertMapping = Partial<Record<ImageFormat, ImageFormat>>;
|
|
34
|
+
/**
|
|
35
|
+
* 单个图片格式的优化配置
|
|
36
|
+
*
|
|
37
|
+
* @interface FormatQualityOptions
|
|
38
|
+
* @description 针对特定图片格式的压缩质量参数。
|
|
39
|
+
* 不同格式使用不同的质量衡量标准。
|
|
40
|
+
*/
|
|
41
|
+
interface FormatQualityOptions {
|
|
42
|
+
/** JPEG 质量,范围 1-100,默认 80 */
|
|
43
|
+
jpeg?: number;
|
|
44
|
+
/** PNG 压缩级别,范围 1-9(仅 palette-based),默认 6 */
|
|
45
|
+
png?: number;
|
|
46
|
+
/** WebP 质量,范围 1-100,默认 75 */
|
|
47
|
+
webp?: number;
|
|
48
|
+
/** AVIF 质量,范围 1-100,默认 50 */
|
|
49
|
+
avif?: number;
|
|
50
|
+
/** GIF 优化选项:是否尝试调色板优化,默认 true */
|
|
51
|
+
gif?: boolean;
|
|
52
|
+
/** TIFF 压缩选项:压缩算法,默认 'deflate' */
|
|
53
|
+
tiff?: 'none' | 'lzw' | 'deflate' | 'packbits';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* SVGO 单个插件配置
|
|
57
|
+
*
|
|
58
|
+
* @interface SvgoPlugin
|
|
59
|
+
* @description SVGO 插件配置项,name 为插件名称,其余为插件参数。
|
|
60
|
+
*/
|
|
61
|
+
interface SvgoPlugin {
|
|
62
|
+
/** SVGO 插件名称 */
|
|
63
|
+
name: string;
|
|
64
|
+
/** 插件参数 */
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* SVG 优化配置
|
|
69
|
+
*
|
|
70
|
+
* @interface SvgoOptions
|
|
71
|
+
* @description 传递给 SVGO 的插件配置,与 SVGO 官方配置格式一致。
|
|
72
|
+
* 每项为 SVGO 插件名称及激活状态。
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const svgo: SvgoOptions = {
|
|
77
|
+
* plugins: [
|
|
78
|
+
* { name: 'removeViewBox', active: false },
|
|
79
|
+
* { name: 'removeEmptyContainers', active: true }
|
|
80
|
+
* ]
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
interface SvgoOptions {
|
|
85
|
+
/** SVGO 插件列表 */
|
|
86
|
+
plugins?: SvgoPlugin[];
|
|
87
|
+
/** 是否启用 SVGO 多进程优化(仅当 SVG 文件较多时建议开启) */
|
|
88
|
+
multipass?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 单个文件的优化统计信息
|
|
92
|
+
*
|
|
93
|
+
* @interface ImageOptimizeStats
|
|
94
|
+
* @description 记录单个图片文件经过优化后的详细统计数据
|
|
95
|
+
*/
|
|
96
|
+
interface ImageOptimizeStats {
|
|
97
|
+
/** 原始文件路径 */
|
|
98
|
+
file: string;
|
|
99
|
+
/** 相对于输出目录的相对路径 */
|
|
100
|
+
relativePath: string;
|
|
101
|
+
/** 原始文件大小(字节) */
|
|
102
|
+
originalSize: number;
|
|
103
|
+
/** 优化后文件大小(字节) */
|
|
104
|
+
optimizedSize: number;
|
|
105
|
+
/** 压缩率百分比(0-100),如 65.2 表示体积减少 65.2% */
|
|
106
|
+
ratio: number;
|
|
107
|
+
/** 源图片格式 */
|
|
108
|
+
sourceFormat: ImageFormat;
|
|
109
|
+
/** 输出图片格式(与 sourceFormat 相同表示仅压缩,不同表示格式转换) */
|
|
110
|
+
outputFormat: ImageFormat;
|
|
111
|
+
/** 是否发生了格式转换 */
|
|
112
|
+
converted: boolean;
|
|
113
|
+
/** 优化耗时(毫秒) */
|
|
114
|
+
duration: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 优化操作的汇总统计信息
|
|
118
|
+
*
|
|
119
|
+
* @interface ImageOptimizeSummary
|
|
120
|
+
* @description 包含整个优化操作的总体统计数据,用于报告生成和日志输出
|
|
121
|
+
*/
|
|
122
|
+
interface ImageOptimizeSummary {
|
|
123
|
+
/** 优化的文件总数 */
|
|
124
|
+
totalFiles: number;
|
|
125
|
+
/** 跳过的文件数量 */
|
|
126
|
+
skippedFiles: number;
|
|
127
|
+
/** 失败的文件数量 */
|
|
128
|
+
failedFiles: number;
|
|
129
|
+
/** 所有文件的原始大小总和(字节) */
|
|
130
|
+
totalOriginalSize: number;
|
|
131
|
+
/** 所有文件的优化后大小总和(字节) */
|
|
132
|
+
totalOptimizedSize: number;
|
|
133
|
+
/** 总体压缩率百分比 */
|
|
134
|
+
totalRatio: number;
|
|
135
|
+
/** 按格式分组的统计 */
|
|
136
|
+
byFormat: Record<string, {
|
|
137
|
+
count: number;
|
|
138
|
+
originalSize: number;
|
|
139
|
+
optimizedSize: number;
|
|
140
|
+
ratio: number;
|
|
141
|
+
}>;
|
|
142
|
+
/** 格式转换统计:转换的文件数量 */
|
|
143
|
+
convertedFiles: number;
|
|
144
|
+
/** 优化操作总耗时(毫秒) */
|
|
145
|
+
executionTime: number;
|
|
146
|
+
/** 每个文件的详细优化统计 */
|
|
147
|
+
stats: ImageOptimizeStats[];
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 图片优化插件的配置选项
|
|
151
|
+
*
|
|
152
|
+
* @interface ImageOptimizerOptions
|
|
153
|
+
* @extends {BasePluginOptions}
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* imageOptimizer({
|
|
158
|
+
* quality: { jpeg: 80, webp: 75, avif: 50 },
|
|
159
|
+
* convertToWebp: { png: true, jpeg: true },
|
|
160
|
+
* convertToAvif: { png: true },
|
|
161
|
+
* svgo: { plugins: [{ name: 'removeViewBox', active: false }] },
|
|
162
|
+
* keepOriginal: true,
|
|
163
|
+
* parallelLimit: 5,
|
|
164
|
+
* reportOutput: 'image-optimize-report.json'
|
|
165
|
+
* })
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
interface ImageOptimizerOptions extends BasePluginOptions {
|
|
169
|
+
/** 各格式的压缩质量参数 */
|
|
170
|
+
quality?: FormatQualityOptions;
|
|
171
|
+
/** 是否将指定格式转换为 WebP,值为需要转换的源格式映射 */
|
|
172
|
+
convertToWebp?: Partial<Record<'jpeg' | 'png' | 'gif' | 'tiff', boolean>>;
|
|
173
|
+
/** 是否将指定格式转换为 AVIF,值为需要转换的源格式映射 */
|
|
174
|
+
convertToAvif?: Partial<Record<'jpeg' | 'png' | 'gif' | 'tiff', boolean>>;
|
|
175
|
+
/** 自定义格式转换映射,优先级高于 convertToWebp/convertToAvif */
|
|
176
|
+
convertMapping?: ConvertMapping;
|
|
177
|
+
/** SVG 优化配置 */
|
|
178
|
+
svgo?: SvgoOptions;
|
|
179
|
+
/** 需要优化的文件扩展名列表 */
|
|
180
|
+
includeExtensions?: string[];
|
|
181
|
+
/** 需要排除的路径前缀列表 */
|
|
182
|
+
excludePaths?: string[];
|
|
183
|
+
/** 最小优化阈值(字节),小于此大小的文件将被跳过 */
|
|
184
|
+
threshold?: number;
|
|
185
|
+
/** 是否保留原始文件(格式转换时有效,仅压缩时原文件始终被覆盖) */
|
|
186
|
+
keepOriginal?: boolean;
|
|
187
|
+
/** 压缩报告输出路径,设为 false 则不生成报告 */
|
|
188
|
+
reportOutput?: string | false;
|
|
189
|
+
/** 并发优化的最大文件数 */
|
|
190
|
+
parallelLimit?: number;
|
|
191
|
+
/** 单个图片最大像素数,超过此值的图片将被缩放(0 表示不限制) */
|
|
192
|
+
maxPixels?: number;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 创建图片优化插件
|
|
197
|
+
*
|
|
198
|
+
* @function imageOptimizer
|
|
199
|
+
* @param {Partial<ImageOptimizerOptions>} [options] - 插件配置选项
|
|
200
|
+
* @returns {Plugin} Vite 插件实例
|
|
201
|
+
*
|
|
202
|
+
* @description 在 Vite 构建完成后自动优化输出目录中的图片文件,
|
|
203
|
+
* 支持多格式压缩、格式转换、并发处理、压缩报告生成等功能。
|
|
204
|
+
* 使用 sharp 处理位图格式,svgo 处理 SVG 格式。
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* // 基本使用:仅压缩
|
|
209
|
+
* imageOptimizer({
|
|
210
|
+
* quality: { jpeg: 80, png: 6, webp: 75 },
|
|
211
|
+
* reportOutput: 'image-optimize-report.json'
|
|
212
|
+
* })
|
|
213
|
+
*
|
|
214
|
+
* // 格式转换:PNG/JPEG → WebP
|
|
215
|
+
* imageOptimizer({
|
|
216
|
+
* convertToWebp: { png: true, jpeg: true },
|
|
217
|
+
* keepOriginal: true,
|
|
218
|
+
* quality: { webp: 75 }
|
|
219
|
+
* })
|
|
220
|
+
*
|
|
221
|
+
* // 自定义转换映射
|
|
222
|
+
* imageOptimizer({
|
|
223
|
+
* convertMapping: { png: 'avif', jpeg: 'webp' },
|
|
224
|
+
* quality: { avif: 50, webp: 75 }
|
|
225
|
+
* })
|
|
226
|
+
*
|
|
227
|
+
* // SVG 优化
|
|
228
|
+
* imageOptimizer({
|
|
229
|
+
* svgo: {
|
|
230
|
+
* plugins: [
|
|
231
|
+
* { name: 'removeViewBox', active: false },
|
|
232
|
+
* { name: 'removeEmptyContainers', active: true }
|
|
233
|
+
* ],
|
|
234
|
+
* multipass: true
|
|
235
|
+
* }
|
|
236
|
+
* })
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
declare const imageOptimizer: PluginFactory<ImageOptimizerOptions, ImageOptimizerOptions>;
|
|
240
|
+
|
|
241
|
+
export { imageOptimizer };
|
|
242
|
+
export type { ConvertMapping, FormatQualityOptions, ImageFormat, ImageOptimizeStats, ImageOptimizeSummary, ImageOptimizerOptions, SvgoOptions, SvgoPlugin };
|