@meng-xi/vite-plugin 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README-en.md CHANGED
@@ -15,8 +15,8 @@
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
18
+ - **Ready to Use** - Provides 9 practical plugins covering build progress display, build artifact compression, file copying, router generation, version management, version update checking, HTML injection, icon injection,
19
+ and global Loading state management
20
20
  - **Plugin Development Framework** - Exports core components like BasePlugin, Logger, Validator for building custom Vite plugins
21
21
  - **Complete Lifecycle** - Supports initialization, config resolution, destroy lifecycle management with automatic hook composition
22
22
  - **Type Safe** - Complete TypeScript type definitions with configuration validators ensuring parameter correctness
@@ -47,53 +47,21 @@ pnpm add @meng-xi/vite-plugin -D
47
47
 
48
48
  ```typescript
49
49
  import { defineConfig } from 'vite'
50
- import { buildProgress, copyFile, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
50
+ import { buildProgress, compressAssets, copyFile, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
51
51
 
52
52
  export default defineConfig({
53
53
  plugins: [
54
- // Build progress bar
55
54
  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)
55
+ compressAssets({ algorithm: 'gzip' }),
56
+ copyFile({ sourceDir: 'src/assets', targetDir: 'dist/assets' }),
57
+ generateRouter({ pagesJsonPath: 'src/pages.json', outputPath: 'src/router.config.ts' }),
58
+ generateVersion({ format: 'datetime', outputType: 'both' }),
76
59
  versionUpdateChecker(),
77
-
78
- // HTML content injection
79
60
  htmlInject({
80
- rules: [
81
- {
82
- id: 'meta-description',
83
- content: '<meta name="description" content="My App">',
84
- position: 'head-end'
85
- }
86
- ]
61
+ rules: [{ id: 'meta-description', content: '<meta name="description" content="My App">', position: 'head-end' }]
87
62
  }),
88
-
89
- // Inject website icon (supports string shorthand)
90
63
  faviconManager('/assets'),
91
-
92
- // Global Loading state management
93
- loadingManager({
94
- defaultVisible: true,
95
- autoHideOn: 'DOMContentLoaded'
96
- })
64
+ loadingManager({ defaultVisible: true, autoHideOn: 'DOMContentLoaded' })
97
65
  ]
98
66
  })
99
67
  ```
@@ -107,8 +75,6 @@ import type { PluginWithInstance } from '@meng-xi/vite-plugin/factory'
107
75
  import type { GenerateRouterOptions } from '@meng-xi/vite-plugin'
108
76
 
109
77
  const routerPlugin = generateRouter({ watch: true }) as PluginWithInstance<GenerateRouterOptions>
110
-
111
- // Access plugin internals via pluginInstance
112
78
  console.log(routerPlugin.pluginInstance?.options)
113
79
  ```
114
80
 
@@ -117,6 +83,7 @@ console.log(routerPlugin.pluginInstance?.options)
117
83
  | Plugin | Description |
118
84
  | -------------------- | ----------------------------------------------------------------------------------------------------------------- |
119
85
  | buildProgress | Real-time build progress bar in terminal, supports bar / spinner / minimal |
86
+ | compressAssets | Compress build artifacts with gzip / brotli / both, concurrent compression and statistics report |
120
87
  | copyFile | Copy files or directories after build, supports incremental copying |
121
88
  | generateRouter | Auto-generate router config from pages.json (uni-app) |
122
89
  | generateVersion | Auto-generate version numbers, supports file output and global variable injection |
@@ -125,6 +92,8 @@ console.log(routerPlugin.pluginInstance?.options)
125
92
  | faviconManager | Manage website favicon links injection into HTML files, supports string shorthand config |
126
93
  | loadingManager | Global Loading state management with request interception and white-screen Loading |
127
94
 
95
+ ---
96
+
128
97
  ### buildProgress
129
98
 
130
99
  Display real-time build progress bar in terminal during Vite build, supporting three display formats.
@@ -155,24 +124,10 @@ Display real-time build progress bar in terminal during Vite build, supporting t
155
124
  | moduleColor | `(text: string) => string` | Module name color |
156
125
 
157
126
  ```typescript
158
- // Default bar format
159
127
  buildProgress()
160
-
161
- // Spinner format
162
128
  buildProgress({ format: 'spinner' })
163
-
164
- // Minimal format
165
129
  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
130
+ buildProgress({ width: 40, completeChar: '■', incompleteChar: '□', clearOnComplete: false })
176
131
  buildProgress({
177
132
  theme: {
178
133
  completeColor: t => `\x1b[32m${t}\x1b[39m`,
@@ -184,6 +139,35 @@ buildProgress({
184
139
  })
185
140
  ```
186
141
 
142
+ ---
143
+
144
+ ### compressAssets
145
+
146
+ Automatically compress files in the output directory after Vite build, supporting both gzip and brotli compression algorithms.
147
+
148
+ | Option | Type | Default | Description |
149
+ | ------------------ | ---------------------------------- | ----------------------------------------------------------- | -------------------------------------------------- |
150
+ | algorithm | `'gzip'` \| `'brotli'` \| `'both'` | `'gzip'` | Compression algorithm |
151
+ | threshold | `number` | `1024` | Minimum compression threshold (bytes) |
152
+ | deleteOriginalFile | `boolean` | `false` | Whether to delete original files after compression |
153
+ | includeExtensions | `string[]` | `['.js', '.css', '.html', '.svg', '.json', '.xml', '.txt']` | File extensions to compress |
154
+ | excludeExtensions | `string[]` | `[]` | File extensions to exclude |
155
+ | excludePaths | `string[]` | `[]` | Path prefixes to exclude |
156
+ | compressionLevel | `number` | `9` | Gzip compression level (1-9) |
157
+ | brotliQuality | `number` | `11` | Brotli compression quality (1-11) |
158
+ | reportOutput | `string` \| `false` | `'compress-report.json'` | Compression report output path, false to skip |
159
+ | parallelLimit | `number` | `10` | Maximum concurrent file compression count |
160
+
161
+ ```typescript
162
+ compressAssets()
163
+ compressAssets({ algorithm: 'brotli' })
164
+ compressAssets({ algorithm: 'both', threshold: 2048, compressionLevel: 9, brotliQuality: 11 })
165
+ compressAssets({ deleteOriginalFile: true, reportOutput: 'compress-report.json' })
166
+ compressAssets({ includeExtensions: ['.js', '.css'], excludePaths: ['assets/images'], parallelLimit: 5 })
167
+ ```
168
+
169
+ ---
170
+
187
171
  ### copyFile
188
172
 
189
173
  Copy files or directories to specified locations after Vite build is completed, with `enforce: 'post'`.
@@ -197,21 +181,12 @@ Copy files or directories to specified locations after Vite build is completed,
197
181
  | incremental | `boolean` | `true` | Whether to enable incremental copying |
198
182
 
199
183
  ```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
- })
184
+ copyFile({ sourceDir: 'src/assets', targetDir: 'dist/assets' })
185
+ copyFile({ sourceDir: 'src/static', targetDir: 'dist/static', overwrite: false, incremental: false })
213
186
  ```
214
187
 
188
+ ---
189
+
215
190
  ### generateRouter
216
191
 
217
192
  Automatically generate router configuration files based on uni-app project's `pages.json`.
@@ -229,38 +204,19 @@ Automatically generate router configuration files based on uni-app project's `pa
229
204
  | exportTypes | `boolean` | `true` | Whether to export type definitions |
230
205
  | preserveRouteChanges | `boolean` | `true` | Whether to preserve user modifications to routes |
231
206
 
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.
207
+ > Default `metaMapping` is `{ navigationBarTitleText: 'title', requireAuth: 'requireAuth' }`. When `nameStrategy` is `'custom'`, `customNameGenerator` must be provided.
234
208
 
235
209
  ```typescript
236
- // Basic usage
237
210
  generateRouter()
238
-
239
- // Custom pages.json path
240
211
  generateRouter({ pagesJsonPath: 'pages.json' })
241
-
242
- // Output JavaScript file
243
212
  generateRouter({ outputFormat: 'js', outputPath: 'src/router.config.js' })
244
-
245
- // PascalCase naming strategy
246
213
  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
- })
214
+ generateRouter({ nameStrategy: 'custom', customNameGenerator: path => `route_${path.replace(/\//g, '_')}` })
215
+ generateRouter({ metaMapping: { navigationBarTitleText: 'title', requireAuth: 'requireAuth', customField: 'custom' } })
262
216
  ```
263
217
 
218
+ ---
219
+
264
220
  ### generateVersion
265
221
 
266
222
  Automatically generate version numbers during the Vite build process.
@@ -300,34 +256,16 @@ Automatically generate version numbers during the Vite build process.
300
256
  > and timestamp.
301
257
 
302
258
  ```typescript
303
- // Timestamp format (default)
304
259
  generateVersion()
305
-
306
- // Date format
307
260
  generateVersion({ format: 'date' })
308
-
309
- // Semantic version format
310
261
  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
262
+ generateVersion({ format: 'custom', customFormat: '{YYYY}.{MM}.{DD}-{hash}', hashLength: 6 })
320
263
  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
- })
264
+ generateVersion({ outputType: 'both', outputFile: 'build-info.json', defineName: '__BUILD_VERSION__', extra: { environment: 'production' } })
329
265
  ```
330
266
 
267
+ ---
268
+
331
269
  ### versionUpdateChecker
332
270
 
333
271
  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.
@@ -360,223 +298,135 @@ Periodically check for version changes at runtime and prompt users to refresh wh
360
298
  > `{{newVersion}}`, `{{refreshButton}}`, `{{dismissButton}}` placeholders. Callbacks are provided as function body strings with available variables: `currentVersion`, `newVersion`.
361
299
 
362
300
  ```typescript
363
- // Basic usage (with generateVersion)
364
301
  generateVersion({ outputType: 'both' })
365
302
  versionUpdateChecker()
366
-
367
- // Read from version file only
368
303
  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
304
+ versionUpdateChecker({ checkInterval: 60000, promptStyle: 'banner' })
377
305
  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)
306
+ versionUpdateChecker({ promptMessage: 'System updated, refresh to experience new features', refreshButtonText: 'Update', dismissButtonText: 'Cancel' })
307
+ versionUpdateChecker({ onUpdateAvailable: 'console.log("New version:", newVersion); return true;', onRefresh: 'console.log("User chose to refresh");', onDismiss: 'console.log("User chose to dismiss");' })
394
308
  versionUpdateChecker({ enableInDev: true })
395
309
  ```
396
310
 
311
+ ---
312
+
397
313
  ### htmlInject
398
314
 
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.
315
+ Inject HTML content into target files during Vite build based on configurable rules, supporting multiple injection positions, conditional injection, template variable substitution, and security filtering.
400
316
 
401
317
  **Injection positions:**
402
318
 
403
319
  | Position | Description |
404
320
  | ------------------ | ------------------------------------------ |
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 |
321
+ | `head-start` | Inject after the `<head>` tag opening |
322
+ | `head-end` | Inject before the `</head>` closing tag |
323
+ | `body-start` | Inject after the `<body>` tag opening |
324
+ | `body-end` | Inject before the `</body>` closing tag |
409
325
  | `before-selector` | Inject before the selector-matched content |
410
326
  | `after-selector` | Inject after the selector-matched content |
411
327
  | `replace-selector` | Replace the selector-matched content |
412
328
 
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 |
329
+ | Option | Type | Default | Description |
330
+ | ------------ | ------------------------ | -------------- | ----------------------------------- |
331
+ | targetFile | `string` | `'index.html'` | Target HTML file path or filename |
332
+ | rules | `InjectRule[]` | - | Array of injection rules (required) |
333
+ | security | `SecurityConfig` | - | Security filtering configuration |
334
+ | templateVars | `Record<string, string>` | - | Global template variables |
335
+ | logInjection | `boolean` | `true` | Whether to output injection logs |
420
336
 
421
337
  **InjectRule**
422
338
 
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 |
434
-
435
- **InjectCondition**
436
-
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 |
339
+ | Property | Type | Default | Description |
340
+ | -------------------- | ------------------------ | ---------- | --------------------------------------------------------- |
341
+ | id | `string` | - | Unique rule identifier |
342
+ | content | `string` | - | HTML content to inject |
343
+ | position | `InjectPosition` | - | Injection position |
344
+ | selector | `string` | - | Selector (required for selector-related positions) |
345
+ | selectorMatch | `'string'` \| `'regex'` | `'string'` | Selector matching mode |
346
+ | priority | `number` | `100` | Rule priority, lower values execute first |
347
+ | condition | `InjectCondition` | - | Injection condition |
348
+ | templateVars | `Record<string, string>` | - | Rule-level template variables, override global vars |
349
+ | allowScriptInjection | `boolean` | `false` | Whether to allow injecting dangerous content like scripts |
442
350
 
443
351
  **SecurityConfig**
444
352
 
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 |
353
+ | Property | Type | Default | Description |
354
+ | ------------------------ | ---------- | ------- | --------------------------------- |
355
+ | blockDangerousTags | `boolean` | `true` | Block dangerous tags |
356
+ | blockDangerousAttributes | `boolean` | `true` | Block dangerous attributes |
357
+ | allowedTags | `string[]` | - | Whitelist of allowed tags |
358
+ | blockedTags | `string[]` | - | Custom list of blocked tags |
359
+ | blockedAttributes | `string[]` | - | Custom list of blocked attributes |
452
360
 
453
361
  ```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
362
  htmlInject({
461
363
  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
- }
364
+ { id: 'meta-description', content: '<meta name="description" content="{{appName}}">', position: 'head-end', templateVars: { appName: 'My Application' } },
365
+ { id: 'analytics', content: '<script src="/analytics.js"></script>', position: 'body-end', condition: { type: 'env', value: 'PRODUCTION' }, allowScriptInjection: true }
469
366
  ]
470
367
  })
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
368
  ```
489
369
 
490
- ### faviconManager
370
+ ---
491
371
 
492
- Inject website icon links into the head of HTML files during the Vite build process. Supports string shorthand config.
493
-
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 |
501
-
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.
372
+ ### faviconManager
503
373
 
504
- `Icon` interface definition:
374
+ Manage website favicon links injection into HTML files, supporting string shorthand config and icon file copying.
505
375
 
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 |
376
+ | Option | Type | Default | Description |
377
+ | ----------- | -------- | ------- | ------------------------------------------------------ |
378
+ | base | `string` | `'/'` | Base path for icon files |
379
+ | url | `string` | - | Complete icon URL, takes precedence over base |
380
+ | link | `string` | - | Custom complete link tag HTML, highest priority |
381
+ | icons | `Icon[]` | - | Custom icon array, supports multiple formats and sizes |
382
+ | copyOptions | `object` | - | Icon file copy config (sourceDir, targetDir) |
512
383
 
513
- `copyOptions` interface definition:
384
+ **Icon**
514
385
 
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 |
386
+ | Property | Type | Description |
387
+ | -------- | -------- | ------------------ |
388
+ | rel | `string` | Icon relation type |
389
+ | href | `string` | Icon URL |
390
+ | sizes | `string` | Icon size |
391
+ | type | `string` | Icon MIME type |
521
392
 
522
393
  ```typescript
523
- // Use default config
524
394
  faviconManager()
525
-
526
- // String shorthand (set base path)
527
395
  faviconManager('/assets')
528
-
529
- // Custom icon array
530
396
  faviconManager({
531
397
  base: '/assets',
532
398
  icons: [
533
399
  { 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' }
400
+ { rel: 'icon', href: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' }
536
401
  ]
537
402
  })
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
- faviconManager({
546
- base: '/assets',
547
- copyOptions: {
548
- sourceDir: 'src/assets/icons',
549
- targetDir: 'dist/assets/icons'
550
- }
551
- })
403
+ faviconManager({ link: '<link rel="icon" href="/favicon.svg" type="image/svg+xml" />' })
404
+ faviconManager({ base: '/assets', copyOptions: { sourceDir: 'src/assets/icons', targetDir: 'dist/assets/icons' } })
552
405
  ```
553
406
 
407
+ ---
408
+
554
409
  ### loadingManager
555
410
 
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 |
411
+ Global Loading state management with request interception and white-screen Loading support.
412
+
413
+ | Option | Type | Default | Description |
414
+ | -------------- | ----------------------------------------------- | ----------------------- | ------------------------------------------ |
415
+ | position | `'center'` \| `'top'` \| `'bottom'` | `'center'` | Loading display position |
416
+ | defaultText | `string` | `'Loading...'` | Default display text |
417
+ | spinnerType | `'spinner'` \| `'dots'` \| `'pulse'` \| `'bar'` | `'spinner'` | Spinner icon type |
418
+ | autoBind | `'fetch'` \| `'xhr'` \| `'all'` \| `'none'` | `'none'` | Auto-bind request interception mode |
419
+ | globalName | `string` | `'__LOADING_MANAGER__'` | Global variable name injected into browser |
420
+ | defaultVisible | `boolean` | `false` | Loading DOM initial visibility state |
421
+ | autoHideOn | `'DOMContentLoaded'` \| `'load'` \| `'manual'` | `'DOMContentLoaded'` | Auto-hide timing |
422
+ | style | `LoadingStyle` | - | Custom style configuration |
423
+ | transition | `TransitionConfig` | - | Transition animation configuration |
424
+ | minDisplayTime | `MinDisplayTime` | - | Minimum display time configuration |
425
+ | delayShow | `DelayShow` | - | Delay show configuration |
426
+ | debounceHide | `DebounceHide` | - | Debounce hide configuration |
427
+ | requestFilter | `RequestFilter` | - | Request filter configuration |
428
+ | customTemplate | `string` | - | Custom HTML template |
429
+ | callbacks | `LoadingCallbacks` | - | Lifecycle callbacks |
580
430
 
581
431
  **LoadingStyle**
582
432
 
@@ -587,388 +437,148 @@ Inject global Loading state management with XHR/Fetch request interception, whit
587
437
  | spinnerSize | `string` | `'40px'` | Spinner icon size |
588
438
  | textColor | `string` | `'#333'` | Text color |
589
439
  | textSize | `string` | `'14px'` | Text size |
590
- | customClass | `string` | - | Custom CSS class name |
591
- | customStyle | `string` | - | Custom inline style |
592
440
  | zIndex | `number` | `9999` | z-index value |
593
441
  | pointerEvents | `boolean` | `true` | Whether to enable overlay pointer events |
594
- | backdropBlur | `boolean` | `false` | Whether to enable backdrop blur |
595
- | backdropBlurAmount | `number` | `4` | Backdrop blur amount (px) |
596
-
597
- **TransitionConfig**
598
-
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 |
604
-
605
- **MinDisplayTime**
606
-
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 |
611
-
612
- **DelayShow**
613
-
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 |
618
-
619
- **DebounceHide**
620
-
621
- | Property | Type | Default | Description |
622
- | -------- | --------- | ------- | ----------------------- |
623
- | enabled | `boolean` | `false` | Whether to enable |
624
- | duration | `number` | `100` | Debounce wait time (ms) |
625
-
626
- **RequestFilter**
627
-
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) |
634
-
635
- **LoadingCallbacks**
636
-
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 |
665
-
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 } })
442
+ | backdropBlur | `boolean` | `false` | Whether to enable background blur |
443
+ | backdropBlurAmount | `number` | `4` | Background blur amount (px) |
444
+ | customClass | `string` | - | Custom CSS class name |
445
+ | customStyle | `string` | - | Custom inline style string |
689
446
 
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
- })
447
+ **Runtime API:**
699
448
 
700
- // Manual control
701
- loadingManager()
702
- window.__LOADING_MANAGER__.show('Saving...')
449
+ ```typescript
450
+ window.__LOADING_MANAGER__.show('Loading...')
703
451
  window.__LOADING_MANAGER__.hide()
452
+ window.__LOADING_MANAGER__.forceHide()
704
453
  window.__LOADING_MANAGER__.toggle()
454
+ window.__LOADING_MANAGER__.updateText('Processing...')
455
+ window.__LOADING_MANAGER__.isVisible()
456
+ window.__LOADING_MANAGER__.getPendingCount()
457
+ window.__LOADING_MANAGER__.destroy()
458
+ window.__LOADING_MANAGER__.enablePointerEvents()
705
459
  window.__LOADING_MANAGER__.disablePointerEvents()
460
+ window.__LOADING_MANAGER__.togglePointerEvents()
461
+ window.__LOADING_MANAGER__.isPointerEventsEnabled()
706
462
  ```
707
463
 
708
- ## Common Utilities
709
-
710
- Exported via `@meng-xi/vite-plugin/common`, reusable in custom plugins:
711
-
712
464
  ```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'
465
+ loadingManager()
466
+ loadingManager({ position: 'top', defaultText: 'Please wait...' })
467
+ loadingManager({ spinnerType: 'dots' })
468
+ loadingManager({ autoBind: 'fetch', requestFilter: { excludeUrls: [/\/api\/health/], excludeUrlPrefixes: ['http://localhost'] } })
469
+ loadingManager({ style: { overlayColor: 'rgba(0,0,0,0.5)', spinnerColor: '#ff6b6b', backdropBlur: true, backdropBlurAmount: 6 } })
470
+ loadingManager({ transition: { enabled: true, duration: 300, easing: 'cubic-bezier(0.4,0,0.2,1)' } })
471
+ loadingManager({ debounceHide: { enabled: true, duration: 100 } })
472
+ loadingManager({ callbacks: { onShow: 'console.log("loading shown")', onBeforeShow: 'return true', onHide: 'console.log("loading hidden")' } })
473
+ loadingManager({ customTemplate: '<div class="my-loader"><span data-loading-text></span></div>' })
474
+ loadingManager({ defaultVisible: true, autoHideOn: 'DOMContentLoaded' })
475
+ loadingManager({ defaultVisible: true, autoHideOn: 'manual' })
718
476
  ```
719
477
 
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` |
478
+ ---
747
479
 
748
480
  ## Plugin Development Framework
749
481
 
750
- ### BasePlugin Core Concepts
751
-
752
- `BasePlugin` is the base class for all plugins, providing complete lifecycle management and development conventions:
753
-
754
- #### Lifecycle
755
-
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 |
762
-
763
- #### Automatic Hook Composition
764
-
765
- The `toPlugin()` method automatically composes the following hooks:
766
-
767
- - **configResolved** - Base class `onConfigResolved` runs first, then subclass hook
768
- - **closeBundle** - Subclass hook runs first, then base class `destroy`
769
-
770
- > Subclasses don't need to manually register `closeBundle` hooks for cleanup — just override the `destroy()` method.
771
-
772
- #### Required Methods
773
-
774
- | Method | Description |
775
- | ------------------------ | --------------------- |
776
- | `getPluginName()` | Return plugin name |
777
- | `addPluginHooks(plugin)` | Add Vite plugin hooks |
778
-
779
- #### Optional Methods
780
-
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 |
788
-
789
- #### Built-in Properties
790
-
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 |
797
-
798
- #### Error Handling Strategy
799
-
800
- Control error behavior via the `errorStrategy` configuration option:
801
-
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
805
-
806
- Wrap error-prone operations with `safeExecute` / `safeExecuteSync`:
807
-
808
- ```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')
818
- ```
819
-
820
- ### createPluginFactory
482
+ ### BasePlugin
821
483
 
822
- Create plugin factory functions with optional normalizer support:
484
+ The base class for all built-in plugins, providing core functionality such as configuration management, logging, lifecycle management, and safe execution.
823
485
 
824
486
  ```typescript
825
- // Basic usage
826
- const myPlugin = createPluginFactory(MyPlugin)
827
-
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')
833
- ```
834
-
835
- ### Validator
836
-
837
- Fluent configuration validator with chainable API:
838
-
839
- ```typescript
840
- import { Validator } from '@meng-xi/vite-plugin/common'
841
-
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()
862
- ```
863
-
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 |
879
-
880
- ### Logger
881
-
882
- Global singleton log manager providing independent log control for each plugin:
883
-
884
- ```typescript
885
- import { Logger } from '@meng-xi/vite-plugin/logger'
886
-
887
- // Create logger (usually called automatically by BasePlugin)
888
- Logger.create({ name: 'my-plugin', enabled: true })
889
-
890
- // Unregister plugin log config (automatically called on plugin destroy)
891
- Logger.unregister('my-plugin')
892
-
893
- // Destroy singleton (for test scenarios)
894
- Logger.destroy()
895
- ```
896
-
897
- Log output format:
898
-
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
904
- ```
905
-
906
- ### Custom Plugin Example
907
-
908
- ```typescript
909
- import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin'
910
- import type { BasePluginOptions, PluginWithInstance } from '@meng-xi/vite-plugin/factory'
487
+ import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin/factory'
911
488
  import type { Plugin } from 'vite'
912
489
 
913
- interface MyPluginOptions extends BasePluginOptions {
914
- path: string
490
+ interface MyPluginOptions {
491
+ enabled?: boolean
492
+ message?: string
915
493
  }
916
494
 
917
495
  class MyPlugin extends BasePlugin<MyPluginOptions> {
496
+ protected getPluginName() {
497
+ return 'my-plugin'
498
+ }
918
499
  protected getDefaultOptions() {
919
- return { path: './default' }
500
+ return { enabled: true, message: 'Hello' }
920
501
  }
921
-
922
- protected validateOptions(): void {
923
- this.validator.field('path').required().string().validate()
502
+ protected validateOptions() {
503
+ this.validator.field('message').string().validate()
924
504
  }
925
-
926
- protected getPluginName(): string {
927
- return 'my-plugin'
928
- }
929
-
930
- protected addPluginHooks(plugin: Plugin): void {
505
+ protected addPluginHooks(plugin: Plugin) {
931
506
  plugin.buildStart = () => {
932
- this.logger.info(`Plugin started with path: ${this.options.path}`)
507
+ this.logger.info(this.options.message)
933
508
  }
934
509
  }
935
-
936
- protected destroy(): void {
937
- super.destroy()
938
- // Custom cleanup logic, e.g. close connections, stop watchers
939
- }
940
510
  }
941
511
 
942
- // Basic usage
943
512
  export const myPlugin = createPluginFactory(MyPlugin)
944
-
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')
948
513
  ```
949
514
 
515
+ ### Core Components
516
+
517
+ | Component | Export Path | Description |
518
+ | --------------------- | ------------------------------ | ----------------------------------- |
519
+ | `BasePlugin` | `@meng-xi/vite-plugin/factory` | Plugin base class |
520
+ | `createPluginFactory` | `@meng-xi/vite-plugin/factory` | Plugin factory function creator |
521
+ | `PluginWithInstance` | `@meng-xi/vite-plugin/factory` | Plugin type with instance reference |
522
+ | `Logger` | `@meng-xi/vite-plugin/logger` | Log manager (singleton pattern) |
523
+ | `Validator` | `@meng-xi/vite-plugin/common` | Fluent API configuration validator |
524
+
525
+ ### Common Utilities
526
+
527
+ | Module | Export Path | Description |
528
+ | ---------- | ---------------------------------------- | ----------------------------------------------------------------- |
529
+ | format | `@meng-xi/vite-plugin/common/format` | Date formatting, name conversion, template parsing, HTML escaping |
530
+ | fs | `@meng-xi/vite-plugin/common/fs` | File copying, directory traversal, concurrency control |
531
+ | html | `@meng-xi/vite-plugin/common/html` | HTML injection (injectBeforeTag, injectHeadAndBody, etc.) |
532
+ | object | `@meng-xi/vite-plugin/common/object` | Deep merge objects |
533
+ | script | `@meng-xi/vite-plugin/common/script` | Callback wrapping, script tag detection, identifier validation |
534
+ | validation | `@meng-xi/vite-plugin/common/validation` | Global name validation, XSS prevention, enum validation, etc. |
535
+
536
+ ---
537
+
950
538
  ## Sub-path Exports
951
539
 
952
540
  Support importing modules on demand to reduce bundle size:
953
541
 
954
542
  ```typescript
955
- // Full import
956
543
  import { buildProgress, copyFile, htmlInject, loadingManager, BasePlugin, Logger } from '@meng-xi/vite-plugin'
957
544
 
958
- // Module-level import
959
545
  import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin/factory'
960
546
  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'
547
+ import { buildProgress, compressAssets, copyFile, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin/plugins'
548
+ import { Validator, readFileContent, writeFileContent, injectHeadAndBody, deepMerge } from '@meng-xi/vite-plugin/common'
963
549
 
964
- // Type imports (on-demand type definitions from sub-paths)
965
550
  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'
551
+ import type { BuildProgressOptions, CompressAssetsOptions, GenerateVersionOptions, VersionUpdateCheckerOptions, HtmlInjectOptions, FaviconManagerOptions, LoadingManagerOptions } from '@meng-xi/vite-plugin/plugins'
967
552
  import type { DateFormatOptions } from '@meng-xi/vite-plugin/common/format'
968
553
  import type { HtmlInjectResult, DualInjectResult } from '@meng-xi/vite-plugin/common/html'
969
554
  import type { CopyOptions, CopyResult } from '@meng-xi/vite-plugin/common/fs'
970
555
  ```
971
556
 
557
+ **All available sub-paths:**
558
+
559
+ ```
560
+ @meng-xi/vite-plugin
561
+ @meng-xi/vite-plugin/factory
562
+ @meng-xi/vite-plugin/logger
563
+ @meng-xi/vite-plugin/plugins
564
+ @meng-xi/vite-plugin/plugins/build-progress
565
+ @meng-xi/vite-plugin/plugins/compress-assets
566
+ @meng-xi/vite-plugin/plugins/copy-file
567
+ @meng-xi/vite-plugin/plugins/favicon-manager
568
+ @meng-xi/vite-plugin/plugins/generate-router
569
+ @meng-xi/vite-plugin/plugins/generate-version
570
+ @meng-xi/vite-plugin/plugins/html-inject
571
+ @meng-xi/vite-plugin/plugins/loading-manager
572
+ @meng-xi/vite-plugin/plugins/version-update-checker
573
+ @meng-xi/vite-plugin/common
574
+ @meng-xi/vite-plugin/common/format
575
+ @meng-xi/vite-plugin/common/fs
576
+ @meng-xi/vite-plugin/common/html
577
+ @meng-xi/vite-plugin/common/object
578
+ @meng-xi/vite-plugin/common/script
579
+ @meng-xi/vite-plugin/common/validation
580
+ ```
581
+
972
582
  ## Changelog
973
583
 
974
584
  View [GitHub Releases](https://github.com/MengXi-Studio/vite-plugin/releases)