@meng-xi/vite-plugin 0.1.5 → 0.1.6

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.
Files changed (57) hide show
  1. package/README-en.md +29 -15
  2. package/README.md +29 -15
  3. package/dist/common/format/index.cjs +1 -1
  4. package/dist/common/format/index.d.cts +35 -1
  5. package/dist/common/format/index.d.mts +35 -1
  6. package/dist/common/format/index.d.ts +35 -1
  7. package/dist/common/format/index.mjs +1 -1
  8. package/dist/common/fs/index.cjs +1 -1
  9. package/dist/common/fs/index.d.cts +36 -1
  10. package/dist/common/fs/index.d.mts +36 -1
  11. package/dist/common/fs/index.d.ts +36 -1
  12. package/dist/common/fs/index.mjs +1 -1
  13. package/dist/common/html/index.cjs +2 -2
  14. package/dist/common/html/index.d.cts +17 -1
  15. package/dist/common/html/index.d.mts +17 -1
  16. package/dist/common/html/index.d.ts +17 -1
  17. package/dist/common/html/index.mjs +2 -2
  18. package/dist/common/index.cjs +1 -1
  19. package/dist/common/index.d.cts +3 -3
  20. package/dist/common/index.d.mts +3 -3
  21. package/dist/common/index.d.ts +3 -3
  22. package/dist/common/index.mjs +1 -1
  23. package/dist/index.cjs +1 -1
  24. package/dist/index.d.cts +3 -3
  25. package/dist/index.d.mts +3 -3
  26. package/dist/index.d.ts +3 -3
  27. package/dist/index.mjs +1 -1
  28. package/dist/plugins/autoImport/index.cjs +7 -5
  29. package/dist/plugins/autoImport/index.mjs +9 -7
  30. package/dist/plugins/buildProgress/index.cjs +2 -2
  31. package/dist/plugins/bundleAnalyzer/index.cjs +21 -21
  32. package/dist/plugins/compressAssets/index.cjs +1 -1
  33. package/dist/plugins/compressAssets/index.mjs +1 -1
  34. package/dist/plugins/envGuard/index.cjs +9 -9
  35. package/dist/plugins/envGuard/index.mjs +7 -7
  36. package/dist/plugins/faviconManager/index.cjs +1 -1
  37. package/dist/plugins/faviconManager/index.mjs +1 -1
  38. package/dist/plugins/generateRouter/index.cjs +5 -4
  39. package/dist/plugins/generateRouter/index.d.cts +33 -0
  40. package/dist/plugins/generateRouter/index.d.mts +33 -0
  41. package/dist/plugins/generateRouter/index.d.ts +33 -0
  42. package/dist/plugins/generateRouter/index.mjs +3 -2
  43. package/dist/plugins/generateVersion/index.cjs +1 -1
  44. package/dist/plugins/generateVersion/index.mjs +1 -1
  45. package/dist/plugins/htmlInject/index.cjs +7 -7
  46. package/dist/plugins/htmlInject/index.mjs +2 -2
  47. package/dist/plugins/index.cjs +1 -1
  48. package/dist/plugins/index.mjs +1 -1
  49. package/dist/plugins/loadingManager/index.cjs +5 -5
  50. package/dist/plugins/loadingManager/index.mjs +4 -4
  51. package/dist/plugins/versionUpdateChecker/index.cjs +4 -4
  52. package/dist/plugins/versionUpdateChecker/index.mjs +4 -4
  53. package/dist/shared/vite-plugin.CcvHfrL8.cjs +1 -0
  54. package/dist/shared/vite-plugin.CuXEJAWX.mjs +1 -0
  55. package/package.json +2 -2
  56. package/dist/shared/vite-plugin.BPFqtmWa.mjs +0 -1
  57. package/dist/shared/vite-plugin.CnOy46d3.cjs +0 -1
package/README-en.md CHANGED
@@ -18,7 +18,7 @@
18
18
  - **Ready to Use** - 12 practical plugins covering auto-import, build progress, bundle analysis & compression, file copying, environment variable validation, route generation, version management, HTML injection, favicon
19
19
  management, global Loading, and more
20
20
  - **Plugin Development Framework** - Exports core components like BasePlugin, Logger, and Validator to quickly build custom Vite plugins
21
- - **Common Utility Library** - Built-in Common utility modules supporting on-demand sub-path imports
21
+ - **Common Utility Library** - Built-in 6 Common utility modules supporting on-demand sub-path imports
22
22
  - **Type Safe** - Complete TypeScript type definitions with configuration validators
23
23
  - **On-demand Import** - Supports sub-path exports to reduce bundle size
24
24
 
@@ -41,7 +41,7 @@ pnpm add @meng-xi/vite-plugin -D
41
41
 
42
42
  ```typescript
43
43
  import { defineConfig } from 'vite'
44
- import { buildProgress, bundleAnalyzer, compressAssets, copyFile, envGuard, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager, autoImport } from '@meng-xi/vite-plugin'
44
+ import { autoImport, buildProgress, bundleAnalyzer, compressAssets, copyFile, envGuard, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
45
45
 
46
46
  export default defineConfig({
47
47
  plugins: [
@@ -70,9 +70,9 @@ export default defineConfig({
70
70
  | [bundleAnalyzer](https://mengxi-studio.github.io/vite-plugin/en/plugins/bundle-analyzer.html) | Bundle volume analysis with JSON/HTML reports, gzip calculation, threshold alerts, and build diff |
71
71
  | [compressAssets](https://mengxi-studio.github.io/vite-plugin/en/plugins/compress-assets.html) | Asset compression with gzip / brotli / both, concurrent compression and statistics report |
72
72
  | [copyFile](https://mengxi-studio.github.io/vite-plugin/en/plugins/copy-file.html) | Copy files or directories after build, supports incremental copying |
73
- | [envGuard](https://mengxi-studio.github.io/vite-plugin/en/plugins/) | Environment variable validation with type checking, range validation, custom rules and runtime guard |
73
+ | [envGuard](https://mengxi-studio.github.io/vite-plugin/en/plugins/env-guard.html) | Environment variable validation with type checking, range validation, custom rules and runtime guard |
74
74
  | [faviconManager](https://mengxi-studio.github.io/vite-plugin/en/plugins/favicon-manager.html) | Manage website favicon link injection and file copying, supports string shorthand config |
75
- | [generateRouter](https://mengxi-studio.github.io/vite-plugin/en/plugins/generate-router.html) | Auto-generate route config from pages.json (uni-app) |
75
+ | [generateRouter](https://mengxi-studio.github.io/vite-plugin/en/plugins/generate-router.html) | Auto-generate route config and type declarations from pages.json (uni-app) |
76
76
  | [generateVersion](https://mengxi-studio.github.io/vite-plugin/en/plugins/generate-version.html) | Auto-generate version numbers with file output and global variable injection |
77
77
  | [htmlInject](https://mengxi-studio.github.io/vite-plugin/en/plugins/html-inject.html) | HTML content injection with multiple positions, selector targeting, conditional injection, template variables, and security filtering |
78
78
  | [loadingManager](https://mengxi-studio.github.io/vite-plugin/en/plugins/loading-manager.html) | Global Loading state management with request interception, debounce, transition animations, and white-screen Loading |
@@ -132,19 +132,33 @@ export const myPlugin = createPluginFactory(MyPlugin)
132
132
  Built-in general-purpose utility function library, organized by functional modules, supporting on-demand sub-path imports.
133
133
 
134
134
  ```typescript
135
- import { formatFileSize } from '@meng-xi/vite-plugin/common/format'
136
- import { scanDirectory } from '@meng-xi/vite-plugin/common/fs'
137
- import { injectBeforeTag } from '@meng-xi/vite-plugin/common/html'
135
+ // Formatting: date params, template variable replacement, file size, date formatting
136
+ import { getDateFormatParams, parseTemplate, formatDate, formatFileSize } from '@meng-xi/vite-plugin/common/format'
137
+
138
+ // File system: file copy, directory scan, safe write, change detection
139
+ import { copySourceToTarget, scanDirectory, writeFileSyncSafely, shouldUpdateFileContent } from '@meng-xi/vite-plugin/common/fs'
140
+
141
+ // HTML: tag injection, content sanitization, attribute escaping
142
+ import { injectBeforeTag, injectHeadAndBody, sanitizeContent, escapeHtmlAttr } from '@meng-xi/vite-plugin/common/html'
143
+
144
+ // Script generation: callback function wrapping
145
+ import { makeCallback } from '@meng-xi/vite-plugin/common/script'
146
+
147
+ // Terminal UI: ANSI color codes
148
+ import { ANSI } from '@meng-xi/vite-plugin/common/ui'
149
+
150
+ // Validation: chain validator, common validation functions
151
+ import { Validator, validateGlobalName, validateNoScriptInTemplate } from '@meng-xi/vite-plugin/common/validation'
138
152
  ```
139
153
 
140
- | Sub-path | Description |
141
- | -------------------------------------------------------------------------------------------- | ----------------------- |
142
- | [`common/format`](https://mengxi-studio.github.io/vite-plugin/en/common/format.html) | Formatting utilities |
143
- | [`common/fs`](https://mengxi-studio.github.io/vite-plugin/en/common/fs.html) | File system utilities |
144
- | [`common/html`](https://mengxi-studio.github.io/vite-plugin/en/common/html.html) | HTML injection utils |
145
- | [`common/script`](https://mengxi-studio.github.io/vite-plugin/en/common/script.html) | Script generation utils |
146
- | [`common/ui`](https://mengxi-studio.github.io/vite-plugin/en/common/ui.html) | Terminal UI utils |
147
- | [`common/validation`](https://mengxi-studio.github.io/vite-plugin/en/common/validation.html) | Validation utilities |
154
+ | Sub-path | Description |
155
+ | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
156
+ | [`common/format`](https://mengxi-studio.github.io/vite-plugin/en/common/format.html) | Date param extraction, template variable replacement `{{key}}`, date formatting `{YYYY}`, file size |
157
+ | [`common/fs`](https://mengxi-studio.github.io/vite-plugin/en/common/fs.html) | File/directory copy, directory scan, sync safe write, file change detection |
158
+ | [`common/html`](https://mengxi-studio.github.io/vite-plugin/en/common/html.html) | HTML tag injection, dual-zone injection, content sanitization, HTML attribute value escaping |
159
+ | [`common/script`](https://mengxi-studio.github.io/vite-plugin/en/common/script.html) | Callback body wrapping into safe function expressions (with try-catch) |
160
+ | [`common/ui`](https://mengxi-studio.github.io/vite-plugin/en/common/ui.html) | Terminal ANSI color code constants |
161
+ | [`common/validation`](https://mengxi-studio.github.io/vite-plugin/en/common/validation.html) | Chain-style config validator, global name validation, script detection, callback field validation |
148
162
 
149
163
  ## Sub-path Exports
150
164
 
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  - **开箱即用** - 12 个实用插件,覆盖自动导入、构建进度、产物分析与压缩、文件复制、环境变量校验、路由生成、版本管理、HTML 注入、图标管理、全局 Loading 等场景
19
19
  - **插件开发框架** - 导出 BasePlugin、Logger、Validator 等核心组件,快速构建自定义 Vite 插件
20
- - **通用工具库** - 内置 Common 工具模块,支持按需子路径导入
20
+ - **通用工具库** - 内置 6 大 Common 工具模块,支持按需子路径导入
21
21
  - **类型安全** - 完整 TypeScript 类型定义与配置验证器
22
22
  - **按需导入** - 支持子路径导出,减少打包体积
23
23
 
@@ -40,7 +40,7 @@ pnpm add @meng-xi/vite-plugin -D
40
40
 
41
41
  ```typescript
42
42
  import { defineConfig } from 'vite'
43
- import { buildProgress, bundleAnalyzer, compressAssets, copyFile, envGuard, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager, autoImport } from '@meng-xi/vite-plugin'
43
+ import { autoImport, buildProgress, bundleAnalyzer, compressAssets, copyFile, envGuard, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
44
44
 
45
45
  export default defineConfig({
46
46
  plugins: [
@@ -69,9 +69,9 @@ export default defineConfig({
69
69
  | [bundleAnalyzer](https://mengxi-studio.github.io/vite-plugin/plugins/bundle-analyzer.html) | 构建产物体积分析,支持 JSON/HTML 报告、gzip 计算、阈值告警和构建对比 |
70
70
  | [compressAssets](https://mengxi-studio.github.io/vite-plugin/plugins/compress-assets.html) | 构建产物压缩,支持 gzip / brotli / both,并发压缩和统计报告 |
71
71
  | [copyFile](https://mengxi-studio.github.io/vite-plugin/plugins/copy-file.html) | 构建完成后复制文件或目录,支持增量复制 |
72
- | [envGuard](https://mengxi-studio.github.io/vite-plugin/plugins/) | 环境变量校验,支持类型检查、范围验证、自定义规则和运行时守卫 |
72
+ | [envGuard](https://mengxi-studio.github.io/vite-plugin/plugins/env-guard.html) | 环境变量校验,支持类型检查、范围验证、自定义规则和运行时守卫 |
73
73
  | [faviconManager](https://mengxi-studio.github.io/vite-plugin/plugins/favicon-manager.html) | 管理网站图标链接注入和文件复制,支持字符串简写配置 |
74
- | [generateRouter](https://mengxi-studio.github.io/vite-plugin/plugins/generate-router.html) | 根据 pages.json 自动生成路由配置(uni-app) |
74
+ | [generateRouter](https://mengxi-studio.github.io/vite-plugin/plugins/generate-router.html) | 根据 pages.json 自动生成路由配置与类型声明(uni-app) |
75
75
  | [generateVersion](https://mengxi-studio.github.io/vite-plugin/plugins/generate-version.html) | 自动生成版本号,支持文件输出和全局变量注入 |
76
76
  | [htmlInject](https://mengxi-studio.github.io/vite-plugin/plugins/html-inject.html) | HTML 内容注入,支持多种位置、选择器定位、条件注入、模板变量和安全过滤 |
77
77
  | [loadingManager](https://mengxi-studio.github.io/vite-plugin/plugins/loading-manager.html) | 全局 Loading 状态管理,支持请求拦截、防抖、过渡动画和白屏 Loading |
@@ -131,19 +131,33 @@ export const myPlugin = createPluginFactory(MyPlugin)
131
131
  内置通用工具函数库,按功能模块组织,支持子路径按需导入。
132
132
 
133
133
  ```typescript
134
- import { formatFileSize } from '@meng-xi/vite-plugin/common/format'
135
- import { scanDirectory } from '@meng-xi/vite-plugin/common/fs'
136
- import { injectBeforeTag } from '@meng-xi/vite-plugin/common/html'
134
+ // 格式化:日期参数、模板变量替换、文件大小、日期格式化
135
+ import { getDateFormatParams, parseTemplate, formatDate, formatFileSize } from '@meng-xi/vite-plugin/common/format'
136
+
137
+ // 文件系统:文件复制、目录扫描、安全写入、变更检测
138
+ import { copySourceToTarget, scanDirectory, writeFileSyncSafely, shouldUpdateFileContent } from '@meng-xi/vite-plugin/common/fs'
139
+
140
+ // HTML:标签注入、内容消毒、属性转义
141
+ import { injectBeforeTag, injectHeadAndBody, sanitizeContent, escapeHtmlAttr } from '@meng-xi/vite-plugin/common/html'
142
+
143
+ // 脚本生成:回调函数包装
144
+ import { makeCallback } from '@meng-xi/vite-plugin/common/script'
145
+
146
+ // 终端 UI:ANSI 颜色码
147
+ import { ANSI } from '@meng-xi/vite-plugin/common/ui'
148
+
149
+ // 参数验证:链式验证器、通用校验函数
150
+ import { Validator, validateGlobalName, validateNoScriptInTemplate } from '@meng-xi/vite-plugin/common/validation'
137
151
  ```
138
152
 
139
- | 子路径 | 描述 |
140
- | ----------------------------------------------------------------------------------------- | ------------- |
141
- | [`common/format`](https://mengxi-studio.github.io/vite-plugin/common/format.html) | 格式化工具 |
142
- | [`common/fs`](https://mengxi-studio.github.io/vite-plugin/common/fs.html) | 文件系统工具 |
143
- | [`common/html`](https://mengxi-studio.github.io/vite-plugin/common/html.html) | HTML 注入工具 |
144
- | [`common/script`](https://mengxi-studio.github.io/vite-plugin/common/script.html) | 脚本生成工具 |
145
- | [`common/ui`](https://mengxi-studio.github.io/vite-plugin/common/ui.html) | 终端 UI 工具 |
146
- | [`common/validation`](https://mengxi-studio.github.io/vite-plugin/common/validation.html) | 参数验证工具 |
153
+ | 子路径 | 描述 |
154
+ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
155
+ | [`common/format`](https://mengxi-studio.github.io/vite-plugin/common/format.html) | 日期参数提取、模板变量替换 `{{key}}`、日期格式化 `{YYYY}`、文件大小格式化 |
156
+ | [`common/fs`](https://mengxi-studio.github.io/vite-plugin/common/fs.html) | 文件/目录复制、目录扫描、同步安全写入、文件变更检测 |
157
+ | [`common/html`](https://mengxi-studio.github.io/vite-plugin/common/html.html) | HTML 标签注入、双区域注入、内容安全消毒、HTML 属性值转义 |
158
+ | [`common/script`](https://mengxi-studio.github.io/vite-plugin/common/script.html) | 回调函数体包装为安全的函数表达式(含 try-catch) |
159
+ | [`common/ui`](https://mengxi-studio.github.io/vite-plugin/common/ui.html) | 终端 ANSI 颜色码常量 |
160
+ | [`common/validation`](https://mengxi-studio.github.io/vite-plugin/common/validation.html) | 链式配置验证器、全局名称校验、脚本检测、回调字段校验 |
147
161
 
148
162
  ## 子路径导出
149
163
 
@@ -1 +1 @@
1
- "use strict";function r(e,t=2){return e.toString().padStart(t,"0")}function getDateFormatParams(e=new Date){return{YYYY:e.getFullYear().toString(),YY:e.getFullYear().toString().slice(-2),MM:r(e.getMonth()+1),DD:r(e.getDate()),HH:r(e.getHours()),mm:r(e.getMinutes()),ss:r(e.getSeconds()),SSS:r(e.getMilliseconds(),3),timestamp:e.getTime().toString()}}function formatFileSize(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(2)}MB`}exports.formatFileSize=formatFileSize,exports.getDateFormatParams=getDateFormatParams;
1
+ "use strict";function t(e,r=2){return e.toString().padStart(r,"0")}function getDateFormatParams(e=new Date){return{YYYY:e.getFullYear().toString(),YY:e.getFullYear().toString().slice(-2),MM:t(e.getMonth()+1),DD:t(e.getDate()),HH:t(e.getHours()),mm:t(e.getMinutes()),ss:t(e.getSeconds()),SSS:t(e.getMilliseconds(),3),timestamp:e.getTime().toString()}}function parseTemplate(e,r){let o=e;for(const[n,a]of Object.entries(r)){const s=n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),i=a.replace(/\$/g,"$$$$");o=o.replace(new RegExp(`\\{\\{${s}\\}\\}`,"g"),i)}return o}function formatDate(e,r){const o=getDateFormatParams(e);let n=r;for(const[a,s]of Object.entries(o))n=n.replace(new RegExp(`\\{${a}\\}`,"g"),s);return n}function formatFileSize(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(2)}MB`}exports.formatDate=formatDate,exports.formatFileSize=formatFileSize,exports.getDateFormatParams=getDateFormatParams,exports.parseTemplate=parseTemplate;
@@ -35,6 +35,40 @@ interface DateFormatOptions {
35
35
  * ```
36
36
  */
37
37
  declare function getDateFormatParams(date?: Date): DateFormatOptions;
38
+ /**
39
+ * 替换模板字符串中的变量占位符
40
+ *
41
+ * @param template - 包含 `{{key}}` 占位符的模板字符串
42
+ * @param values - 占位符键值映射,支持合并多组变量(后者覆盖前者)
43
+ * @returns 替换占位符后的字符串
44
+ *
45
+ * @description 将模板中的 `{{key}}` 占位符替换为对应的值。
46
+ * 键名中的正则特殊字符会被自动转义,值中的 `$` 也会被安全处理。
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * parseTemplate('Hello {{name}}!', { name: 'World' })
51
+ * // 'Hello World!'
52
+ *
53
+ * parseTemplate('{{YYYY}}-{{MM}}-{{DD}}', getDateFormatParams())
54
+ * // '2026-06-06'
55
+ * ```
56
+ */
57
+ declare function parseTemplate(template: string, values: Record<string, string>): string;
58
+ /**
59
+ * 格式化日期字符串
60
+ *
61
+ * @param date - 日期对象
62
+ * @param format - 格式字符串,支持 `{YYYY}`、`{MM}`、`{DD}`、`{HH}`、`{mm}`、`{ss}` 等占位符
63
+ * @returns 格式化后的日期字符串
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * formatDate(new Date(), '{YYYY}-{MM}-{DD}T{HH}:{mm}:{ss}')
68
+ * // '2026-06-06T15:30:00'
69
+ * ```
70
+ */
71
+ declare function formatDate(date: Date, format: string): string;
38
72
  /**
39
73
  * 将字节数格式化为人类可读的文件大小字符串
40
74
  *
@@ -55,5 +89,5 @@ declare function getDateFormatParams(date?: Date): DateFormatOptions;
55
89
  */
56
90
  declare function formatFileSize(bytes: number): string;
57
91
 
58
- export { formatFileSize, getDateFormatParams };
92
+ export { formatDate, formatFileSize, getDateFormatParams, parseTemplate };
59
93
  export type { DateFormatOptions };
@@ -35,6 +35,40 @@ interface DateFormatOptions {
35
35
  * ```
36
36
  */
37
37
  declare function getDateFormatParams(date?: Date): DateFormatOptions;
38
+ /**
39
+ * 替换模板字符串中的变量占位符
40
+ *
41
+ * @param template - 包含 `{{key}}` 占位符的模板字符串
42
+ * @param values - 占位符键值映射,支持合并多组变量(后者覆盖前者)
43
+ * @returns 替换占位符后的字符串
44
+ *
45
+ * @description 将模板中的 `{{key}}` 占位符替换为对应的值。
46
+ * 键名中的正则特殊字符会被自动转义,值中的 `$` 也会被安全处理。
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * parseTemplate('Hello {{name}}!', { name: 'World' })
51
+ * // 'Hello World!'
52
+ *
53
+ * parseTemplate('{{YYYY}}-{{MM}}-{{DD}}', getDateFormatParams())
54
+ * // '2026-06-06'
55
+ * ```
56
+ */
57
+ declare function parseTemplate(template: string, values: Record<string, string>): string;
58
+ /**
59
+ * 格式化日期字符串
60
+ *
61
+ * @param date - 日期对象
62
+ * @param format - 格式字符串,支持 `{YYYY}`、`{MM}`、`{DD}`、`{HH}`、`{mm}`、`{ss}` 等占位符
63
+ * @returns 格式化后的日期字符串
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * formatDate(new Date(), '{YYYY}-{MM}-{DD}T{HH}:{mm}:{ss}')
68
+ * // '2026-06-06T15:30:00'
69
+ * ```
70
+ */
71
+ declare function formatDate(date: Date, format: string): string;
38
72
  /**
39
73
  * 将字节数格式化为人类可读的文件大小字符串
40
74
  *
@@ -55,5 +89,5 @@ declare function getDateFormatParams(date?: Date): DateFormatOptions;
55
89
  */
56
90
  declare function formatFileSize(bytes: number): string;
57
91
 
58
- export { formatFileSize, getDateFormatParams };
92
+ export { formatDate, formatFileSize, getDateFormatParams, parseTemplate };
59
93
  export type { DateFormatOptions };
@@ -35,6 +35,40 @@ interface DateFormatOptions {
35
35
  * ```
36
36
  */
37
37
  declare function getDateFormatParams(date?: Date): DateFormatOptions;
38
+ /**
39
+ * 替换模板字符串中的变量占位符
40
+ *
41
+ * @param template - 包含 `{{key}}` 占位符的模板字符串
42
+ * @param values - 占位符键值映射,支持合并多组变量(后者覆盖前者)
43
+ * @returns 替换占位符后的字符串
44
+ *
45
+ * @description 将模板中的 `{{key}}` 占位符替换为对应的值。
46
+ * 键名中的正则特殊字符会被自动转义,值中的 `$` 也会被安全处理。
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * parseTemplate('Hello {{name}}!', { name: 'World' })
51
+ * // 'Hello World!'
52
+ *
53
+ * parseTemplate('{{YYYY}}-{{MM}}-{{DD}}', getDateFormatParams())
54
+ * // '2026-06-06'
55
+ * ```
56
+ */
57
+ declare function parseTemplate(template: string, values: Record<string, string>): string;
58
+ /**
59
+ * 格式化日期字符串
60
+ *
61
+ * @param date - 日期对象
62
+ * @param format - 格式字符串,支持 `{YYYY}`、`{MM}`、`{DD}`、`{HH}`、`{mm}`、`{ss}` 等占位符
63
+ * @returns 格式化后的日期字符串
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * formatDate(new Date(), '{YYYY}-{MM}-{DD}T{HH}:{mm}:{ss}')
68
+ * // '2026-06-06T15:30:00'
69
+ * ```
70
+ */
71
+ declare function formatDate(date: Date, format: string): string;
38
72
  /**
39
73
  * 将字节数格式化为人类可读的文件大小字符串
40
74
  *
@@ -55,5 +89,5 @@ declare function getDateFormatParams(date?: Date): DateFormatOptions;
55
89
  */
56
90
  declare function formatFileSize(bytes: number): string;
57
91
 
58
- export { formatFileSize, getDateFormatParams };
92
+ export { formatDate, formatFileSize, getDateFormatParams, parseTemplate };
59
93
  export type { DateFormatOptions };
@@ -1 +1 @@
1
- function t(e,r=2){return e.toString().padStart(r,"0")}function n(e=new Date){return{YYYY:e.getFullYear().toString(),YY:e.getFullYear().toString().slice(-2),MM:t(e.getMonth()+1),DD:t(e.getDate()),HH:t(e.getHours()),mm:t(e.getMinutes()),ss:t(e.getSeconds()),SSS:t(e.getMilliseconds(),3),timestamp:e.getTime().toString()}}function i(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(2)}MB`}export{i as formatFileSize,n as getDateFormatParams};
1
+ function e(t,n=2){return t.toString().padStart(n,"0")}function g(t=new Date){return{YYYY:t.getFullYear().toString(),YY:t.getFullYear().toString().slice(-2),MM:e(t.getMonth()+1),DD:e(t.getDate()),HH:e(t.getHours()),mm:e(t.getMinutes()),ss:e(t.getSeconds()),SSS:e(t.getMilliseconds(),3),timestamp:t.getTime().toString()}}function c(t,n){let r=t;for(const[o,i]of Object.entries(n)){const a=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),s=i.replace(/\$/g,"$$$$");r=r.replace(new RegExp(`\\{\\{${a}\\}\\}`,"g"),s)}return r}function l(t,n){const r=g(t);let o=n;for(const[i,a]of Object.entries(r))o=o.replace(new RegExp(`\\{${i}\\}`,"g"),a);return o}function u(t){return t<1024?`${t}B`:t<1024*1024?`${(t/1024).toFixed(1)}KB`:`${(t/(1024*1024)).toFixed(2)}MB`}export{l as formatDate,u as formatFileSize,g as getDateFormatParams,c as parseTemplate};
@@ -1 +1 @@
1
- "use strict";const n=require("fs"),m=require("path");function _interopDefaultCompat(t){return t&&typeof t=="object"&&"default"in t?t.default:t}const n__default=_interopDefaultCompat(n),m__default=_interopDefaultCompat(m),R=10;async function checkSourceExists(t){try{await n__default.promises.access(t,n__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 - ${t}`):e.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u6E90\u6587\u4EF6 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u68C0\u67E5\u6E90\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function h(t){try{await n__default.promises.mkdir(t,{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 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u521B\u5EFA\u76EE\u6807\u76EE\u5F55\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function C(t,u){const e=await n__default.promises.readdir(t,{withFileTypes:!0}),s=[];for(const o of e){const a=m__default.join(t,o.name),F=o.isFile(),c=o.isDirectory();if(s.push({path:a,isFile:F,isDirectory:c}),c&&u){const l=await C(a,u);s.push(...l)}}return s}async function S(t,u){try{const[e,s]=await Promise.all([n__default.promises.stat(t),n__default.promises.stat(u)]);return e.mtimeMs>s.mtimeMs||e.size!==s.size}catch{return!0}}async function D(t){try{return await n__default.promises.access(t,n__default.constants.F_OK),!0}catch{return!1}}async function T(t,u,e){const s=[];let o=0;async function a(){for(;o<t.length;){const c=o++,l=await u(t[c]);s[c]=l}}const F=Array(Math.min(e,t.length)).fill(null).map(()=>a());return await Promise.all(F),s}async function copySourceToTarget(t,u,e){const s=Date.now(),{recursive:o,overwrite:a,incremental:F=!1,parallelLimit:c=R}=e;let l=0,E=0,p=0;if((await n__default.promises.stat(t)).isDirectory()){await h(u);const r=await C(t,o),w=r.filter(i=>i.isFile);p=r.filter(i=>i.isDirectory).length;const x=new Set;for(const i of w){const A=m__default.relative(t,i.path),d=m__default.dirname(m__default.join(u,A));x.add(d)}await Promise.all([...x].map(i=>h(i)));const g=await T(w,async i=>{const A=m__default.relative(t,i.path),d=m__default.join(u,A);let y=a;return y||(y=!await D(d)),F&&y&&(y=await S(i.path,d)),y?(await n__default.promises.copyFile(i.path,d),{copied:!0,skipped:!1}):{copied:!1,skipped:!0}},c);for(const i of g)i.copied&&l++,i.skipped&&E++}else{await h(m__default.dirname(u));let r=a;r||(r=!await D(u)),F&&r&&(r=await S(t,u)),r?(await n__default.promises.copyFile(t,u),l++):E++}const f=Date.now()-s;return{copiedFiles:l,skippedFiles:E,copiedDirs:p,executionTime:f}}async function writeFileContent(t,u){try{await n__default.promises.writeFile(t,u,"utf-8")}catch(e){const s=e;throw s.code==="EACCES"?new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u5199\u5165\u6587\u4EF6 - ${t}`):new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u5199\u5165\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${s.message}`)}}async function scanDirectory(t,u={}){const{includeExtensions:e=[],excludePatterns:s=[],filter:o}=u,a=[];async function F(c){const l=await n__default.promises.readdir(c,{withFileTypes:!0});for(const E of l){const p=m__default.join(c,E.name);if(E.isDirectory()){await F(p);continue}if(!E.isFile()||s.some(w=>w.startsWith("*")?p.endsWith(w.slice(1)):p.includes(w)))continue;const f=m__default.extname(E.name).toLowerCase();if(e.length>0&&!e.includes(f))continue;const r=await n__default.promises.stat(p);o&&!o(p,f,r.size)||a.push({filePath:p,size:r.size,extension:f})}}return await F(t),a}async function writeJsonReport(t,u,e=2){await writeFileContent(t,JSON.stringify(u,null,e))}exports.checkSourceExists=checkSourceExists,exports.copySourceToTarget=copySourceToTarget,exports.scanDirectory=scanDirectory,exports.writeFileContent=writeFileContent,exports.writeJsonReport=writeJsonReport;
1
+ "use strict";const s=require("fs"),u=require("path");function _interopDefaultCompat(t){return t&&typeof t=="object"&&"default"in t?t.default:t}const s__default=_interopDefaultCompat(s),u__default=_interopDefaultCompat(u),R=10;async function checkSourceExists(t){try{await s__default.promises.access(t,s__default.constants.F_OK)}catch(e){const i=e;throw i.code==="ENOENT"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728 - ${t}`):i.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u6E90\u6587\u4EF6 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u68C0\u67E5\u6E90\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${i.message}`)}}async function g(t){try{await s__default.promises.mkdir(t,{recursive:!0})}catch(e){const i=e;throw i.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u521B\u5EFA\u76EE\u6807\u76EE\u5F55 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u521B\u5EFA\u76EE\u6807\u76EE\u5F55\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${i.message}`)}}async function h(t,e){const i=await s__default.promises.readdir(t,{withFileTypes:!0}),r=[];for(const o of i){const c=u__default.join(t,o.name),l=o.isFile(),F=o.isDirectory();if(r.push({path:c,isFile:l,isDirectory:F}),F&&e){const p=await h(c,e);r.push(...p)}}return r}async function C(t,e){try{const[i,r]=await Promise.all([s__default.promises.stat(t),s__default.promises.stat(e)]);return i.mtimeMs>r.mtimeMs||i.size!==r.size}catch{return!0}}async function D(t){try{return await s__default.promises.access(t,s__default.constants.F_OK),!0}catch{return!1}}async function T(t,e,i){const r=[];let o=0;async function c(){for(;o<t.length;){const F=o++,p=await e(t[F]);r[F]=p}}const l=Array(Math.min(i,t.length)).fill(null).map(()=>c());return await Promise.all(l),r}async function copySourceToTarget(t,e,i){const r=Date.now(),{recursive:o,overwrite:c,incremental:l=!1,parallelLimit:F=R}=i;let p=0,w=0,f=0;if((await s__default.promises.stat(t)).isDirectory()){await g(e);const a=await h(t,o),E=a.filter(n=>n.isFile);f=a.filter(n=>n.isDirectory).length;const S=new Set;for(const n of E){const A=u__default.relative(t,n.path),d=u__default.dirname(u__default.join(e,A));S.add(d)}await Promise.all([...S].map(n=>g(n)));const x=await T(E,async n=>{const A=u__default.relative(t,n.path),d=u__default.join(e,A);let m=c;return m||(m=!await D(d)),l&&m&&(m=await C(n.path,d)),m?(await s__default.promises.copyFile(n.path,d),{copied:!0,skipped:!1}):{copied:!1,skipped:!0}},F);for(const n of x)n.copied&&p++,n.skipped&&w++}else{await g(u__default.dirname(e));let a=c;a||(a=!await D(e)),l&&a&&(a=await C(t,e)),a?(await s__default.promises.copyFile(t,e),p++):w++}const y=Date.now()-r;return{copiedFiles:p,skippedFiles:w,copiedDirs:f,executionTime:y}}async function writeFileContent(t,e){try{await s__default.promises.writeFile(t,e,"utf-8")}catch(i){const r=i;throw r.code==="EACCES"?new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u5199\u5165\u6587\u4EF6 - ${t}`):new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u5199\u5165\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${r.message}`)}}async function scanDirectory(t,e={}){const{includeExtensions:i=[],excludePatterns:r=[],filter:o}=e,c=[];async function l(F){const p=await s__default.promises.readdir(F,{withFileTypes:!0});for(const w of p){const f=u__default.join(F,w.name);if(w.isDirectory()){await l(f);continue}if(!w.isFile()||r.some(E=>E.startsWith("*")?f.endsWith(E.slice(1)):f.includes(E)))continue;const y=u__default.extname(w.name).toLowerCase();if(i.length>0&&!i.includes(y))continue;const a=await s__default.promises.stat(f);o&&!o(f,y,a.size)||c.push({filePath:f,size:a.size,extension:y})}}return await l(t),c}async function writeJsonReport(t,e,i=2){await writeFileContent(t,JSON.stringify(e,null,i))}function writeFileSyncSafely(t,e){const i=u__default.dirname(t);s__default.existsSync(i)||s__default.mkdirSync(i,{recursive:!0}),s__default.writeFileSync(t,e,"utf-8")}function shouldUpdateFileContent(t,e){if(!s__default.existsSync(t))return!0;try{return s__default.readFileSync(t,"utf-8")!==e}catch{return!0}}exports.checkSourceExists=checkSourceExists,exports.copySourceToTarget=copySourceToTarget,exports.scanDirectory=scanDirectory,exports.shouldUpdateFileContent=shouldUpdateFileContent,exports.writeFileContent=writeFileContent,exports.writeFileSyncSafely=writeFileSyncSafely,exports.writeJsonReport=writeJsonReport;
@@ -125,6 +125,41 @@ declare function scanDirectory(dirPath: string, options?: ScanDirectoryOptions):
125
125
  * ```
126
126
  */
127
127
  declare function writeJsonReport(filePath: string, data: object, indent?: number): Promise<void>;
128
+ /**
129
+ * 同步写入文件内容,自动创建不存在的目录
130
+ *
131
+ * @param filePath 文件路径
132
+ * @param content 文件内容
133
+ *
134
+ * @description 同步写入文件,如果目标目录不存在会自动递归创建。
135
+ * 适用于构建钩子中需要同步写入的场景(如 `transform` 钩子)。
136
+ *
137
+ * @throws 当文件写入失败时(如权限不足),抛出 `NodeJS.ErrnoException`
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * writeFileSyncSafely('/project/src/auto-imports.d.ts', 'declare global { ... }')
142
+ * ```
143
+ */
144
+ declare function writeFileSyncSafely(filePath: string, content: string): void;
145
+ /**
146
+ * 检查文件内容是否需要更新(同步版本)
147
+ *
148
+ * @param filePath 文件路径
149
+ * @param newContent 新生成的文件内容
150
+ * @returns 如果需要更新返回 `true`,否则返回 `false`
151
+ *
152
+ * @description 对比现有文件内容与新生成的内容,
153
+ * 仅在内容发生变化时才需要写入,减少不必要的文件 IO 操作。
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * if (shouldUpdateFileContent('/project/src/auto-imports.d.ts', newContent)) {
158
+ * writeFileSyncSafely('/project/src/auto-imports.d.ts', newContent)
159
+ * }
160
+ * ```
161
+ */
162
+ declare function shouldUpdateFileContent(filePath: string, newContent: string): boolean;
128
163
 
129
- export { checkSourceExists, copySourceToTarget, scanDirectory, writeFileContent, writeJsonReport };
164
+ export { checkSourceExists, copySourceToTarget, scanDirectory, shouldUpdateFileContent, writeFileContent, writeFileSyncSafely, writeJsonReport };
130
165
  export type { CopyOptions, CopyResult, ScanDirectoryOptions, ScannedFile };
@@ -125,6 +125,41 @@ declare function scanDirectory(dirPath: string, options?: ScanDirectoryOptions):
125
125
  * ```
126
126
  */
127
127
  declare function writeJsonReport(filePath: string, data: object, indent?: number): Promise<void>;
128
+ /**
129
+ * 同步写入文件内容,自动创建不存在的目录
130
+ *
131
+ * @param filePath 文件路径
132
+ * @param content 文件内容
133
+ *
134
+ * @description 同步写入文件,如果目标目录不存在会自动递归创建。
135
+ * 适用于构建钩子中需要同步写入的场景(如 `transform` 钩子)。
136
+ *
137
+ * @throws 当文件写入失败时(如权限不足),抛出 `NodeJS.ErrnoException`
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * writeFileSyncSafely('/project/src/auto-imports.d.ts', 'declare global { ... }')
142
+ * ```
143
+ */
144
+ declare function writeFileSyncSafely(filePath: string, content: string): void;
145
+ /**
146
+ * 检查文件内容是否需要更新(同步版本)
147
+ *
148
+ * @param filePath 文件路径
149
+ * @param newContent 新生成的文件内容
150
+ * @returns 如果需要更新返回 `true`,否则返回 `false`
151
+ *
152
+ * @description 对比现有文件内容与新生成的内容,
153
+ * 仅在内容发生变化时才需要写入,减少不必要的文件 IO 操作。
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * if (shouldUpdateFileContent('/project/src/auto-imports.d.ts', newContent)) {
158
+ * writeFileSyncSafely('/project/src/auto-imports.d.ts', newContent)
159
+ * }
160
+ * ```
161
+ */
162
+ declare function shouldUpdateFileContent(filePath: string, newContent: string): boolean;
128
163
 
129
- export { checkSourceExists, copySourceToTarget, scanDirectory, writeFileContent, writeJsonReport };
164
+ export { checkSourceExists, copySourceToTarget, scanDirectory, shouldUpdateFileContent, writeFileContent, writeFileSyncSafely, writeJsonReport };
130
165
  export type { CopyOptions, CopyResult, ScanDirectoryOptions, ScannedFile };
@@ -125,6 +125,41 @@ declare function scanDirectory(dirPath: string, options?: ScanDirectoryOptions):
125
125
  * ```
126
126
  */
127
127
  declare function writeJsonReport(filePath: string, data: object, indent?: number): Promise<void>;
128
+ /**
129
+ * 同步写入文件内容,自动创建不存在的目录
130
+ *
131
+ * @param filePath 文件路径
132
+ * @param content 文件内容
133
+ *
134
+ * @description 同步写入文件,如果目标目录不存在会自动递归创建。
135
+ * 适用于构建钩子中需要同步写入的场景(如 `transform` 钩子)。
136
+ *
137
+ * @throws 当文件写入失败时(如权限不足),抛出 `NodeJS.ErrnoException`
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * writeFileSyncSafely('/project/src/auto-imports.d.ts', 'declare global { ... }')
142
+ * ```
143
+ */
144
+ declare function writeFileSyncSafely(filePath: string, content: string): void;
145
+ /**
146
+ * 检查文件内容是否需要更新(同步版本)
147
+ *
148
+ * @param filePath 文件路径
149
+ * @param newContent 新生成的文件内容
150
+ * @returns 如果需要更新返回 `true`,否则返回 `false`
151
+ *
152
+ * @description 对比现有文件内容与新生成的内容,
153
+ * 仅在内容发生变化时才需要写入,减少不必要的文件 IO 操作。
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * if (shouldUpdateFileContent('/project/src/auto-imports.d.ts', newContent)) {
158
+ * writeFileSyncSafely('/project/src/auto-imports.d.ts', newContent)
159
+ * }
160
+ * ```
161
+ */
162
+ declare function shouldUpdateFileContent(filePath: string, newContent: string): boolean;
128
163
 
129
- export { checkSourceExists, copySourceToTarget, scanDirectory, writeFileContent, writeJsonReport };
164
+ export { checkSourceExists, copySourceToTarget, scanDirectory, shouldUpdateFileContent, writeFileContent, writeFileSyncSafely, writeJsonReport };
130
165
  export type { CopyOptions, CopyResult, ScanDirectoryOptions, ScannedFile };
@@ -1 +1 @@
1
- import n from"fs";import p from"path";const S=10;async function T(t){try{await n.promises.access(t,n.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 - ${t}`):e.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u6E90\u6587\u4EF6 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u68C0\u67E5\u6E90\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function D(t){try{await n.promises.mkdir(t,{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 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u521B\u5EFA\u76EE\u6807\u76EE\u5F55\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${e.message}`)}}async function C(t,u){const e=await n.promises.readdir(t,{withFileTypes:!0}),i=[];for(const o of e){const r=p.join(t,o.name),F=o.isFile(),c=o.isDirectory();if(i.push({path:r,isFile:F,isDirectory:c}),c&&u){const l=await C(r,u);i.push(...l)}}return i}async function g(t,u){try{const[e,i]=await Promise.all([n.promises.stat(t),n.promises.stat(u)]);return e.mtimeMs>i.mtimeMs||e.size!==i.size}catch{return!0}}async function $(t){try{return await n.promises.access(t,n.constants.F_OK),!0}catch{return!1}}async function k(t,u,e){const i=[];let o=0;async function r(){for(;o<t.length;){const c=o++,l=await u(t[c]);i[c]=l}}const F=Array(Math.min(e,t.length)).fill(null).map(()=>r());return await Promise.all(F),i}async function v(t,u,e){const i=Date.now(),{recursive:o,overwrite:r,incremental:F=!1,parallelLimit:c=S}=e;let l=0,w=0,E=0;if((await n.promises.stat(t)).isDirectory()){await D(u);const a=await C(t,o),f=a.filter(s=>s.isFile);E=a.filter(s=>s.isDirectory).length;const A=new Set;for(const s of f){const h=p.relative(t,s.path),y=p.dirname(p.join(u,h));A.add(y)}await Promise.all([...A].map(s=>D(s)));const B=await k(f,async s=>{const h=p.relative(t,s.path),y=p.join(u,h);let d=r;return d||(d=!await $(y)),F&&d&&(d=await g(s.path,y)),d?(await n.promises.copyFile(s.path,y),{copied:!0,skipped:!1}):{copied:!1,skipped:!0}},c);for(const s of B)s.copied&&l++,s.skipped&&w++}else{await D(p.dirname(u));let a=r;a||(a=!await $(u)),F&&a&&(a=await g(t,u)),a?(await n.promises.copyFile(t,u),l++):w++}const m=Date.now()-i;return{copiedFiles:l,skippedFiles:w,copiedDirs:E,executionTime:m}}async function x(t,u){try{await n.promises.writeFile(t,u,"utf-8")}catch(e){const i=e;throw i.code==="EACCES"?new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u5199\u5165\u6587\u4EF6 - ${t}`):new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u5199\u5165\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${i.message}`)}}async function z(t,u={}){const{includeExtensions:e=[],excludePatterns:i=[],filter:o}=u,r=[];async function F(c){const l=await n.promises.readdir(c,{withFileTypes:!0});for(const w of l){const E=p.join(c,w.name);if(w.isDirectory()){await F(E);continue}if(!w.isFile()||i.some(f=>f.startsWith("*")?E.endsWith(f.slice(1)):E.includes(f)))continue;const m=p.extname(w.name).toLowerCase();if(e.length>0&&!e.includes(m))continue;const a=await n.promises.stat(E);o&&!o(E,m,a.size)||r.push({filePath:E,size:a.size,extension:m})}}return await F(t),r}async function P(t,u,e=2){await x(t,JSON.stringify(u,null,e))}export{T as checkSourceExists,v as copySourceToTarget,z as scanDirectory,x as writeFileContent,P as writeJsonReport};
1
+ import e from"fs";import F from"path";const B=10;async function k(t){try{await e.promises.access(t,e.constants.F_OK)}catch(u){const i=u;throw i.code==="ENOENT"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728 - ${t}`):i.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u6E90\u6587\u4EF6 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u68C0\u67E5\u6E90\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${i.message}`)}}async function D(t){try{await e.promises.mkdir(t,{recursive:!0})}catch(u){const i=u;throw i.code==="EACCES"?new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u521B\u5EFA\u76EE\u6807\u76EE\u5F55 - ${t}`):new Error(`\u590D\u5236\u6587\u4EF6\u5931\u8D25\uFF1A\u521B\u5EFA\u76EE\u6807\u76EE\u5F55\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${i.message}`)}}async function C(t,u){const i=await e.promises.readdir(t,{withFileTypes:!0}),r=[];for(const n of i){const a=F.join(t,n.name),l=n.isFile(),c=n.isDirectory();if(r.push({path:a,isFile:l,isDirectory:c}),c&&u){const p=await C(a,u);r.push(...p)}}return r}async function S(t,u){try{const[i,r]=await Promise.all([e.promises.stat(t),e.promises.stat(u)]);return i.mtimeMs>r.mtimeMs||i.size!==r.size}catch{return!0}}async function x(t){try{return await e.promises.access(t,e.constants.F_OK),!0}catch{return!1}}async function v(t,u,i){const r=[];let n=0;async function a(){for(;n<t.length;){const c=n++,p=await u(t[c]);r[c]=p}}const l=Array(Math.min(i,t.length)).fill(null).map(()=>a());return await Promise.all(l),r}async function T(t,u,i){const r=Date.now(),{recursive:n,overwrite:a,incremental:l=!1,parallelLimit:c=B}=i;let p=0,E=0,w=0;if((await e.promises.stat(t)).isDirectory()){await D(u);const o=await C(t,n),f=o.filter(s=>s.isFile);w=o.filter(s=>s.isDirectory).length;const A=new Set;for(const s of f){const h=F.relative(t,s.path),y=F.dirname(F.join(u,h));A.add(y)}await Promise.all([...A].map(s=>D(s)));const $=await v(f,async s=>{const h=F.relative(t,s.path),y=F.join(u,h);let d=a;return d||(d=!await x(y)),l&&d&&(d=await S(s.path,y)),d?(await e.promises.copyFile(s.path,y),{copied:!0,skipped:!1}):{copied:!1,skipped:!0}},c);for(const s of $)s.copied&&p++,s.skipped&&E++}else{await D(F.dirname(u));let o=a;o||(o=!await x(u)),l&&o&&(o=await S(t,u)),o?(await e.promises.copyFile(t,u),p++):E++}const m=Date.now()-r;return{copiedFiles:p,skippedFiles:E,copiedDirs:w,executionTime:m}}async function g(t,u){try{await e.promises.writeFile(t,u,"utf-8")}catch(i){const r=i;throw r.code==="EACCES"?new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u5199\u5165\u6587\u4EF6 - ${t}`):new Error(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A\u5199\u5165\u6587\u4EF6\u65F6\u51FA\u9519 - ${t}\uFF0C\u9519\u8BEF\uFF1A${r.message}`)}}async function z(t,u={}){const{includeExtensions:i=[],excludePatterns:r=[],filter:n}=u,a=[];async function l(c){const p=await e.promises.readdir(c,{withFileTypes:!0});for(const E of p){const w=F.join(c,E.name);if(E.isDirectory()){await l(w);continue}if(!E.isFile()||r.some(f=>f.startsWith("*")?w.endsWith(f.slice(1)):w.includes(f)))continue;const m=F.extname(E.name).toLowerCase();if(i.length>0&&!i.includes(m))continue;const o=await e.promises.stat(w);n&&!n(w,m,o.size)||a.push({filePath:w,size:o.size,extension:m})}}return await l(t),a}async function P(t,u,i=2){await g(t,JSON.stringify(u,null,i))}function j(t,u){const i=F.dirname(t);e.existsSync(i)||e.mkdirSync(i,{recursive:!0}),e.writeFileSync(t,u,"utf-8")}function O(t,u){if(!e.existsSync(t))return!0;try{return e.readFileSync(t,"utf-8")!==u}catch{return!0}}export{k as checkSourceExists,T as copySourceToTarget,z as scanDirectory,O as shouldUpdateFileContent,g as writeFileContent,j as writeFileSyncSafely,P as writeJsonReport};
@@ -1,2 +1,2 @@
1
- "use strict";const security=require("../../shared/vite-plugin.CnOy46d3.cjs");function injectBeforeTag(e,t,s){const n=new RegExp(t,"i");return n.test(e)?{html:e.replace(n,`${s}
2
- ${t}`),injected:!0}:{html:e,injected:!1}}function c(e,t,s){const n=injectBeforeTag(e,"</body>",t);if(n.injected)return{...n,usedFallback:!1};const i=injectBeforeTag(e,"</html>",t);return i.injected?{...i,usedFallback:!1}:{html:e+t,injected:!0,usedFallback:!0}}function injectHeadAndBody(e,t,s){let n=e,i=!1;if(t){const d=injectBeforeTag(n,"</head>",t);d.injected&&(n=d.html,i=!0)}const l=c(n,s);return{html:l.html,headInjected:i,bodyInjected:l.injected,usedFallback:l.usedFallback}}exports.sanitizeContent=security.sanitizeContent,exports.injectBeforeTag=injectBeforeTag,exports.injectHeadAndBody=injectHeadAndBody;
1
+ "use strict";const security=require("../../shared/vite-plugin.CcvHfrL8.cjs");function injectBeforeTag(e,t,l){const n=new RegExp(t,"i");return n.test(e)?{html:e.replace(n,`${l}
2
+ ${t}`),injected:!0}:{html:e,injected:!1}}function c(e,t,l){const n=injectBeforeTag(e,"</body>",t);if(n.injected)return{...n,usedFallback:!1};const s=injectBeforeTag(e,"</html>",t);return s.injected?{...s,usedFallback:!1}:{html:e+t,injected:!0,usedFallback:!0}}function injectHeadAndBody(e,t,l){let n=e,s=!1;if(t){const r=injectBeforeTag(n,"</head>",t);r.injected&&(n=r.html,s=!0)}const i=c(n,l);return{html:i.html,headInjected:s,bodyInjected:i.injected,usedFallback:i.usedFallback}}exports.escapeHtmlAttr=security.escapeHtmlAttr,exports.sanitizeContent=security.sanitizeContent,exports.injectBeforeTag=injectBeforeTag,exports.injectHeadAndBody=injectHeadAndBody;
@@ -1,6 +1,22 @@
1
1
  import { S as SecurityConfig, H as HtmlInjectResult, D as DualInjectResult } from '../../shared/vite-plugin.BI9taN75.cjs';
2
2
  export { C as ConditionType, I as InjectCondition, a as InjectPosition, b as SelectorMatch } from '../../shared/vite-plugin.BI9taN75.cjs';
3
3
 
4
+ /**
5
+ * 转义 HTML 属性值中的特殊字符
6
+ *
7
+ * @param str - 需要转义的字符串
8
+ * @returns 转义后的安全字符串
9
+ *
10
+ * @description 将字符串中的 `&`、`"`、`'`、`<`、`>` 转义为对应的 HTML 实体,
11
+ * 防止在 HTML 属性值中注入恶意代码。
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * escapeHtmlAttr('hello "world" & <friends>')
16
+ * // 'hello &quot;world&quot; &amp; &lt;friends&gt;'
17
+ * ```
18
+ */
19
+ declare function escapeHtmlAttr(str: string): string;
4
20
  /**
5
21
  * 内容消毒规则选项
6
22
  */
@@ -43,5 +59,5 @@ declare function injectBeforeTag(html: string, tag: string, code: string): HtmlI
43
59
  */
44
60
  declare function injectHeadAndBody(html: string, headCode: string | undefined, bodyCode: string): DualInjectResult;
45
61
 
46
- export { DualInjectResult, HtmlInjectResult, SecurityConfig, injectBeforeTag, injectHeadAndBody, sanitizeContent };
62
+ export { DualInjectResult, HtmlInjectResult, SecurityConfig, escapeHtmlAttr, injectBeforeTag, injectHeadAndBody, sanitizeContent };
47
63
  export type { SanitizeRuleOptions };
@@ -1,6 +1,22 @@
1
1
  import { S as SecurityConfig, H as HtmlInjectResult, D as DualInjectResult } from '../../shared/vite-plugin.BI9taN75.mjs';
2
2
  export { C as ConditionType, I as InjectCondition, a as InjectPosition, b as SelectorMatch } from '../../shared/vite-plugin.BI9taN75.mjs';
3
3
 
4
+ /**
5
+ * 转义 HTML 属性值中的特殊字符
6
+ *
7
+ * @param str - 需要转义的字符串
8
+ * @returns 转义后的安全字符串
9
+ *
10
+ * @description 将字符串中的 `&`、`"`、`'`、`<`、`>` 转义为对应的 HTML 实体,
11
+ * 防止在 HTML 属性值中注入恶意代码。
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * escapeHtmlAttr('hello "world" & <friends>')
16
+ * // 'hello &quot;world&quot; &amp; &lt;friends&gt;'
17
+ * ```
18
+ */
19
+ declare function escapeHtmlAttr(str: string): string;
4
20
  /**
5
21
  * 内容消毒规则选项
6
22
  */
@@ -43,5 +59,5 @@ declare function injectBeforeTag(html: string, tag: string, code: string): HtmlI
43
59
  */
44
60
  declare function injectHeadAndBody(html: string, headCode: string | undefined, bodyCode: string): DualInjectResult;
45
61
 
46
- export { DualInjectResult, HtmlInjectResult, SecurityConfig, injectBeforeTag, injectHeadAndBody, sanitizeContent };
62
+ export { DualInjectResult, HtmlInjectResult, SecurityConfig, escapeHtmlAttr, injectBeforeTag, injectHeadAndBody, sanitizeContent };
47
63
  export type { SanitizeRuleOptions };
@@ -1,6 +1,22 @@
1
1
  import { S as SecurityConfig, H as HtmlInjectResult, D as DualInjectResult } from '../../shared/vite-plugin.BI9taN75.js';
2
2
  export { C as ConditionType, I as InjectCondition, a as InjectPosition, b as SelectorMatch } from '../../shared/vite-plugin.BI9taN75.js';
3
3
 
4
+ /**
5
+ * 转义 HTML 属性值中的特殊字符
6
+ *
7
+ * @param str - 需要转义的字符串
8
+ * @returns 转义后的安全字符串
9
+ *
10
+ * @description 将字符串中的 `&`、`"`、`'`、`<`、`>` 转义为对应的 HTML 实体,
11
+ * 防止在 HTML 属性值中注入恶意代码。
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * escapeHtmlAttr('hello "world" & <friends>')
16
+ * // 'hello &quot;world&quot; &amp; &lt;friends&gt;'
17
+ * ```
18
+ */
19
+ declare function escapeHtmlAttr(str: string): string;
4
20
  /**
5
21
  * 内容消毒规则选项
6
22
  */
@@ -43,5 +59,5 @@ declare function injectBeforeTag(html: string, tag: string, code: string): HtmlI
43
59
  */
44
60
  declare function injectHeadAndBody(html: string, headCode: string | undefined, bodyCode: string): DualInjectResult;
45
61
 
46
- export { DualInjectResult, HtmlInjectResult, SecurityConfig, injectBeforeTag, injectHeadAndBody, sanitizeContent };
62
+ export { DualInjectResult, HtmlInjectResult, SecurityConfig, escapeHtmlAttr, injectBeforeTag, injectHeadAndBody, sanitizeContent };
47
63
  export type { SanitizeRuleOptions };
@@ -1,2 +1,2 @@
1
- export{s as sanitizeContent}from"../../shared/vite-plugin.BPFqtmWa.mjs";function a(e,t,l){const n=new RegExp(t,"i");return n.test(e)?{html:e.replace(n,`${l}
2
- ${t}`),injected:!0}:{html:e,injected:!1}}function i(e,t,l){const n=a(e,"</body>",t);if(n.injected)return{...n,usedFallback:!1};const c=a(e,"</html>",t);return c.injected?{...c,usedFallback:!1}:{html:e+t,injected:!0,usedFallback:!0}}function r(e,t,l){let n=e,c=!1;if(t){const s=a(n,"</head>",t);s.injected&&(n=s.html,c=!0)}const d=i(n,l);return{html:d.html,headInjected:c,bodyInjected:d.injected,usedFallback:d.usedFallback}}export{a as injectBeforeTag,r as injectHeadAndBody};
1
+ export{e as escapeHtmlAttr,s as sanitizeContent}from"../../shared/vite-plugin.CuXEJAWX.mjs";function a(e,t,l){const n=new RegExp(t,"i");return n.test(e)?{html:e.replace(n,`${l}
2
+ ${t}`),injected:!0}:{html:e,injected:!1}}function r(e,t,l){const n=a(e,"</body>",t);if(n.injected)return{...n,usedFallback:!1};const c=a(e,"</html>",t);return c.injected?{...c,usedFallback:!1}:{html:e+t,injected:!0,usedFallback:!0}}function i(e,t,l){let n=e,c=!1;if(t){const s=a(n,"</head>",t);s.injected&&(n=s.html,c=!0)}const d=r(n,l);return{html:d.html,headInjected:c,bodyInjected:d.injected,usedFallback:d.usedFallback}}export{a as injectBeforeTag,i as injectHeadAndBody};