@meng-xi/vite-plugin 0.1.1 → 0.1.3

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 (55) hide show
  1. package/README-en.md +522 -610
  2. package/README.md +496 -584
  3. package/dist/common/compress/index.cjs +1 -0
  4. package/dist/common/compress/index.d.cts +23 -0
  5. package/dist/common/compress/index.d.mts +23 -0
  6. package/dist/common/compress/index.d.ts +23 -0
  7. package/dist/common/compress/index.mjs +1 -0
  8. package/dist/common/format/index.cjs +1 -1
  9. package/dist/common/format/index.d.cts +33 -1
  10. package/dist/common/format/index.d.mts +33 -1
  11. package/dist/common/format/index.d.ts +33 -1
  12. package/dist/common/format/index.mjs +1 -1
  13. package/dist/common/fs/index.cjs +1 -1
  14. package/dist/common/fs/index.d.cts +70 -2
  15. package/dist/common/fs/index.d.mts +70 -2
  16. package/dist/common/fs/index.d.ts +70 -2
  17. package/dist/common/fs/index.mjs +1 -1
  18. package/dist/common/index.cjs +1 -1
  19. package/dist/common/index.d.cts +4 -2
  20. package/dist/common/index.d.mts +4 -2
  21. package/dist/common/index.d.ts +4 -2
  22. package/dist/common/index.mjs +1 -1
  23. package/dist/common/path/index.cjs +1 -0
  24. package/dist/common/path/index.d.cts +22 -0
  25. package/dist/common/path/index.d.mts +22 -0
  26. package/dist/common/path/index.d.ts +22 -0
  27. package/dist/common/path/index.mjs +1 -0
  28. package/dist/index.cjs +1 -1
  29. package/dist/index.d.cts +6 -2
  30. package/dist/index.d.mts +6 -2
  31. package/dist/index.d.ts +6 -2
  32. package/dist/index.mjs +1 -1
  33. package/dist/plugins/bundleAnalyzer/index.cjs +235 -0
  34. package/dist/plugins/bundleAnalyzer/index.d.cts +215 -0
  35. package/dist/plugins/bundleAnalyzer/index.d.mts +215 -0
  36. package/dist/plugins/bundleAnalyzer/index.d.ts +215 -0
  37. package/dist/plugins/bundleAnalyzer/index.mjs +235 -0
  38. package/dist/plugins/compressAssets/index.cjs +1 -0
  39. package/dist/plugins/compressAssets/index.d.cts +132 -0
  40. package/dist/plugins/compressAssets/index.d.mts +132 -0
  41. package/dist/plugins/compressAssets/index.d.ts +132 -0
  42. package/dist/plugins/compressAssets/index.mjs +1 -0
  43. package/dist/plugins/generateRouter/index.cjs +4 -4
  44. package/dist/plugins/generateRouter/index.mjs +1 -1
  45. package/dist/plugins/generateVersion/index.cjs +1 -1
  46. package/dist/plugins/generateVersion/index.mjs +1 -1
  47. package/dist/plugins/htmlInject/index.cjs +7 -7
  48. package/dist/plugins/index.cjs +1 -1
  49. package/dist/plugins/index.d.cts +2 -0
  50. package/dist/plugins/index.d.mts +2 -0
  51. package/dist/plugins/index.d.ts +2 -0
  52. package/dist/plugins/index.mjs +1 -1
  53. package/dist/plugins/loadingManager/index.cjs +1 -1
  54. package/dist/plugins/loadingManager/index.mjs +1 -1
  55. package/package.json +24 -2
package/README-en.md CHANGED
@@ -15,9 +15,11 @@
15
15
 
16
16
  ## Features
17
17
 
18
- - **Ready to Use** - Provides 8 practical plugins covering build progress display, file copying, router generation, version management, version update checking, HTML injection, icon injection, and global Loading state
19
- management
20
- - **Plugin Development Framework** - Exports core components like BasePlugin, Logger, Validator for building custom Vite plugins
18
+ - **Ready to Use** - Provides 10 practical plugins covering build progress display, build artifact analysis & compression, file copying, router generation, version management, version update checking, HTML injection,
19
+ icon injection, and global Loading state management
20
+ - **Plugin Development Framework** - Exports core components like BasePlugin, Logger, Validator for building custom Vite plugins that follow conventions
21
+ - **Common Utility Library** - Built-in Common module providing reusable utility functions for formatting, file system, compression, path handling, HTML injection, object operations, script generation, and parameter
22
+ validation
21
23
  - **Complete Lifecycle** - Supports initialization, config resolution, destroy lifecycle management with automatic hook composition
22
24
  - **Type Safe** - Complete TypeScript type definitions with configuration validators ensuring parameter correctness
23
25
  - **Flexible Configuration** - All plugins support detailed configuration to meet diverse scenario requirements
@@ -47,53 +49,22 @@ pnpm add @meng-xi/vite-plugin -D
47
49
 
48
50
  ```typescript
49
51
  import { defineConfig } from 'vite'
50
- import { buildProgress, copyFile, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
52
+ import { buildProgress, bundleAnalyzer, compressAssets, copyFile, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
51
53
 
52
54
  export default defineConfig({
53
55
  plugins: [
54
- // Build progress bar
55
56
  buildProgress(),
56
-
57
- // Copy files
58
- copyFile({
59
- sourceDir: 'src/assets',
60
- targetDir: 'dist/assets'
61
- }),
62
-
63
- // Generate router config (uni-app)
64
- generateRouter({
65
- pagesJsonPath: 'src/pages.json',
66
- outputPath: 'src/router.config.ts'
67
- }),
68
-
69
- // Generate version
70
- generateVersion({
71
- format: 'datetime',
72
- outputType: 'both'
73
- }),
74
-
75
- // Version update checker (works with generateVersion)
57
+ bundleAnalyzer({ outputFormat: 'both', sizeThreshold: 200 }),
58
+ compressAssets({ algorithm: 'gzip' }),
59
+ copyFile({ sourceDir: 'src/assets', targetDir: 'dist/assets' }),
60
+ generateRouter({ pagesJsonPath: 'src/pages.json', outputPath: 'src/router.config.ts' }),
61
+ generateVersion({ format: 'datetime', outputType: 'both' }),
76
62
  versionUpdateChecker(),
77
-
78
- // HTML content injection
79
63
  htmlInject({
80
- rules: [
81
- {
82
- id: 'meta-description',
83
- content: '<meta name="description" content="My App">',
84
- position: 'head-end'
85
- }
86
- ]
64
+ rules: [{ id: 'meta-description', content: '<meta name="description" content="My App">', position: 'head-end' }]
87
65
  }),
88
-
89
- // Inject website icon (supports string shorthand)
90
66
  faviconManager('/assets'),
91
-
92
- // Global Loading state management
93
- loadingManager({
94
- defaultVisible: true,
95
- autoHideOn: 'DOMContentLoaded'
96
- })
67
+ loadingManager({ defaultVisible: true, autoHideOn: 'DOMContentLoaded' })
97
68
  ]
98
69
  })
99
70
  ```
@@ -107,23 +78,25 @@ import type { PluginWithInstance } from '@meng-xi/vite-plugin/factory'
107
78
  import type { GenerateRouterOptions } from '@meng-xi/vite-plugin'
108
79
 
109
80
  const routerPlugin = generateRouter({ watch: true }) as PluginWithInstance<GenerateRouterOptions>
110
-
111
- // Access plugin internals via pluginInstance
112
81
  console.log(routerPlugin.pluginInstance?.options)
113
82
  ```
114
83
 
115
84
  ## Built-in Plugins
116
85
 
117
- | Plugin | Description |
118
- | -------------------- | ----------------------------------------------------------------------------------------------------------------- |
119
- | buildProgress | Real-time build progress bar in terminal, supports bar / spinner / minimal |
120
- | copyFile | Copy files or directories after build, supports incremental copying |
121
- | generateRouter | Auto-generate router config from pages.json (uni-app) |
122
- | generateVersion | Auto-generate version numbers, supports file output and global variable injection |
123
- | versionUpdateChecker | Runtime version update checking with multiple prompt styles and custom callbacks |
124
- | htmlInject | HTML content injection with multiple positions, conditional injection, template variables, and security filtering |
125
- | faviconManager | Manage website favicon links injection into HTML files, supports string shorthand config |
126
- | loadingManager | Global Loading state management with request interception and white-screen Loading |
86
+ | Plugin | Description |
87
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------- |
88
+ | buildProgress | Real-time build progress bar in terminal, supports bar / spinner / minimal |
89
+ | bundleAnalyzer | Build artifact size analysis with JSON/HTML reports, gzip calculation, threshold alerts, and build comparison |
90
+ | compressAssets | Compress build artifacts with gzip / brotli / both, concurrent compression and statistics report |
91
+ | copyFile | Copy files or directories after build, supports incremental copying |
92
+ | generateRouter | Auto-generate router config from pages.json (uni-app) |
93
+ | generateVersion | Auto-generate version numbers, supports file output and global variable injection |
94
+ | versionUpdateChecker | Runtime version update checking with multiple prompt styles and custom callbacks |
95
+ | htmlInject | HTML content injection with multiple positions, conditional injection, template variables, and security filtering |
96
+ | faviconManager | Manage website favicon links injection and file copying, supports string shorthand config |
97
+ | loadingManager | Global Loading state management with request interception, debounce, transition animations, and white-screen Loading |
98
+
99
+ ---
127
100
 
128
101
  ### buildProgress
129
102
 
@@ -155,24 +128,10 @@ Display real-time build progress bar in terminal during Vite build, supporting t
155
128
  | moduleColor | `(text: string) => string` | Module name color |
156
129
 
157
130
  ```typescript
158
- // Default bar format
159
131
  buildProgress()
160
-
161
- // Spinner format
162
132
  buildProgress({ format: 'spinner' })
163
-
164
- // Minimal format
165
133
  buildProgress({ format: 'minimal' })
166
-
167
- // Custom appearance
168
- buildProgress({
169
- width: 40,
170
- completeChar: '■',
171
- incompleteChar: '□',
172
- clearOnComplete: false
173
- })
174
-
175
- // Custom color theme
134
+ buildProgress({ width: 40, completeChar: '■', incompleteChar: '□', clearOnComplete: false })
176
135
  buildProgress({
177
136
  theme: {
178
137
  completeColor: t => `\x1b[32m${t}\x1b[39m`,
@@ -184,6 +143,73 @@ buildProgress({
184
143
  })
185
144
  ```
186
145
 
146
+ ---
147
+
148
+ ### bundleAnalyzer
149
+
150
+ Automatically analyze build artifacts in the output directory after Vite build, generating size statistics, module rankings, file type distribution, and other key metrics, with JSON report and HTML visualization support.
151
+
152
+ **Core Features:**
153
+
154
+ - Scan build output directory, analyze chunks, modules, and asset files
155
+ - Calculate original size and gzip compressed size
156
+ - File type distribution statistics by extension
157
+ - Top N largest modules ranking
158
+ - Size threshold alerts (2x threshold marked as critical)
159
+ - Compare with previous build results and generate diff report
160
+ - HTML reports support treemap / sunburst / list visualization charts
161
+
162
+ | Option | Type | Default | Description |
163
+ | ------------------ | --------------------------------------- | ------------------- | --------------------------------------------------------- |
164
+ | outputFormat | `'json'` \| `'html'` \| `'both'` | `'json'` | Report output format |
165
+ | outputFile | `string` | `'bundle-analysis'` | Report output filename (without extension) |
166
+ | openAnalyzer | `boolean` | `false` | Whether to auto-open browser after generating HTML report |
167
+ | sizeThreshold | `number` | `100` | Size alert threshold (KB) |
168
+ | topModules | `number` | `20` | Top N largest modules ranking count |
169
+ | gzipSize | `boolean` | `true` | Whether to calculate gzip size |
170
+ | excludeNodeModules | `boolean` | `false` | Whether to exclude node_modules modules |
171
+ | excludePatterns | `string[]` | `[]` | File path patterns to exclude |
172
+ | includeExtensions | `string[]` | `[]` | File extensions to include, empty means all |
173
+ | compareWith | `string` \| `null` | `null` | Path to previous analysis report for comparison |
174
+ | defaultChartType | `'treemap'` \| `'sunburst'` \| `'list'` | `'treemap'` | Default chart type in HTML report |
175
+
176
+ ```typescript
177
+ bundleAnalyzer()
178
+ bundleAnalyzer({ outputFormat: 'both', openAnalyzer: true })
179
+ bundleAnalyzer({ sizeThreshold: 200, topModules: 30, gzipSize: true })
180
+ bundleAnalyzer({ compareWith: 'dist/bundle-analysis.json', defaultChartType: 'sunburst' })
181
+ bundleAnalyzer({ excludeNodeModules: true, includeExtensions: ['.js', '.css'] })
182
+ ```
183
+
184
+ ---
185
+
186
+ ### compressAssets
187
+
188
+ Automatically compress files in the output directory after Vite build, supporting both gzip and brotli compression algorithms.
189
+
190
+ | Option | Type | Default | Description |
191
+ | ------------------ | ---------------------------------- | ----------------------------------------------------------- | -------------------------------------------------- |
192
+ | algorithm | `'gzip'` \| `'brotli'` \| `'both'` | `'gzip'` | Compression algorithm |
193
+ | threshold | `number` | `1024` | Minimum compression threshold (bytes) |
194
+ | deleteOriginalFile | `boolean` | `false` | Whether to delete original files after compression |
195
+ | includeExtensions | `string[]` | `['.js', '.css', '.html', '.svg', '.json', '.xml', '.txt']` | File extensions to compress |
196
+ | excludeExtensions | `string[]` | `[]` | File extensions to exclude |
197
+ | excludePaths | `string[]` | `[]` | Path prefixes to exclude |
198
+ | compressionLevel | `number` | `9` | Gzip compression level (1-9) |
199
+ | brotliQuality | `number` | `11` | Brotli compression quality (1-11) |
200
+ | reportOutput | `string` \| `false` | `'compress-report.json'` | Compression report output path, false to skip |
201
+ | parallelLimit | `number` | `10` | Maximum concurrent file compression count |
202
+
203
+ ```typescript
204
+ compressAssets()
205
+ compressAssets({ algorithm: 'brotli' })
206
+ compressAssets({ algorithm: 'both', threshold: 2048, compressionLevel: 9, brotliQuality: 11 })
207
+ compressAssets({ deleteOriginalFile: true, reportOutput: 'compress-report.json' })
208
+ compressAssets({ includeExtensions: ['.js', '.css'], excludePaths: ['assets/images'], parallelLimit: 5 })
209
+ ```
210
+
211
+ ---
212
+
187
213
  ### copyFile
188
214
 
189
215
  Copy files or directories to specified locations after Vite build is completed, with `enforce: 'post'`.
@@ -197,21 +223,12 @@ Copy files or directories to specified locations after Vite build is completed,
197
223
  | incremental | `boolean` | `true` | Whether to enable incremental copying |
198
224
 
199
225
  ```typescript
200
- // Basic usage
201
- copyFile({
202
- sourceDir: 'src/assets',
203
- targetDir: 'dist/assets'
204
- })
205
-
206
- // Disable overwrite and incremental copy
207
- copyFile({
208
- sourceDir: 'src/static',
209
- targetDir: 'dist/static',
210
- overwrite: false,
211
- incremental: false
212
- })
226
+ copyFile({ sourceDir: 'src/assets', targetDir: 'dist/assets' })
227
+ copyFile({ sourceDir: 'src/static', targetDir: 'dist/static', overwrite: false, incremental: false })
213
228
  ```
214
229
 
230
+ ---
231
+
215
232
  ### generateRouter
216
233
 
217
234
  Automatically generate router configuration files based on uni-app project's `pages.json`.
@@ -229,38 +246,19 @@ Automatically generate router configuration files based on uni-app project's `pa
229
246
  | exportTypes | `boolean` | `true` | Whether to export type definitions |
230
247
  | preserveRouteChanges | `boolean` | `true` | Whether to preserve user modifications to routes |
231
248
 
232
- > Default `metaMapping` is `{ navigationBarTitleText: 'title', requireAuth: 'requireAuth' }`, automatically mapping page style fields to route meta. When `nameStrategy` is `'custom'`, `customNameGenerator` must be
233
- > provided.
249
+ > Default `metaMapping` is `{ navigationBarTitleText: 'title', requireAuth: 'requireAuth' }`. When `nameStrategy` is `'custom'`, `customNameGenerator` must be provided.
234
250
 
235
251
  ```typescript
236
- // Basic usage
237
252
  generateRouter()
238
-
239
- // Custom pages.json path
240
253
  generateRouter({ pagesJsonPath: 'pages.json' })
241
-
242
- // Output JavaScript file
243
254
  generateRouter({ outputFormat: 'js', outputPath: 'src/router.config.js' })
244
-
245
- // PascalCase naming strategy
246
255
  generateRouter({ nameStrategy: 'pascalCase' })
247
-
248
- // Custom route name generator
249
- generateRouter({
250
- nameStrategy: 'custom',
251
- customNameGenerator: path => `route_${path.replace(/\//g, '_')}`
252
- })
253
-
254
- // Custom meta mapping
255
- generateRouter({
256
- metaMapping: {
257
- navigationBarTitleText: 'title',
258
- requireAuth: 'requireAuth',
259
- customField: 'custom'
260
- }
261
- })
256
+ generateRouter({ nameStrategy: 'custom', customNameGenerator: path => `route_${path.replace(/\//g, '_')}` })
257
+ generateRouter({ metaMapping: { navigationBarTitleText: 'title', requireAuth: 'requireAuth', customField: 'custom' } })
262
258
  ```
263
259
 
260
+ ---
261
+
264
262
  ### generateVersion
265
263
 
266
264
  Automatically generate version numbers during the Vite build process.
@@ -300,37 +298,19 @@ Automatically generate version numbers during the Vite build process.
300
298
  > and timestamp.
301
299
 
302
300
  ```typescript
303
- // Timestamp format (default)
304
301
  generateVersion()
305
-
306
- // Date format
307
302
  generateVersion({ format: 'date' })
308
-
309
- // Semantic version format
310
303
  generateVersion({ format: 'semver', semverBase: '2.0.0', prefix: 'v' })
311
-
312
- // Custom format
313
- generateVersion({
314
- format: 'custom',
315
- customFormat: '{YYYY}.{MM}.{DD}-{hash}',
316
- hashLength: 6
317
- })
318
-
319
- // Inject into code
304
+ generateVersion({ format: 'custom', customFormat: '{YYYY}.{MM}.{DD}-{hash}', hashLength: 6 })
320
305
  generateVersion({ outputType: 'define', defineName: '__VERSION__' })
321
-
322
- // Both file output and code injection
323
- generateVersion({
324
- outputType: 'both',
325
- outputFile: 'build-info.json',
326
- defineName: '__BUILD_VERSION__',
327
- extra: { environment: 'production' }
328
- })
306
+ generateVersion({ outputType: 'both', outputFile: 'build-info.json', defineName: '__BUILD_VERSION__', extra: { environment: 'production' } })
329
307
  ```
330
308
 
309
+ ---
310
+
331
311
  ### versionUpdateChecker
332
312
 
333
- Periodically check for version changes at runtime and prompt users to refresh when a new version is detected. Typically used in conjunction with the `generateVersion` plugin.
313
+ Periodically check for version changes at runtime, prompting users to refresh when a new version is detected. Typically used with the `generateVersion` plugin.
334
314
 
335
315
  **How it works:**
336
316
 
@@ -338,650 +318,582 @@ Periodically check for version changes at runtime and prompt users to refresh wh
338
318
  2. `versionUpdateChecker` periodically requests the version file at runtime and compares it with the current version
339
319
  3. When a version mismatch is detected, a prompt is shown to guide the user to refresh
340
320
 
341
- | Option | Type | Default | Description |
342
- | ----------------------- | ------------------------------------ | -------------------------------------------------------------- | ------------------------------------------------------------ |
343
- | versionSource | `'define'` \| `'file'` \| `'auto'` | `'auto'` | Current version source |
344
- | defineName | `string` | `'__APP_VERSION__'` | Global variable name in define mode |
345
- | checkUrl | `string` | `'/version.json'` | URL path for version check file |
346
- | checkInterval | `number` | `300000` | Check interval in milliseconds (default 5 minutes) |
347
- | checkOnVisibilityChange | `boolean` | `true` | Whether to check immediately on page visibility change |
348
- | enableInDev | `boolean` | `false` | Whether to enable in development mode |
349
- | promptStyle | `'modal'` \| `'banner'` \| `'toast'` | `'modal'` | Update prompt UI style |
350
- | promptMessage | `string` | `'A new version is available. Refresh now to get the latest?'` | Prompt message text |
351
- | refreshButtonText | `string` | `'Refresh'` | Refresh button text |
352
- | dismissButtonText | `string` | `'Later'` | Dismiss button text |
353
- | customPromptTemplate | `string` | - | Custom HTML template for the prompt UI |
354
- | customStyle | `string` | - | Custom CSS style string |
355
- | onUpdateAvailable | `string` | - | Callback when new version is found (function body string) |
356
- | onRefresh | `string` | - | Callback when user chooses to refresh (function body string) |
357
- | onDismiss | `string` | - | Callback when user chooses to dismiss (function body string) |
321
+ | Option | Type | Default | Description |
322
+ | ----------------------- | ------------------------------------ | -------------------------------------------- | ------------------------------------------------------------ |
323
+ | versionSource | `'define'` \| `'file'` \| `'auto'` | `'auto'` | Current version source |
324
+ | defineName | `string` | `'__APP_VERSION__'` | Global variable name in define mode |
325
+ | checkUrl | `string` | `'/version.json'` | URL path for version check file |
326
+ | checkInterval | `number` | `300000` | Check interval (ms, default 5 minutes) |
327
+ | checkOnVisibilityChange | `boolean` | `true` | Whether to check immediately on page visibility change |
328
+ | enableInDev | `boolean` | `false` | Whether to enable in development mode |
329
+ | promptStyle | `'modal'` \| `'banner'` \| `'toast'` | `'modal'` | Update prompt UI style |
330
+ | promptMessage | `string` | `'A new version is available. Refresh now?'` | Prompt message text |
331
+ | refreshButtonText | `string` | `'Refresh Now'` | Refresh button text |
332
+ | dismissButtonText | `string` | `'Later'` | Dismiss button text |
333
+ | customPromptTemplate | `string` | - | Custom HTML template for the prompt UI |
334
+ | customStyle | `string` | - | Custom CSS style string |
335
+ | onUpdateAvailable | `string` | - | Callback when new version is found (function body string) |
336
+ | onRefresh | `string` | - | Callback when user chooses to refresh (function body string) |
337
+ | onDismiss | `string` | - | Callback when user chooses to dismiss (function body string) |
358
338
 
359
339
  > `versionSource` explanation: `'define'` reads from global variable, `'file'` reads from version file, `'auto'` prefers define and falls back to file. Custom templates can use `{{message}}`, `{{currentVersion}}`,
360
- > `{{newVersion}}`, `{{refreshButton}}`, `{{dismissButton}}` placeholders. Callbacks are provided as function body strings with available variables: `currentVersion`, `newVersion`.
340
+ > `{{newVersion}}`, `{{refreshButton}}`, `{{dismissButton}}` placeholders. Callbacks are provided as function body strings, available variables: `currentVersion`, `newVersion`.
361
341
 
362
342
  ```typescript
363
- // Basic usage (with generateVersion)
364
343
  generateVersion({ outputType: 'both' })
365
344
  versionUpdateChecker()
366
-
367
- // Read from version file only
368
345
  versionUpdateChecker({ versionSource: 'file' })
369
-
370
- // Custom check interval and prompt style
371
- versionUpdateChecker({
372
- checkInterval: 60000,
373
- promptStyle: 'banner'
374
- })
375
-
376
- // Toast-style prompt
346
+ versionUpdateChecker({ checkInterval: 60000, promptStyle: 'banner' })
377
347
  versionUpdateChecker({ promptStyle: 'toast' })
378
-
379
- // Custom prompt text
380
- versionUpdateChecker({
381
- promptMessage: 'System updated, refresh to experience new features',
382
- refreshButtonText: 'Update',
383
- dismissButtonText: 'Cancel'
384
- })
385
-
386
- // Custom callbacks
387
- versionUpdateChecker({
388
- onUpdateAvailable: 'console.log("New version:", newVersion); return true;',
389
- onRefresh: 'console.log("User chose to refresh");',
390
- onDismiss: 'console.log("User chose to dismiss");'
391
- })
392
-
393
- // Enable in development (for debugging)
348
+ versionUpdateChecker({ promptMessage: 'System updated, refresh to experience new features', refreshButtonText: 'Update', dismissButtonText: 'Cancel' })
349
+ versionUpdateChecker({ onUpdateAvailable: 'console.log("New version:", newVersion); return true;', onRefresh: 'console.log("User chose refresh");', onDismiss: 'console.log("User chose dismiss");' })
394
350
  versionUpdateChecker({ enableInDev: true })
395
351
  ```
396
352
 
397
- ### htmlInject
398
-
399
- Inject HTML content into target files during Vite build based on configured rules, supporting multiple injection positions, conditional injection, template variable replacement, and security filtering.
353
+ ---
400
354
 
401
- **Injection positions:**
355
+ ### htmlInject
402
356
 
403
- | Position | Description |
404
- | ------------------ | ------------------------------------------ |
405
- | `head-start` | Inject after the `<head>` tag |
406
- | `head-end` | Inject before the `</head>` tag |
407
- | `body-start` | Inject after the `<body>` tag |
408
- | `body-end` | Inject before the `</body>` tag |
409
- | `before-selector` | Inject before the selector-matched content |
410
- | `after-selector` | Inject after the selector-matched content |
411
- | `replace-selector` | Replace the selector-matched content |
357
+ Inject custom content into HTML files, supporting multiple positions, selector targeting, conditional injection, template variable replacement, and security filtering.
412
358
 
413
- | Option | Type | Default | Description |
414
- | ------------ | ------------------------ | -------------- | --------------------------------- |
415
- | targetFile | `string` | `'index.html'` | Target HTML file path or filename |
416
- | rules | `InjectRule[]` | - | Injection rules array (required) |
417
- | security | `SecurityConfig` | - | Security filtering configuration |
418
- | templateVars | `Record<string, string>` | - | Global template variables |
419
- | logInjection | `boolean` | `true` | Whether to output injection logs |
359
+ | Option | Type | Default | Description |
360
+ | ------------ | ------------------------ | -------------- | ------------------------------------------- |
361
+ | targetFile | `string` | `'index.html'` | Target HTML file path or filename |
362
+ | rules | `InjectRule[]` | - | Injection rules list (required) |
363
+ | security | `SecurityConfig` | - | Security filtering config |
364
+ | templateVars | `Record<string, string>` | `{}` | Global template variable mapping |
365
+ | logInjection | `boolean` | `true` | Whether to output injection logs to console |
420
366
 
421
367
  **InjectRule**
422
368
 
423
- | Property | Type | Default | Description |
424
- | -------------------- | ------------------------ | ---------- | -------------------------------------------------------- |
425
- | id | `string` | - | Unique rule identifier for logging and debugging |
426
- | content | `string` | - | HTML content to inject (required) |
427
- | position | `InjectPosition` | - | Injection position (required) |
428
- | selector | `string` | - | Selector string (required for selector positions) |
429
- | selectorMatch | `'string'` \| `'regex'` | `'string'` | Selector match mode |
430
- | priority | `number` | `100` | Priority, lower values execute first |
431
- | condition | `InjectCondition` | - | Injection condition |
432
- | templateVars | `Record<string, string>` | - | Rule-level template variables (override global) |
433
- | allowScriptInjection | `boolean` | `false` | Whether to allow injecting scripts and dangerous content |
369
+ | Property | Type | Default | Description |
370
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------- | --------------------------------------------------------- |
371
+ | id | `string` | - | Unique rule identifier |
372
+ | content | `string` | - | Content to inject |
373
+ | position | `'head-start'` \| `'head-end'` \| `'body-start'` \| `'body-end'` \| `'before-selector'` \| `'after-selector'` \| `'replace-selector'` | - | Injection position |
374
+ | selector | `string` | - | Selector string (required for selector-related positions) |
375
+ | selectorMatch | `'string'` \| `'regex'` | `'string'` | Selector match mode |
376
+ | priority | `number` | `100` | Rule priority, lower values execute first |
377
+ | condition | `InjectCondition` | - | Injection condition |
378
+ | templateVars | `Record<string, string>` | - | Rule-level template variables (override global) |
379
+ | allowScriptInjection | `boolean` | `false` | Whether to allow injecting scripts and dangerous content |
434
380
 
435
381
  **InjectCondition**
436
382
 
437
- | Property | Type | Default | Description |
438
- | -------- | ------------------------------------------- | ------- | -------------------------------------- |
439
- | type | `'env'` \| `'file-contains'` \| `'custom'` | - | Condition type (required) |
440
- | value | `string` \| `((...args: any[]) => boolean)` | - | Condition value (required) |
441
- | negate | `boolean` | `false` | Whether to negate the condition result |
383
+ | Property | Type | Default | Description |
384
+ | -------- | ------------------------------------------ | ------- | -------------------------------------- |
385
+ | type | `'env'` \| `'file-contains'` \| `'custom'` | - | Condition type |
386
+ | value | `string` \| `(...args: any[]) => boolean` | - | Condition value |
387
+ | negate | `boolean` | `false` | Whether to negate the condition result |
442
388
 
443
389
  **SecurityConfig**
444
390
 
445
- | Property | Type | Default | Description |
446
- | ------------------------ | ---------- | ------- | ------------------------------------- |
447
- | blockDangerousTags | `boolean` | `true` | Whether to block dangerous tags |
448
- | blockDangerousAttributes | `boolean` | `true` | Whether to block dangerous attributes |
449
- | allowedTags | `string[]` | - | Whitelist of allowed tags |
450
- | blockedTags | `string[]` | - | Custom blocked tags list |
451
- | blockedAttributes | `string[]` | - | Custom blocked attributes list |
391
+ | Property | Type | Default | Description |
392
+ | ------------------------ | ---------- | ------- | ----------------------------------------------------- |
393
+ | blockDangerousTags | `boolean` | `true` | Whether to block dangerous tags (script, etc.) |
394
+ | blockDangerousAttributes | `boolean` | `true` | Whether to block dangerous attributes (onclick, etc.) |
395
+ | allowedTags | `string[]` | - | Whitelist of allowed tags |
396
+ | blockedTags | `string[]` | - | Custom blocked tags list |
397
+ | blockedAttributes | `string[]` | - | Custom blocked attributes list |
452
398
 
453
399
  ```typescript
454
- // Basic usage
455
- htmlInject({
456
- rules: [{ id: 'meta-desc', content: '<meta name="description" content="My App">', position: 'head-end' }]
457
- })
458
-
459
- // Conditional injection (production only)
460
400
  htmlInject({
461
401
  rules: [
462
- {
463
- id: 'analytics',
464
- content: '<script src="/analytics.js"></script>',
465
- position: 'body-end',
466
- condition: { type: 'env', value: 'PRODUCTION' },
467
- allowScriptInjection: true
468
- }
402
+ { id: 'meta-description', content: '<meta name="description" content="My App">', position: 'head-end' },
403
+ { id: 'analytics', content: '<script src="https://analytics.example.com/track.js"></script>', position: 'body-end', allowScriptInjection: true },
404
+ { id: 'env-var', content: '<script>window.__ENV__ = "{{env}}"</script>', position: 'head-end', templateVars: { env: 'production' }, allowScriptInjection: true },
405
+ { id: 'before-app', content: '<div>Before App</div>', position: 'before-selector', selector: '<div id="app">' },
406
+ { id: 'prod-only', content: '<meta name="robots" content="noindex">', position: 'head-end', condition: { type: 'env', value: 'PRODUCTION' } }
469
407
  ]
470
408
  })
471
-
472
- // Template variable replacement
473
- htmlInject({
474
- templateVars: { appName: 'My App', version: '1.0.0' },
475
- rules: [{ id: 'meta', content: '<meta name="description" content="{{appName}}">', position: 'head-end' }]
476
- })
477
-
478
- // Selector injection
479
- htmlInject({
480
- rules: [{ id: 'replace-title', content: '<title>New Title</title>', position: 'replace-selector', selector: '<title>.*</title>', selectorMatch: 'regex' }]
481
- })
482
-
483
- // Security configuration
484
- htmlInject({
485
- security: { blockDangerousTags: true, allowedTags: ['iframe'] },
486
- rules: [{ id: 'embed', content: '<iframe src="https://example.com"></iframe>', position: 'body-end' }]
487
- })
488
409
  ```
489
410
 
490
- ### faviconManager
411
+ ---
491
412
 
492
- Inject website icon links into the head of HTML files during the Vite build process. Supports string shorthand config.
413
+ ### faviconManager
493
414
 
494
- | Option | Type | Default | Description |
495
- | ----------- | -------- | ------- | ------------------------------- |
496
- | base | `string` | `'/'` | Base path for icon files |
497
- | url | `string` | - | Complete URL for the icon |
498
- | link | `string` | - | Custom complete link tag HTML |
499
- | icons | `Icon[]` | - | Custom icon array |
500
- | copyOptions | `object` | - | Icon file copying configuration |
415
+ Manage website favicon links injection into HTML files, supports icon file copying, supports string shorthand config.
501
416
 
502
- > Priority: `link` > `url` > `base`. When `link` is provided, custom HTML is injected directly; when `url` is provided, the complete URL is used; otherwise `base + '/favicon.ico'` is used.
417
+ | Option | Type | Default | Description |
418
+ | ----------- | ------------- | ------- | ------------------------------------------------------ |
419
+ | base | `string` | `'/'` | Base path for icon files |
420
+ | url | `string` | - | Complete icon URL (overrides base + favicon.ico) |
421
+ | link | `string` | - | Custom complete link tag HTML (highest priority) |
422
+ | icons | `Icon[]` | - | Custom icon array, supports multiple formats and sizes |
423
+ | copyOptions | `CopyOptions` | - | Icon file copying config |
503
424
 
504
- `Icon` interface definition:
425
+ **Icon**
505
426
 
506
- | Property | Type | Required | Description |
507
- | -------- | -------- | -------- | ------------------ |
508
- | rel | `string` | Yes | Icon relation type |
509
- | href | `string` | Yes | Icon URL |
510
- | sizes | `string` | No | Icon sizes |
511
- | type | `string` | No | Icon MIME type |
427
+ | Property | Type | Description |
428
+ | -------- | -------- | ------------------ |
429
+ | rel | `string` | Icon relation type |
430
+ | href | `string` | Icon URL |
431
+ | sizes | `string` | Icon sizes |
432
+ | type | `string` | Icon MIME type |
512
433
 
513
- `copyOptions` interface definition:
434
+ **CopyOptions**
514
435
 
515
- | Property | Type | Required | Default | Description |
516
- | --------- | --------- | -------- | ------- | --------------------------- |
517
- | sourceDir | `string` | Yes | - | Icon source directory |
518
- | targetDir | `string` | Yes | - | Icon target directory |
519
- | overwrite | `boolean` | No | `true` | Whether to overwrite files |
520
- | recursive | `boolean` | No | `true` | Whether to copy recursively |
436
+ | Property | Type | Default | Description |
437
+ | --------- | --------- | ------- | ----------------------------------- |
438
+ | sourceDir | `string` | - | Icon source directory (required) |
439
+ | targetDir | `string` | - | Icon target directory (required) |
440
+ | overwrite | `boolean` | `true` | Whether to overwrite existing files |
441
+ | recursive | `boolean` | `true` | Whether to copy recursively |
521
442
 
522
443
  ```typescript
523
- // Use default config
524
- faviconManager()
525
-
526
- // String shorthand (set base path)
527
444
  faviconManager('/assets')
528
-
529
- // Custom icon array
445
+ faviconManager({ base: '/assets', url: '/assets/favicon.ico' })
530
446
  faviconManager({
531
447
  base: '/assets',
532
448
  icons: [
533
449
  { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
534
- { rel: 'icon', href: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
535
- { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' }
450
+ { rel: 'icon', href: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' }
536
451
  ]
537
452
  })
538
-
539
- // Custom complete link tag
540
- faviconManager({
541
- link: '<link rel="icon" href="/favicon.svg" type="image/svg+xml" />'
542
- })
543
-
544
- // With file copying
545
453
  faviconManager({
546
454
  base: '/assets',
547
- copyOptions: {
548
- sourceDir: 'src/assets/icons',
549
- targetDir: 'dist/assets/icons'
550
- }
455
+ copyOptions: { sourceDir: 'src/assets/icons', targetDir: 'dist/assets/icons' }
551
456
  })
552
457
  ```
553
458
 
459
+ ---
460
+
554
461
  ### loadingManager
555
462
 
556
- Inject global Loading state management with XHR/Fetch request interception, white-screen Loading, custom styles, and lifecycle callbacks.
557
-
558
- **Injection strategy:**
559
-
560
- - `defaultVisible: false` (default): All code (CSS + HTML + JS) is dynamically injected via JS before `</body>`
561
- - `defaultVisible: true`: CSS + HTML are injected as static tags before `</head>` (visible on white screen), JS is injected before `</body>`
562
-
563
- | Option | Type | Default | Description |
564
- | -------------- | ----------------------------------------------- | ----------------------- | ------------------------------------------------------- |
565
- | position | `'center'` \| `'top'` \| `'bottom'` | `'center'` | Loading display position |
566
- | defaultText | `string` | `'Loading...'` | Default display text |
567
- | spinnerType | `'spinner'` \| `'dots'` \| `'pulse'` \| `'bar'` | `'spinner'` | Spinner icon type |
568
- | style | `LoadingStyle` | - | Custom style configuration |
569
- | transition | `TransitionConfig` | `{ enabled: true }` | Transition animation configuration |
570
- | minDisplayTime | `MinDisplayTime` | `{ enabled: true }` | Minimum display time configuration |
571
- | delayShow | `DelayShow` | `{ enabled: true }` | Delayed show configuration |
572
- | debounceHide | `DebounceHide` | `{ enabled: false }` | Debounced hide configuration |
573
- | autoBind | `'fetch'` \| `'xhr'` \| `'all'` \| `'none'` | `'none'` | Auto-bind request interception mode |
574
- | requestFilter | `RequestFilter` | - | Request filter configuration |
575
- | globalName | `string` | `'__LOADING_MANAGER__'` | Global variable name injected into browser |
576
- | customTemplate | `string` | - | Custom HTML template (must include `data-loading-text`) |
577
- | defaultVisible | `boolean` | `false` | Whether initially visible (white-screen Loading) |
578
- | autoHideOn | `'DOMContentLoaded'` \| `'load'` \| `'manual'` | `'DOMContentLoaded'` | Auto-hide timing (requires `defaultVisible: true`) |
579
- | callbacks | `LoadingCallbacks` | - | Lifecycle callbacks |
463
+ Global Loading state management with request interception, debounce, transition animations, and white-screen Loading.
464
+
465
+ | Option | Type | Default | Description |
466
+ | -------------- | ------------------ | ----------------------- | ------------------------------------------------------------- |
467
+ | position | `LoadingPosition` | `'center'` | Loading display position |
468
+ | defaultText | `string` | `'Loading...'` | Default display text |
469
+ | spinnerType | `SpinnerType` | `'spinner'` | Spinner icon type |
470
+ | style | `LoadingStyle` | - | Custom style config |
471
+ | transition | `TransitionConfig` | - | Transition animation config |
472
+ | minDisplayTime | `MinDisplayTime` | - | Minimum display time config |
473
+ | delayShow | `DelayShow` | - | Delay show config |
474
+ | debounceHide | `DebounceHide` | - | Debounce hide config |
475
+ | autoBind | `AutoBindMode` | `'none'` | Auto-bind request interception mode |
476
+ | requestFilter | `RequestFilter` | - | Request filter config |
477
+ | globalName | `string` | `'__LOADING_MANAGER__'` | Injected global variable name |
478
+ | customTemplate | `string` | - | Custom Loading HTML template |
479
+ | defaultVisible | `boolean` | `false` | Loading DOM initial visibility (white-screen Loading) |
480
+ | autoHideOn | `AutoHideOn` | `'DOMContentLoaded'` | Auto-hide timing (only effective when defaultVisible is true) |
481
+ | callbacks | `LoadingCallbacks` | - | Lifecycle callbacks |
482
+
483
+ **LoadingPosition**: `'center'` | `'top'` | `'bottom'`
484
+
485
+ **SpinnerType**: `'spinner'` | `'dots'` | `'pulse'` | `'bar'`
486
+
487
+ **AutoBindMode**: `'fetch'` | `'xhr'` | `'all'` | `'none'`
488
+
489
+ **AutoHideOn**: `'DOMContentLoaded'` | `'load'` | `'manual'`
580
490
 
581
491
  **LoadingStyle**
582
492
 
583
493
  | Property | Type | Default | Description |
584
494
  | ------------------ | --------- | ------------------------- | ---------------------------------------- |
585
495
  | overlayColor | `string` | `'rgba(255,255,255,0.7)'` | Overlay background color |
586
- | spinnerColor | `string` | `'#4361ee'` | Spinner icon color |
587
- | spinnerSize | `string` | `'40px'` | Spinner icon size |
496
+ | spinnerColor | `string` | `'#4361ee'` | Loading icon color |
497
+ | spinnerSize | `string` | `'40px'` | Loading icon size |
588
498
  | textColor | `string` | `'#333'` | Text color |
589
499
  | textSize | `string` | `'14px'` | Text size |
590
500
  | customClass | `string` | - | Custom CSS class name |
591
- | customStyle | `string` | - | Custom inline style |
592
- | zIndex | `number` | `9999` | z-index value |
501
+ | customStyle | `string` | - | Custom inline style string |
502
+ | zIndex | `number` | `9999` | Overlay z-index |
593
503
  | pointerEvents | `boolean` | `true` | Whether to enable overlay pointer events |
594
504
  | backdropBlur | `boolean` | `false` | Whether to enable backdrop blur |
595
505
  | backdropBlurAmount | `number` | `4` | Backdrop blur amount (px) |
596
506
 
597
507
  **TransitionConfig**
598
508
 
599
- | Property | Type | Default | Description |
600
- | -------- | --------- | ------------ | ---------------------------- |
601
- | enabled | `boolean` | `true` | Whether to enable transition |
602
- | duration | `number` | `200` | Transition duration (ms) |
603
- | easing | `string` | `'ease-out'` | Easing function |
509
+ | Property | Type | Default | Description |
510
+ | -------- | --------- | ------------ | ------------------------------ |
511
+ | enabled | `boolean` | `true` | Whether to enable transition |
512
+ | duration | `number` | `200` | Transition duration (ms) |
513
+ | easing | `string` | `'ease-out'` | CSS transition easing function |
604
514
 
605
515
  **MinDisplayTime**
606
516
 
607
- | Property | Type | Default | Description |
608
- | -------- | --------- | ------- | --------------------------------------------------------- |
609
- | enabled | `boolean` | `true` | Whether to enable |
610
- | duration | `number` | `300` | Minimum display time (ms), prevents Loading from flashing |
517
+ | Property | Type | Default | Description |
518
+ | -------- | --------- | ------- | -------------------------------------- |
519
+ | enabled | `boolean` | `true` | Whether to enable minimum display time |
520
+ | duration | `number` | `300` | Minimum display time (ms) |
611
521
 
612
522
  **DelayShow**
613
523
 
614
- | Property | Type | Default | Description |
615
- | -------- | --------- | ------- | ------------------------------------------------------------------------------ |
616
- | enabled | `boolean` | `true` | Whether to enable |
617
- | duration | `number` | `200` | Delay duration (ms); if request completes within this time, Loading won't show |
524
+ | Property | Type | Default | Description |
525
+ | -------- | --------- | ------- | --------------------------------------------------------------- |
526
+ | enabled | `boolean` | `true` | Whether to enable delay show |
527
+ | duration | `number` | `200` | Delay time (ms), requests completed within this time won't show |
618
528
 
619
529
  **DebounceHide**
620
530
 
621
- | Property | Type | Default | Description |
622
- | -------- | --------- | ------- | ----------------------- |
623
- | enabled | `boolean` | `false` | Whether to enable |
624
- | duration | `number` | `100` | Debounce wait time (ms) |
531
+ | Property | Type | Default | Description |
532
+ | -------- | --------- | ------- | ------------------------------- |
533
+ | enabled | `boolean` | `false` | Whether to enable debounce hide |
534
+ | duration | `number` | `100` | Debounce wait time (ms) |
625
535
 
626
536
  **RequestFilter**
627
537
 
628
- | Property | Type | Description |
629
- | ------------------ | ---------- | --------------------------------------------------------------------- |
630
- | excludeUrls | `RegExp[]` | Array of URL regex patterns to exclude |
631
- | includeUrls | `RegExp[]` | Array of URL regex patterns to include (higher priority than exclude) |
632
- | excludeMethods | `string[]` | Array of HTTP methods to exclude |
633
- | excludeUrlPrefixes | `string[]` | Array of URL prefixes to exclude (prefix matching, more efficient) |
538
+ | Property | Type | Description |
539
+ | ------------------ | ---------- | ------------------------------ |
540
+ | excludeUrls | `RegExp[]` | URL regex patterns to exclude |
541
+ | includeUrls | `RegExp[]` | URL regex patterns to include |
542
+ | excludeMethods | `string[]` | HTTP methods to exclude |
543
+ | excludeUrlPrefixes | `string[]` | URL string prefixes to exclude |
634
544
 
635
545
  **LoadingCallbacks**
636
546
 
637
- Callbacks are provided as **function body strings** (injected into browser at build time, function references cannot be passed).
638
-
639
- | Property | Type | Description |
640
- | ------------ | -------- | ----------------------------------------------- |
641
- | onBeforeShow | `string` | Before show callback, `return false` to prevent |
642
- | onShow | `string` | After show callback |
643
- | onBeforeHide | `string` | Before hide callback, `return false` to prevent |
644
- | onHide | `string` | After hide callback |
645
- | onDestroy | `string` | On destroy callback |
646
-
647
- **LoadingManager API**
648
-
649
- Access via `window.__LOADING_MANAGER__`:
650
-
651
- | Method | Description |
652
- | -------------------------- | ------------------------------------------------------------------- |
653
- | `show(text?)` | Show Loading, optionally pass text |
654
- | `hide()` | Hide Loading (subject to min display time and debounce constraints) |
655
- | `forceHide()` | Force hide, ignoring min display time and debounce |
656
- | `toggle(text?)` | Toggle Loading show/hide state |
657
- | `updateText(text)` | Update text content |
658
- | `isVisible()` | Get whether Loading is currently visible |
659
- | `isPointerEventsEnabled()` | Get whether pointer events are currently enabled |
660
- | `enablePointerEvents()` | Enable overlay pointer events, intercept all clicks and scrolls |
661
- | `disablePointerEvents()` | Disable overlay pointer events, allow interaction passthrough |
662
- | `togglePointerEvents()` | Toggle overlay pointer events state |
663
- | `getPendingCount()` | Get the number of pending requests |
664
- | `destroy()` | Destroy instance, clean up DOM and restore original interceptors |
547
+ | Property | Type | Description |
548
+ | ------------ | -------- | -------------------------------------------------------- |
549
+ | onBeforeShow | `string` | Before show callback (`return false` to prevent showing) |
550
+ | onShow | `string` | After show callback |
551
+ | onBeforeHide | `string` | Before hide callback (`return false` to prevent hiding) |
552
+ | onHide | `string` | After hide callback |
553
+ | onDestroy | `string` | On destroy callback |
665
554
 
666
- ```typescript
667
- // White-screen Loading: visible on page load, auto-hide on DOMContentLoaded
668
- loadingManager({ defaultVisible: true, autoHideOn: 'DOMContentLoaded' })
669
-
670
- // White-screen Loading: auto-hide after all resources loaded
671
- loadingManager({ defaultVisible: true, autoHideOn: 'load' })
672
-
673
- // Vue/React SPA: visible on white screen, manually hide after framework renders
674
- loadingManager({ defaultVisible: true, autoHideOn: 'manual' })
675
- // In app entry: window.__LOADING_MANAGER__.hide()
676
-
677
- // Auto-intercept all requests
678
- loadingManager({ autoBind: 'all' })
679
-
680
- // Custom styles + request filtering
681
- loadingManager({
682
- style: { overlayColor: 'rgba(0,0,0,0.5)', spinnerColor: '#fff', backdropBlur: true },
683
- autoBind: 'fetch',
684
- requestFilter: { excludeUrls: [/\/api\/health/], excludeUrlPrefixes: ['http://localhost'] }
685
- })
686
-
687
- // Debounced hide (prevent rapid flashing)
688
- loadingManager({ debounceHide: { enabled: true, duration: 100 } })
555
+ > Callbacks are provided as function body strings because they need to be injected into client-side code. `customTemplate` must contain an element with the `data-loading-text` attribute for text display.
689
556
 
690
- // Lifecycle callbacks
691
- loadingManager({
692
- callbacks: {
693
- onBeforeShow: 'if (shouldSkip) return false;',
694
- onShow: 'console.log("loading shown")',
695
- onBeforeHide: 'if (shouldKeepVisible) return false;',
696
- onHide: 'console.log("loading hidden")'
697
- }
698
- })
557
+ **Runtime API:**
699
558
 
700
- // Manual control
701
- loadingManager()
702
- window.__LOADING_MANAGER__.show('Saving...')
559
+ ```typescript
560
+ window.__LOADING_MANAGER__.show('Loading...')
703
561
  window.__LOADING_MANAGER__.hide()
704
- window.__LOADING_MANAGER__.toggle()
562
+ window.__LOADING_MANAGER__.forceHide()
563
+ window.__LOADING_MANAGER__.toggle('Loading...')
564
+ window.__LOADING_MANAGER__.updateText('Processing...')
565
+ window.__LOADING_MANAGER__.isVisible()
566
+ window.__LOADING_MANAGER__.getPendingCount()
567
+ window.__LOADING_MANAGER__.enablePointerEvents()
705
568
  window.__LOADING_MANAGER__.disablePointerEvents()
569
+ window.__LOADING_MANAGER__.destroy()
706
570
  ```
707
571
 
708
- ## Common Utilities
709
-
710
- Exported via `@meng-xi/vite-plugin/common`, reusable in custom plugins:
711
-
712
572
  ```typescript
713
- import { deepMerge, formatDate, parseTemplate, toCamelCase, toPascalCase, stripJsonComments, generateRandomHash, escapeHtmlAttr, Validator } from '@meng-xi/vite-plugin/common'
714
- import { readFileContent, writeFileContent, fileExists, copySourceToTarget } from '@meng-xi/vite-plugin/common/fs'
715
- import { injectBeforeTag, injectHtmlByPriority, injectBeforeTagWithFallback, injectHeadAndBody } from '@meng-xi/vite-plugin/common/html'
716
- import { makeCallback, containsScriptTag, validateIdentifierName } from '@meng-xi/vite-plugin/common/script'
717
- import { validateGlobalName, validateNoScriptInTemplate, validateCallbackFields, validateNonNegativeNumber, validateNestedDuration, validateEnumValue } from '@meng-xi/vite-plugin/common/validation'
573
+ loadingManager()
574
+ loadingManager({ defaultVisible: true, autoHideOn: 'DOMContentLoaded' })
575
+ loadingManager({ position: 'top', defaultText: 'Please wait...', spinnerType: 'dots' })
576
+ loadingManager({ autoBind: 'fetch', requestFilter: { excludeUrls: [/\/api\/health/] } })
577
+ loadingManager({
578
+ style: { overlayColor: 'rgba(0,0,0,0.5)', spinnerColor: '#ff6b6b', backdropBlur: true, backdropBlurAmount: 6 }
579
+ })
580
+ loadingManager({ transition: { enabled: true, duration: 300, easing: 'cubic-bezier(0.4,0,0.2,1)' } })
581
+ loadingManager({ debounceHide: { enabled: true, duration: 100 } })
582
+ loadingManager({ callbacks: { onShow: 'console.log("shown")', onBeforeShow: 'return true' } })
583
+ loadingManager({ customTemplate: '<div class="my-loader"><span data-loading-text></span></div>' })
584
+ loadingManager({ defaultVisible: true, autoHideOn: 'manual' })
718
585
  ```
719
586
 
720
- | Function | Description | Sub-path |
721
- | ------------------------------- | ----------------------------------------------------------------------------- | ------------------- |
722
- | `deepMerge()` | Deep merge objects (undefined skipped, arrays overwritten) | `common/object` |
723
- | `formatDate()` | Format date with `{YYYY}`, `{MM}`, `{DD}` etc. placeholders | `common/format` |
724
- | `parseTemplate()` | Parse template string, replace placeholders | `common/format` |
725
- | `toCamelCase()` | Convert to camelCase | `common/format` |
726
- | `toPascalCase()` | Convert to PascalCase | `common/format` |
727
- | `stripJsonComments()` | Remove comments from JSON string | `common/format` |
728
- | `generateRandomHash()` | Generate random hash string (1-64 characters) | `common/format` |
729
- | `escapeHtmlAttr()` | Escape special characters in HTML attribute values, prevent XSS injection | `common/format` |
730
- | `readFileContent()` | Async read file content | `common/fs` |
731
- | `writeFileContent()` | Async write file content | `common/fs` |
732
- | `fileExists()` | Async check if file exists | `common/fs` |
733
- | `copySourceToTarget()` | Copy files or directories with incremental copy and concurrency | `common/fs` |
734
- | `injectBeforeTag()` | Inject code before a specified closing HTML tag | `common/html` |
735
- | `injectHtmlByPriority()` | Inject code into HTML by priority (`</head>` → `</body>` → `</html>`) | `common/html` |
736
- | `injectBeforeTagWithFallback()` | Inject with fallback strategy (`</body>` → `</html>` → append) | `common/html` |
737
- | `injectHeadAndBody()` | Dual-zone HTML injection (head + body) | `common/html` |
738
- | `makeCallback()` | Wrap callback function body as safe function expression (with try-catch) | `common/script` |
739
- | `containsScriptTag()` | Detect if a string contains `<script>` tags | `common/script` |
740
- | `validateIdentifierName()` | Validate string as a legal JavaScript identifier, prevent prototype pollution | `common/script` |
741
- | `validateGlobalName()` | Validate global variable name legality | `common/validation` |
742
- | `validateNoScriptInTemplate()` | Validate template string does not contain script tags (XSS protection) | `common/validation` |
743
- | `validateCallbackFields()` | Validate callback fields do not contain script tags | `common/validation` |
744
- | `validateNonNegativeNumber()` | Validate number is non-negative | `common/validation` |
745
- | `validateNestedDuration()` | Validate nested config duration legality | `common/validation` |
746
- | `validateEnumValue()` | Validate string value is in allowed enum list | `common/validation` |
587
+ ---
747
588
 
748
589
  ## Plugin Development Framework
749
590
 
750
- ### BasePlugin Core Concepts
591
+ This package not only provides built-in plugins but also exports a complete plugin development framework to help quickly build custom Vite plugins that follow conventions.
751
592
 
752
- `BasePlugin` is the base class for all plugins, providing complete lifecycle management and development conventions:
593
+ ### BasePlugin
753
594
 
754
- #### Lifecycle
595
+ The base class for all built-in plugins, providing core capabilities such as configuration management, logging, error handling, and lifecycle management.
755
596
 
756
- | Phase | Method | Description |
757
- | ----------------- | ------------------ | -------------------------------------------------------------- |
758
- | Initialization | `constructor` | Merge options, initialize logger and validator |
759
- | Config Resolution | `onConfigResolved` | Called when Vite config is resolved |
760
- | Hook Registration | `addPluginHooks` | Register Vite plugin hooks |
761
- | Destroy | `destroy` | Automatically called during `closeBundle` for resource cleanup |
597
+ ```typescript
598
+ import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin'
599
+ import type { Plugin } from 'vite'
762
600
 
763
- #### Automatic Hook Composition
601
+ interface MyPluginOptions {
602
+ prefix?: string
603
+ }
764
604
 
765
- The `toPlugin()` method automatically composes the following hooks:
605
+ class MyPlugin extends BasePlugin<MyPluginOptions> {
606
+ protected getPluginName() {
607
+ return 'my-plugin'
608
+ }
766
609
 
767
- - **configResolved** - Base class `onConfigResolved` runs first, then subclass hook
768
- - **closeBundle** - Subclass hook runs first, then base class `destroy`
610
+ protected getDefaultOptions() {
611
+ return { prefix: '[app]' }
612
+ }
769
613
 
770
- > Subclasses don't need to manually register `closeBundle` hooks for cleanup — just override the `destroy()` method.
614
+ protected validateOptions() {
615
+ this.validator.field('prefix').string().notEmpty().validate()
616
+ }
771
617
 
772
- #### Required Methods
618
+ protected addPluginHooks(plugin: Plugin) {
619
+ plugin.writeBundle = {
620
+ order: 'post',
621
+ handler: async () => {
622
+ await this.safeExecute(async () => {
623
+ this.logger.info('Plugin executing...')
624
+ }, 'Execute custom logic')
625
+ }
626
+ }
627
+ }
628
+ }
773
629
 
774
- | Method | Description |
775
- | ------------------------ | --------------------- |
776
- | `getPluginName()` | Return plugin name |
777
- | `addPluginHooks(plugin)` | Add Vite plugin hooks |
630
+ export const myPlugin = createPluginFactory(MyPlugin)
631
+ ```
778
632
 
779
- #### Optional Methods
633
+ **BasePlugin Core Methods:**
634
+
635
+ | Method | Description |
636
+ | ------------------- | -------------------------------------------------------------- |
637
+ | `getDefaultOptions` | Returns plugin default config, can be overridden by subclasses |
638
+ | `validateOptions` | Validates user config, can be overridden by subclasses |
639
+ | `getPluginName` | Returns plugin name (abstract method, must be implemented) |
640
+ | `getEnforce` | Returns plugin execution timing (pre / post / undefined) |
641
+ | `addPluginHooks` | Registers Vite hooks (abstract method, must be implemented) |
642
+ | `onConfigResolved` | Config resolution complete callback |
643
+ | `destroy` | Plugin destroy callback |
644
+ | `safeExecute` | Safely execute async function with automatic error handling |
645
+ | `safeExecuteSync` | Safely execute sync function with automatic error handling |
646
+ | `handleError` | Handle errors based on errorStrategy |
647
+ | `toPlugin` | Convert to Vite plugin object |
648
+
649
+ **BasePluginOptions Base Config:**
650
+
651
+ | Option | Type | Default | Description |
652
+ | ------------- | ---------------------------------- | --------- | ----------------------- |
653
+ | enabled | `boolean` | `true` | Whether to enable |
654
+ | verbose | `boolean` | `true` | Whether to log |
655
+ | errorStrategy | `'throw'` \| `'log'` \| `'ignore'` | `'throw'` | Error handling strategy |
780
656
 
781
- | Method | Default Behavior | Description |
782
- | -------------------------- | ----------------- | ------------------------------------------- |
783
- | `getDefaultOptions()` | Returns `{}` | Provide plugin default options |
784
- | `validateOptions()` | No validation | Validate configuration parameters |
785
- | `getEnforce()` | `undefined` | Plugin execution order (`'pre'` / `'post'`) |
786
- | `onConfigResolved(config)` | Store config | Config resolved callback |
787
- | `destroy()` | Unregister logger | Cleanup logic when plugin is destroyed |
657
+ ### createPluginFactory
788
658
 
789
- #### Built-in Properties
659
+ Creates a plugin factory function that converts a BasePlugin subclass into a directly usable Vite plugin function.
790
660
 
791
- | Property | Type | Description |
792
- | ------------ | ------------------------ | ----------------------------- |
793
- | `options` | `Required<T>` | Merged complete configuration |
794
- | `logger` | `PluginLogger` | Plugin logger |
795
- | `validator` | `Validator<T>` | Configuration validator |
796
- | `viteConfig` | `ResolvedConfig \| null` | Resolved Vite configuration |
661
+ ```typescript
662
+ import { createPluginFactory } from '@meng-xi/vite-plugin'
797
663
 
798
- #### Error Handling Strategy
664
+ const myPlugin = createPluginFactory(MyPlugin)
799
665
 
800
- Control error behavior via the `errorStrategy` configuration option:
666
+ // Supports options normalizer (e.g. string shorthand config)
667
+ const myPluginWithNormalizer = createPluginFactory(MyPlugin, opt => (typeof opt === 'string' ? { prefix: opt } : opt))
668
+ ```
801
669
 
802
- - `'throw'` (default) - Log error and throw exception, halting the build
803
- - `'log'` - Log error but don't throw, continue execution
804
- - `'ignore'` - Log error but don't throw, continue execution
670
+ ### Logger
805
671
 
806
- Wrap error-prone operations with `safeExecute` / `safeExecuteSync`:
672
+ Global singleton log manager, providing independent log proxies for each plugin.
807
673
 
808
674
  ```typescript
809
- // Async safe execution
810
- const result = await this.safeExecute(async () => {
811
- return await someAsyncOperation()
812
- }, 'Execute async operation')
813
-
814
- // Sync safe execution
815
- const value = this.safeExecuteSync(() => {
816
- return someSyncOperation()
817
- }, 'Execute sync operation')
675
+ import { Logger } from '@meng-xi/vite-plugin/logger'
676
+
677
+ const logger = Logger.create({ name: 'my-plugin', enabled: true })
678
+ logger.info('Info log')
679
+ logger.success('Success log')
680
+ logger.warn('Warning log')
681
+ logger.error('Error log')
818
682
  ```
819
683
 
820
- ### createPluginFactory
684
+ ### Validator
821
685
 
822
- Create plugin factory functions with optional normalizer support:
686
+ Chain-style configuration validator for validating plugin configuration parameters.
823
687
 
824
688
  ```typescript
825
- // Basic usage
826
- const myPlugin = createPluginFactory(MyPlugin)
689
+ import { Validator } from '@meng-xi/vite-plugin/common/validation'
827
690
 
828
- // With normalizer (supports shorthand string config)
829
- const myPlugin = createPluginFactory(MyPlugin, opt => (typeof opt === 'string' ? { path: opt } : opt))
830
-
831
- // Usage with shorthand
832
- myPlugin('./custom-path')
691
+ const validator = new Validator(myOptions)
692
+ validator.field('port').number().minValue(1).maxValue(65535).field('host').string().notEmpty().field('mode').enum(['development', 'production']).validate()
833
693
  ```
834
694
 
835
- ### Validator
695
+ ---
836
696
 
837
- Fluent configuration validator with chainable API:
697
+ ## Common Utility Modules
838
698
 
839
- ```typescript
840
- import { Validator } from '@meng-xi/vite-plugin/common'
699
+ Built-in general-purpose utility function library, organized by functional modules, supporting on-demand sub-path imports.
841
700
 
842
- const validator = new Validator(options)
843
- validator
844
- .field('sourceDir')
845
- .required()
846
- .string()
847
- .field('targetDir')
848
- .required()
849
- .string()
850
- .field('overwrite')
851
- .boolean()
852
- .default(true)
853
- .field('port')
854
- .number()
855
- .field('list')
856
- .array()
857
- .field('config')
858
- .object()
859
- .field('name')
860
- .custom(val => val.length > 0, 'name cannot be empty')
861
- .validate()
701
+ ### Import Methods
702
+
703
+ ```typescript
704
+ // Import all utilities
705
+ import { formatFileSize, scanDirectory } from '@meng-xi/vite-plugin/common'
706
+
707
+ // Import by module
708
+ import { formatFileSize } from '@meng-xi/vite-plugin/common/format'
709
+ import { scanDirectory, writeJsonReport } from '@meng-xi/vite-plugin/common/fs'
710
+ import { calculateGzipSize } from '@meng-xi/vite-plugin/common/compress'
711
+ import { isNodeModule } from '@meng-xi/vite-plugin/common/path'
862
712
  ```
863
713
 
864
- | Method | Description |
865
- | ------------ | ----------------------------------------------------------------- |
866
- | `field()` | Specify the field to validate |
867
- | `required()` | Mark field as required |
868
- | `string()` | Validate field value is a string type |
869
- | `boolean()` | Validate field value is a boolean type |
870
- | `number()` | Validate field value is a number type |
871
- | `array()` | Validate field value is an array type |
872
- | `object()` | Validate field value is an object type |
873
- | `enum()` | Validate field value is in allowed enum list |
874
- | `minValue()` | Validate number field value is not less than specified minimum |
875
- | `maxValue()` | Validate number field value is not greater than specified maximum |
876
- | `default()` | Set default value for field (only when value is undefined/null) |
877
- | `custom()` | Validate field value with a custom function |
878
- | `validate()` | Execute validation, throws error on failure |
714
+ ### Module List
879
715
 
880
- ### Logger
716
+ | Sub-path | Description |
717
+ | ---------------------------------------- | ----------------------- |
718
+ | `@meng-xi/vite-plugin/common/compress` | Compression utilities |
719
+ | `@meng-xi/vite-plugin/common/format` | Formatting utilities |
720
+ | `@meng-xi/vite-plugin/common/fs` | File system utilities |
721
+ | `@meng-xi/vite-plugin/common/html` | HTML injection utils |
722
+ | `@meng-xi/vite-plugin/common/object` | Object operation utils |
723
+ | `@meng-xi/vite-plugin/common/path` | Path handling utils |
724
+ | `@meng-xi/vite-plugin/common/script` | Script generation utils |
725
+ | `@meng-xi/vite-plugin/common/validation` | Validation utilities |
726
+
727
+ ### compress — Compression
881
728
 
882
- Global singleton log manager providing independent log control for each plugin:
729
+ | Function | Description |
730
+ | ------------------- | ---------------------------------------------------- |
731
+ | `calculateGzipSize` | Calculate gzip compressed size of given data (bytes) |
883
732
 
884
733
  ```typescript
885
- import { Logger } from '@meng-xi/vite-plugin/logger'
734
+ import { calculateGzipSize } from '@meng-xi/vite-plugin/common/compress'
886
735
 
887
- // Create logger (usually called automatically by BasePlugin)
888
- Logger.create({ name: 'my-plugin', enabled: true })
736
+ const size = await calculateGzipSize(Buffer.from('hello world'))
737
+ ```
738
+
739
+ ### format — Formatting
740
+
741
+ | Function | Description |
742
+ | --------------------- | -------------------------------------------------- |
743
+ | `formatFileSize` | Format bytes to human-readable file size string |
744
+ | `getExtension` | Get file extension (lowercase) |
745
+ | `formatDate` | Format date |
746
+ | `parseTemplate` | Parse template string, replace placeholders |
747
+ | `toCamelCase` | Convert string to camelCase |
748
+ | `toPascalCase` | Convert string to PascalCase |
749
+ | `padNumber` | Pad number with leading zeros |
750
+ | `generateRandomHash` | Generate random hash string |
751
+ | `getDateFormatParams` | Get date formatting parameters |
752
+ | `stripJsonComments` | Remove comments from JSON string |
753
+ | `escapeHtmlAttr` | Escape special characters in HTML attribute values |
889
754
 
890
- // Unregister plugin log config (automatically called on plugin destroy)
891
- Logger.unregister('my-plugin')
755
+ ```typescript
756
+ import { formatFileSize, formatDate, toCamelCase } from '@meng-xi/vite-plugin/common/format'
892
757
 
893
- // Destroy singleton (for test scenarios)
894
- Logger.destroy()
758
+ formatFileSize(2461726) // '2.35MB'
759
+ formatDate(new Date(), '{YYYY}-{MM}-{DD}') // '2026-05-31'
760
+ toCamelCase('pages/user/profile') // 'pagesUserProfile'
895
761
  ```
896
762
 
897
- Log output format:
763
+ ### fs — File System
898
764
 
899
- ```
900
- ℹ️ [@meng-xi/vite-plugin:my-plugin] Info message
901
- [@meng-xi/vite-plugin:my-plugin] Success message
902
- ⚠️ [@meng-xi/vite-plugin:my-plugin] Warning message
903
- [@meng-xi/vite-plugin:my-plugin] Error message
765
+ | Function | Description |
766
+ | -------------------- | --------------------------------------------- |
767
+ | `scanDirectory` | Recursively scan directory, collect file info |
768
+ | `writeJsonReport` | Write data to JSON file |
769
+ | `writeFileContent` | Write file content |
770
+ | `readFileContent` | Read file content |
771
+ | `fileExists` | Check if file exists |
772
+ | `checkSourceExists` | Check if source file exists |
773
+ | `ensureTargetDir` | Create target directory |
774
+ | `copySourceToTarget` | Execute file copy operation |
775
+ | `runWithConcurrency` | Batch execution with concurrency limit |
776
+
777
+ ```typescript
778
+ import { scanDirectory, writeJsonReport } from '@meng-xi/vite-plugin/common/fs'
779
+
780
+ const files = await scanDirectory('dist', {
781
+ includeExtensions: ['.js', '.css'],
782
+ excludePatterns: ['node_modules'],
783
+ filter: (filePath, ext, size) => size > 1024
784
+ })
785
+
786
+ await writeJsonReport('dist/report.json', { timestamp: Date.now(), files })
904
787
  ```
905
788
 
906
- ### Custom Plugin Example
789
+ ### html HTML Injection
790
+
791
+ | Function | Description |
792
+ | ----------------------------- | ---------------------------------------- |
793
+ | `injectBeforeTag` | Inject code before specified closing tag |
794
+ | `injectHtmlByPriority` | Inject code into HTML by priority |
795
+ | `injectBeforeTagWithFallback` | Inject code with fallback strategy |
796
+ | `injectHeadAndBody` | Dual-zone HTML injection (head + body) |
907
797
 
908
798
  ```typescript
909
- import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin'
910
- import type { BasePluginOptions, PluginWithInstance } from '@meng-xi/vite-plugin/factory'
911
- import type { Plugin } from 'vite'
799
+ import { injectBeforeTag, injectHeadAndBody } from '@meng-xi/vite-plugin/common/html'
912
800
 
913
- interface MyPluginOptions extends BasePluginOptions {
914
- path: string
915
- }
801
+ const result = injectBeforeTag(html, '</head>', '<style>body{margin:0}</style>')
802
+ const dual = injectHeadAndBody(html, '<style>...</style>', '<script>...</script>')
803
+ ```
916
804
 
917
- class MyPlugin extends BasePlugin<MyPluginOptions> {
918
- protected getDefaultOptions() {
919
- return { path: './default' }
920
- }
805
+ ### object Object Operations
921
806
 
922
- protected validateOptions(): void {
923
- this.validator.field('path').required().string().validate()
924
- }
807
+ | Function | Description |
808
+ | ----------- | ------------------ |
809
+ | `deepMerge` | Deep merge objects |
925
810
 
926
- protected getPluginName(): string {
927
- return 'my-plugin'
928
- }
811
+ ```typescript
812
+ import { deepMerge } from '@meng-xi/vite-plugin/common/object'
929
813
 
930
- protected addPluginHooks(plugin: Plugin): void {
931
- plugin.buildStart = () => {
932
- this.logger.info(`Plugin started with path: ${this.options.path}`)
933
- }
934
- }
814
+ deepMerge({ a: { b: 1 } }, { a: { c: 2 } }) // { a: { b: 1, c: 2 } }
815
+ ```
935
816
 
936
- protected destroy(): void {
937
- super.destroy()
938
- // Custom cleanup logic, e.g. close connections, stop watchers
939
- }
940
- }
817
+ ### path Path Handling
941
818
 
942
- // Basic usage
943
- export const myPlugin = createPluginFactory(MyPlugin)
819
+ | Function | Description |
820
+ | -------------- | --------------------------------------- |
821
+ | `isNodeModule` | Check if module ID is from node_modules |
944
822
 
945
- // With normalizer (supports shorthand string config)
946
- export const myPluginWithNormalizer = createPluginFactory(MyPlugin, opt => (typeof opt === 'string' ? { path: opt } : opt))
947
- // Usage with shorthand: myPluginWithNormalizer('./custom-path')
823
+ ```typescript
824
+ import { isNodeModule } from '@meng-xi/vite-plugin/common/path'
825
+
826
+ isNodeModule('node_modules/lodash/index.js') // true
827
+ isNodeModule('src/utils/helper.ts') // false
948
828
  ```
949
829
 
950
- ## Sub-path Exports
830
+ ### script — Script Generation
951
831
 
952
- Support importing modules on demand to reduce bundle size:
832
+ | Function | Description |
833
+ | ------------------------ | -------------------------------------------------------------- |
834
+ | `makeCallback` | Wrap callback function body string as safe function expression |
835
+ | `containsScriptTag` | Detect if string contains `<script>` tag |
836
+ | `validateIdentifierName` | Validate if string is a valid JS identifier |
953
837
 
954
838
  ```typescript
955
- // Full import
956
- import { buildProgress, copyFile, htmlInject, loadingManager, BasePlugin, Logger } from '@meng-xi/vite-plugin'
839
+ import { makeCallback, validateIdentifierName } from '@meng-xi/vite-plugin/common/script'
957
840
 
958
- // Module-level import
959
- import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin/factory'
960
- import { Logger } from '@meng-xi/vite-plugin/logger'
961
- import { buildProgress, copyFile, generateRouter, htmlInject, loadingManager } from '@meng-xi/vite-plugin/plugins'
962
- import { Validator, readFileContent, writeFileContent } from '@meng-xi/vite-plugin/common'
963
-
964
- // Type imports (on-demand type definitions from sub-paths)
965
- import type { PluginWithInstance, PluginFactory, BasePluginOptions } from '@meng-xi/vite-plugin/factory'
966
- import type { BuildProgressOptions, GenerateVersionOptions, VersionUpdateCheckerOptions, HtmlInjectOptions, InjectRule, FaviconManagerOptions, LoadingManagerOptions, Icon } from '@meng-xi/vite-plugin/plugins'
967
- import type { DateFormatOptions } from '@meng-xi/vite-plugin/common/format'
968
- import type { HtmlInjectResult, DualInjectResult } from '@meng-xi/vite-plugin/common/html'
969
- import type { CopyOptions, CopyResult } from '@meng-xi/vite-plugin/common/fs'
841
+ makeCallback('console.log("done")')
842
+ // 'function() { try { console.log("done") } catch(e) { console.error("[callback] error:", e); } }'
843
+
844
+ validateIdentifierName('__APP_VERSION__') // passes
970
845
  ```
971
846
 
972
- ## Changelog
847
+ ### validation — Validation
973
848
 
974
- View [GitHub Releases](https://github.com/MengXi-Studio/vite-plugin/releases)
849
+ | Function | Description |
850
+ | ---------------------------- | ----------------------------------- |
851
+ | `Validator` | Chain-style configuration validator |
852
+ | `validateGlobalName` | Validate global variable name |
853
+ | `validateNoScriptInTemplate` | Validate no script tag in template |
854
+ | `validateCallbackFields` | Validate callback function fields |
855
+ | `validateNonNegativeNumber` | Validate non-negative number |
856
+ | `validateNestedDuration` | Validate nested duration config |
857
+ | `validateEnumValue` | Validate enum value |
975
858
 
976
- ## Contributing
859
+ ```typescript
860
+ import { Validator } from '@meng-xi/vite-plugin/common/validation'
977
861
 
978
- Contributions are welcome! Please follow these steps:
862
+ const validator = new Validator(options)
863
+ validator.field('port').number().minValue(1).maxValue(65535).validate()
864
+ ```
865
+
866
+ ---
867
+
868
+ ## Sub-path Exports
979
869
 
980
- 1. Fork this project
981
- 2. Create a feature branch: `git checkout -b feature/your-feature`
982
- 3. Commit changes: `git commit -m "feat: your feature description"`
983
- 4. Push branch: `git push origin feature/your-feature`
984
- 5. Create a Pull Request
870
+ | Sub-path | Description |
871
+ | ----------------------------------------------------- | ------------------------------------ |
872
+ | `@meng-xi/vite-plugin` | Main entry (all plugins + framework) |
873
+ | `@meng-xi/vite-plugin/factory` | Plugin development framework |
874
+ | `@meng-xi/vite-plugin/logger` | Log manager |
875
+ | `@meng-xi/vite-plugin/plugins` | All plugins |
876
+ | `@meng-xi/vite-plugin/common` | All utility functions |
877
+ | `@meng-xi/vite-plugin/common/compress` | Compression utilities |
878
+ | `@meng-xi/vite-plugin/common/format` | Formatting utilities |
879
+ | `@meng-xi/vite-plugin/common/fs` | File system utilities |
880
+ | `@meng-xi/vite-plugin/common/html` | HTML injection utilities |
881
+ | `@meng-xi/vite-plugin/common/object` | Object operation utilities |
882
+ | `@meng-xi/vite-plugin/common/path` | Path handling utilities |
883
+ | `@meng-xi/vite-plugin/common/script` | Script generation utilities |
884
+ | `@meng-xi/vite-plugin/common/validation` | Validation utilities |
885
+ | `@meng-xi/vite-plugin/plugins/build-progress` | buildProgress plugin |
886
+ | `@meng-xi/vite-plugin/plugins/bundle-analyzer` | bundleAnalyzer plugin |
887
+ | `@meng-xi/vite-plugin/plugins/compress-assets` | compressAssets plugin |
888
+ | `@meng-xi/vite-plugin/plugins/copy-file` | copyFile plugin |
889
+ | `@meng-xi/vite-plugin/plugins/favicon-manager` | faviconManager plugin |
890
+ | `@meng-xi/vite-plugin/plugins/generate-router` | generateRouter plugin |
891
+ | `@meng-xi/vite-plugin/plugins/generate-version` | generateVersion plugin |
892
+ | `@meng-xi/vite-plugin/plugins/html-inject` | htmlInject plugin |
893
+ | `@meng-xi/vite-plugin/plugins/loading-manager` | loadingManager plugin |
894
+ | `@meng-xi/vite-plugin/plugins/version-update-checker` | versionUpdateChecker plugin |
895
+
896
+ ---
985
897
 
986
898
  ## License
987
899