@meng-xi/vite-plugin 0.1.0 → 0.1.1

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 (105) hide show
  1. package/README-en.md +170 -49
  2. package/README.md +149 -29
  3. package/dist/common/format/index.cjs +1 -0
  4. package/dist/common/format/index.d.cts +156 -0
  5. package/dist/common/format/index.d.mts +156 -0
  6. package/dist/common/format/index.d.ts +156 -0
  7. package/dist/common/format/index.mjs +1 -0
  8. package/dist/common/fs/index.cjs +1 -0
  9. package/dist/common/fs/index.d.cts +150 -0
  10. package/dist/common/fs/index.d.mts +150 -0
  11. package/dist/common/fs/index.d.ts +150 -0
  12. package/dist/common/fs/index.mjs +1 -0
  13. package/dist/common/html/index.cjs +2 -0
  14. package/dist/common/html/index.d.cts +109 -0
  15. package/dist/common/html/index.d.mts +109 -0
  16. package/dist/common/html/index.d.ts +109 -0
  17. package/dist/common/html/index.mjs +2 -0
  18. package/dist/common/index.cjs +1 -1
  19. package/dist/common/index.d.cts +7 -417
  20. package/dist/common/index.d.mts +7 -417
  21. package/dist/common/index.d.ts +7 -417
  22. package/dist/common/index.mjs +1 -1
  23. package/dist/common/object/index.cjs +1 -0
  24. package/dist/common/object/index.d.cts +30 -0
  25. package/dist/common/object/index.d.mts +30 -0
  26. package/dist/common/object/index.d.ts +30 -0
  27. package/dist/common/object/index.mjs +1 -0
  28. package/dist/common/script/index.cjs +1 -0
  29. package/dist/common/script/index.d.cts +54 -0
  30. package/dist/common/script/index.d.mts +54 -0
  31. package/dist/common/script/index.d.ts +54 -0
  32. package/dist/common/script/index.mjs +1 -0
  33. package/dist/common/validation/index.cjs +1 -0
  34. package/dist/common/validation/index.d.cts +93 -0
  35. package/dist/common/validation/index.d.mts +93 -0
  36. package/dist/common/validation/index.d.ts +93 -0
  37. package/dist/common/validation/index.mjs +1 -0
  38. package/dist/factory/index.cjs +1 -1
  39. package/dist/factory/index.d.cts +1 -1
  40. package/dist/factory/index.d.mts +1 -1
  41. package/dist/factory/index.d.ts +1 -1
  42. package/dist/factory/index.mjs +1 -1
  43. package/dist/index.cjs +1 -1
  44. package/dist/index.d.cts +15 -3
  45. package/dist/index.d.mts +15 -3
  46. package/dist/index.d.ts +15 -3
  47. package/dist/index.mjs +1 -1
  48. package/dist/plugins/buildProgress/index.cjs +2 -0
  49. package/dist/plugins/buildProgress/index.d.cts +187 -0
  50. package/dist/plugins/buildProgress/index.d.mts +187 -0
  51. package/dist/plugins/buildProgress/index.d.ts +187 -0
  52. package/dist/plugins/buildProgress/index.mjs +2 -0
  53. package/dist/plugins/copyFile/index.cjs +1 -0
  54. package/dist/plugins/copyFile/index.d.cts +78 -0
  55. package/dist/plugins/copyFile/index.d.mts +78 -0
  56. package/dist/plugins/copyFile/index.d.ts +78 -0
  57. package/dist/plugins/copyFile/index.mjs +1 -0
  58. package/dist/plugins/faviconManager/index.cjs +1 -0
  59. package/dist/plugins/faviconManager/index.d.cts +143 -0
  60. package/dist/plugins/faviconManager/index.d.mts +143 -0
  61. package/dist/plugins/faviconManager/index.d.ts +143 -0
  62. package/dist/plugins/faviconManager/index.mjs +1 -0
  63. package/dist/plugins/generateRouter/index.cjs +35 -0
  64. package/dist/plugins/generateRouter/index.d.cts +215 -0
  65. package/dist/plugins/generateRouter/index.d.mts +215 -0
  66. package/dist/plugins/generateRouter/index.d.ts +215 -0
  67. package/dist/plugins/generateRouter/index.mjs +35 -0
  68. package/dist/plugins/generateVersion/index.cjs +1 -0
  69. package/dist/plugins/generateVersion/index.d.cts +184 -0
  70. package/dist/plugins/generateVersion/index.d.mts +184 -0
  71. package/dist/plugins/generateVersion/index.d.ts +184 -0
  72. package/dist/plugins/generateVersion/index.mjs +1 -0
  73. package/dist/plugins/htmlInject/index.cjs +7 -0
  74. package/dist/plugins/htmlInject/index.d.cts +278 -0
  75. package/dist/plugins/htmlInject/index.d.mts +278 -0
  76. package/dist/plugins/htmlInject/index.d.ts +278 -0
  77. package/dist/plugins/htmlInject/index.mjs +7 -0
  78. package/dist/plugins/index.cjs +1 -1
  79. package/dist/plugins/index.d.cts +10 -1714
  80. package/dist/plugins/index.d.mts +10 -1714
  81. package/dist/plugins/index.d.ts +10 -1714
  82. package/dist/plugins/index.mjs +1 -1
  83. package/dist/plugins/loadingManager/index.cjs +487 -0
  84. package/dist/plugins/loadingManager/index.d.cts +769 -0
  85. package/dist/plugins/loadingManager/index.d.mts +769 -0
  86. package/dist/plugins/loadingManager/index.d.ts +769 -0
  87. package/dist/plugins/loadingManager/index.mjs +487 -0
  88. package/dist/plugins/versionUpdateChecker/index.cjs +185 -0
  89. package/dist/plugins/versionUpdateChecker/index.d.cts +200 -0
  90. package/dist/plugins/versionUpdateChecker/index.d.mts +200 -0
  91. package/dist/plugins/versionUpdateChecker/index.d.ts +200 -0
  92. package/dist/plugins/versionUpdateChecker/index.mjs +185 -0
  93. package/dist/shared/vite-plugin.Bcg6RW2N.cjs +3 -0
  94. package/dist/shared/{vite-plugin.CiHfwMiN.d.ts → vite-plugin.DRRlWY8P.d.cts} +50 -0
  95. package/dist/shared/{vite-plugin.CiHfwMiN.d.cts → vite-plugin.DRRlWY8P.d.mts} +50 -0
  96. package/dist/shared/{vite-plugin.CiHfwMiN.d.mts → vite-plugin.DRRlWY8P.d.ts} +50 -0
  97. package/dist/shared/{vite-plugin.B88RyRN8.mjs → vite-plugin.DcExl6jd.mjs} +2 -2
  98. package/package.json +72 -2
  99. package/dist/shared/vite-plugin.CawoITTT.cjs +0 -1
  100. package/dist/shared/vite-plugin.D6Law9Ke.mjs +0 -706
  101. package/dist/shared/vite-plugin.D8L9KzuW.cjs +0 -706
  102. package/dist/shared/vite-plugin.DFjf9wFM.mjs +0 -2
  103. package/dist/shared/vite-plugin.DSb6XzBn.mjs +0 -1
  104. package/dist/shared/vite-plugin.IGZeStMa.cjs +0 -3
  105. package/dist/shared/vite-plugin.Tab4qcIM.cjs +0 -2
package/README-en.md CHANGED
@@ -15,7 +15,8 @@
15
15
 
16
16
  ## Features
17
17
 
18
- - **Ready to Use** - Provides 7 practical plugins covering build progress display, file copying, router generation, version management, version update checking, icon injection, and global Loading state management
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
19
20
  - **Plugin Development Framework** - Exports core components like BasePlugin, Logger, Validator for building custom Vite plugins
20
21
  - **Complete Lifecycle** - Supports initialization, config resolution, destroy lifecycle management with automatic hook composition
21
22
  - **Type Safe** - Complete TypeScript type definitions with configuration validators ensuring parameter correctness
@@ -46,7 +47,7 @@ pnpm add @meng-xi/vite-plugin -D
46
47
 
47
48
  ```typescript
48
49
  import { defineConfig } from 'vite'
49
- import { buildProgress, copyFile, generateRouter, generateVersion, versionUpdateChecker, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
50
+ import { buildProgress, copyFile, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
50
51
 
51
52
  export default defineConfig({
52
53
  plugins: [
@@ -74,6 +75,17 @@ export default defineConfig({
74
75
  // Version update checker (works with generateVersion)
75
76
  versionUpdateChecker(),
76
77
 
78
+ // HTML content injection
79
+ htmlInject({
80
+ rules: [
81
+ {
82
+ id: 'meta-description',
83
+ content: '<meta name="description" content="My App">',
84
+ position: 'head-end'
85
+ }
86
+ ]
87
+ }),
88
+
77
89
  // Inject website icon (supports string shorthand)
78
90
  faviconManager('/assets'),
79
91
 
@@ -102,15 +114,16 @@ console.log(routerPlugin.pluginInstance?.options)
102
114
 
103
115
  ## Built-in Plugins
104
116
 
105
- | Plugin | Description |
106
- | -------------------- | ---------------------------------------------------------------------------------------- |
107
- | buildProgress | Real-time build progress bar in terminal, supports bar / spinner / minimal |
108
- | copyFile | Copy files or directories after build, supports incremental copying |
109
- | generateRouter | Auto-generate router config from pages.json (uni-app) |
110
- | generateVersion | Auto-generate version numbers, supports file output and global variable injection |
111
- | versionUpdateChecker | Runtime version update checking with multiple prompt styles and custom callbacks |
112
- | faviconManager | Manage website favicon links injection into HTML files, supports string shorthand config |
113
- | loadingManager | Global Loading state management with request interception and white-screen Loading |
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 |
114
127
 
115
128
  ### buildProgress
116
129
 
@@ -381,6 +394,99 @@ versionUpdateChecker({
381
394
  versionUpdateChecker({ enableInDev: true })
382
395
  ```
383
396
 
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.
400
+
401
+ **Injection positions:**
402
+
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 |
412
+
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 |
420
+
421
+ **InjectRule**
422
+
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 |
442
+
443
+ **SecurityConfig**
444
+
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 |
452
+
453
+ ```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
+ htmlInject({
461
+ 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
+ }
469
+ ]
470
+ })
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
+ ```
489
+
384
490
  ### faviconManager
385
491
 
386
492
  Inject website icon links into the head of HTML files during the Vite build process. Supports string shorthand config.
@@ -604,30 +710,40 @@ window.__LOADING_MANAGER__.disablePointerEvents()
604
710
  Exported via `@meng-xi/vite-plugin/common`, reusable in custom plugins:
605
711
 
606
712
  ```typescript
607
- import { deepMerge, formatDate, parseTemplate, toCamelCase, toPascalCase, stripJsonComments, generateRandomHash, Validator } from '@meng-xi/vite-plugin/common'
608
- import { readFileContent, writeFileContent, fileExists, copySourceToTarget } from '@meng-xi/vite-plugin/common'
609
- import { injectBeforeTag, injectHtmlByPriority } from '@meng-xi/vite-plugin/common'
610
- import { makeCallback, containsScriptTag, validateIdentifierName } from '@meng-xi/vite-plugin/common'
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'
611
718
  ```
612
719
 
613
- | Function | Description |
614
- | -------------------------- | ----------------------------------------------------------------------------- |
615
- | `deepMerge()` | Deep merge objects (undefined skipped, arrays overwritten) |
616
- | `formatDate()` | Format date with `{YYYY}`, `{MM}`, `{DD}` etc. placeholders |
617
- | `parseTemplate()` | Parse template string, replace placeholders |
618
- | `toCamelCase()` | Convert to camelCase |
619
- | `toPascalCase()` | Convert to PascalCase |
620
- | `stripJsonComments()` | Remove comments from JSON string |
621
- | `generateRandomHash()` | Generate random hash string (1-64 characters) |
622
- | `readFileContent()` | Async read file content |
623
- | `writeFileContent()` | Async write file content |
624
- | `fileExists()` | Async check if file exists |
625
- | `copySourceToTarget()` | Copy files or directories with incremental copy and concurrency |
626
- | `injectBeforeTag()` | Inject code before a specified closing HTML tag |
627
- | `injectHtmlByPriority()` | Inject code into HTML by priority (`</head>` `</body>` → `</html>`) |
628
- | `makeCallback()` | Wrap callback function body as safe function expression (with try-catch) |
629
- | `containsScriptTag()` | Detect if a string contains `<script>` tags |
630
- | `validateIdentifierName()` | Validate string as a legal JavaScript identifier, prevent prototype pollution |
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` |
631
747
 
632
748
  ## Plugin Development Framework
633
749
 
@@ -745,18 +861,21 @@ validator
745
861
  .validate()
746
862
  ```
747
863
 
748
- | Method | Description |
749
- | ------------ | --------------------------------------------------------------- |
750
- | `field()` | Specify the field to validate |
751
- | `required()` | Mark field as required |
752
- | `string()` | Validate field value is a string type |
753
- | `boolean()` | Validate field value is a boolean type |
754
- | `number()` | Validate field value is a number type |
755
- | `array()` | Validate field value is an array type |
756
- | `object()` | Validate field value is an object type |
757
- | `default()` | Set default value for field (only when value is undefined/null) |
758
- | `custom()` | Validate field value with a custom function |
759
- | `validate()` | Execute validation, throws error on failure |
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 |
760
879
 
761
880
  ### Logger
762
881
 
@@ -834,18 +953,20 @@ Support importing modules on demand to reduce bundle size:
834
953
 
835
954
  ```typescript
836
955
  // Full import
837
- import { buildProgress, copyFile, loadingManager, BasePlugin, Logger } from '@meng-xi/vite-plugin'
956
+ import { buildProgress, copyFile, htmlInject, loadingManager, BasePlugin, Logger } from '@meng-xi/vite-plugin'
838
957
 
839
958
  // Module-level import
840
959
  import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin/factory'
841
960
  import { Logger } from '@meng-xi/vite-plugin/logger'
842
- import { buildProgress, copyFile, generateRouter, loadingManager } from '@meng-xi/vite-plugin/plugins'
961
+ import { buildProgress, copyFile, generateRouter, htmlInject, loadingManager } from '@meng-xi/vite-plugin/plugins'
843
962
  import { Validator, readFileContent, writeFileContent } from '@meng-xi/vite-plugin/common'
844
963
 
845
964
  // Type imports (on-demand type definitions from sub-paths)
846
965
  import type { PluginWithInstance, PluginFactory, BasePluginOptions } from '@meng-xi/vite-plugin/factory'
847
- import type { BuildProgressOptions, GenerateVersionOptions, VersionUpdateCheckerOptions, FaviconManagerOptions, LoadingManagerOptions, Icon } from '@meng-xi/vite-plugin/plugins'
848
- import type { DateFormatOptions } from '@meng-xi/vite-plugin/common'
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'
849
970
  ```
850
971
 
851
972
  ## Changelog
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  ## 特性
17
17
 
18
- - **开箱即用** - 提供 7 个实用插件,覆盖构建进度展示、文件复制、路由生成、版本管理、版本更新检查、图标注入、全局 Loading 状态管理等常见场景
18
+ - **开箱即用** - 提供 8 个实用插件,覆盖构建进度展示、文件复制、路由生成、版本管理、版本更新检查、HTML 注入、图标注入、全局 Loading 状态管理等常见场景
19
19
  - **插件开发框架** - 导出 BasePlugin、Logger、Validator 等核心组件,快速构建符合规范的自定义 Vite 插件
20
20
  - **完整生命周期** - 支持初始化、配置解析、销毁等生命周期管理,自动组合钩子逻辑
21
21
  - **类型安全** - 完整的 TypeScript 类型定义,配置验证器确保参数正确性
@@ -46,7 +46,7 @@ pnpm add @meng-xi/vite-plugin -D
46
46
 
47
47
  ```typescript
48
48
  import { defineConfig } from 'vite'
49
- import { buildProgress, copyFile, generateRouter, generateVersion, versionUpdateChecker, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
49
+ import { buildProgress, copyFile, generateRouter, generateVersion, versionUpdateChecker, htmlInject, faviconManager, loadingManager } from '@meng-xi/vite-plugin'
50
50
 
51
51
  export default defineConfig({
52
52
  plugins: [
@@ -74,6 +74,17 @@ export default defineConfig({
74
74
  // 版本更新检查(配合 generateVersion 使用)
75
75
  versionUpdateChecker(),
76
76
 
77
+ // HTML 内容注入
78
+ htmlInject({
79
+ rules: [
80
+ {
81
+ id: 'meta-description',
82
+ content: '<meta name="description" content="My App">',
83
+ position: 'head-end'
84
+ }
85
+ ]
86
+ }),
87
+
77
88
  // 注入网站图标(支持字符串简写)
78
89
  faviconManager('/assets'),
79
90
 
@@ -109,6 +120,7 @@ console.log(routerPlugin.pluginInstance?.options)
109
120
  | generateRouter | 根据 pages.json 自动生成路由配置(uni-app) |
110
121
  | generateVersion | 自动生成版本号,支持文件输出和全局变量注入 |
111
122
  | versionUpdateChecker | 运行时版本更新检查,支持多种提示样式和自定义回调 |
123
+ | htmlInject | HTML 内容注入,支持多种位置、条件注入、模板变量替换和安全过滤 |
112
124
  | faviconManager | 管理网站图标(favicon)链接注入到 HTML 文件,支持字符串简写配置 |
113
125
  | loadingManager | 全局 Loading 状态管理,支持请求拦截和白屏 Loading |
114
126
 
@@ -214,7 +226,7 @@ copyFile({
214
226
  | watch | `boolean` | `true` | 是否监听变化自动重新生成 |
215
227
  | metaMapping | `Record<string, string>` | - | 页面 style 字段到 meta 的映射 |
216
228
  | exportTypes | `boolean` | `true` | 是否导出类型定义 |
217
- | preserveRouteChanges | `boolean` | `true'` | 是否保留用户对 routes 的修改 |
229
+ | preserveRouteChanges | `boolean` | `true` | 是否保留用户对 routes 的修改 |
218
230
 
219
231
  > 默认 `metaMapping` 为 `{ navigationBarTitleText: 'title', requireAuth: 'requireAuth' }`,自动将页面样式字段映射到路由元信息。当 `nameStrategy` 为 `'custom'` 时,必须提供 `customNameGenerator`。
220
232
 
@@ -379,6 +391,99 @@ versionUpdateChecker({
379
391
  versionUpdateChecker({ enableInDev: true })
380
392
  ```
381
393
 
394
+ ### htmlInject
395
+
396
+ 在 Vite 构建过程中根据配置规则将 HTML 内容注入到目标文件中,支持多种注入位置、条件注入、模板变量替换和安全过滤。
397
+
398
+ **注入位置:**
399
+
400
+ | 位置 | 说明 |
401
+ | ------------------ | -------------------------- |
402
+ | `head-start` | 注入到 `<head>` 标签开始后 |
403
+ | `head-end` | 注入到 `</head>` 标签前 |
404
+ | `body-start` | 注入到 `<body>` 标签开始后 |
405
+ | `body-end` | 注入到 `</body>` 标签前 |
406
+ | `before-selector` | 注入到选择器匹配内容前 |
407
+ | `after-selector` | 注入到选择器匹配内容后 |
408
+ | `replace-selector` | 替换选择器匹配的内容 |
409
+
410
+ | 选项 | 类型 | 默认值 | 描述 |
411
+ | ------------ | ------------------------ | -------------- | -------------------------- |
412
+ | targetFile | `string` | `'index.html'` | 目标 HTML 文件路径或文件名 |
413
+ | rules | `InjectRule[]` | - | 注入规则数组(必填) |
414
+ | security | `SecurityConfig` | - | 安全过滤配置 |
415
+ | templateVars | `Record<string, string>` | - | 全局模板变量 |
416
+ | logInjection | `boolean` | `true` | 是否输出注入日志 |
417
+
418
+ **InjectRule**
419
+
420
+ | 属性 | 类型 | 默认值 | 描述 |
421
+ | -------------------- | ------------------------ | ---------- | ----------------------------------- |
422
+ | id | `string` | - | 规则唯一标识,用于日志和调试 |
423
+ | content | `string` | - | 要注入的 HTML 内容(必填) |
424
+ | position | `InjectPosition` | - | 注入位置(必填) |
425
+ | selector | `string` | - | 选择器字符串(selector 位置时必填) |
426
+ | selectorMatch | `'string'` \| `'regex'` | `'string'` | 选择器匹配模式 |
427
+ | priority | `number` | `100` | 优先级,数值越小越先执行 |
428
+ | condition | `InjectCondition` | - | 注入条件 |
429
+ | templateVars | `Record<string, string>` | - | 规则级模板变量(覆盖全局) |
430
+ | allowScriptInjection | `boolean` | `false` | 是否允许注入脚本等危险内容 |
431
+
432
+ **InjectCondition**
433
+
434
+ | 属性 | 类型 | 默认值 | 描述 |
435
+ | ------ | ------------------------------------------- | ------- | ------------------ |
436
+ | type | `'env'` \| `'file-contains'` \| `'custom'` | - | 条件类型(必填) |
437
+ | value | `string` \| `((...args: any[]) => boolean)` | - | 条件值(必填) |
438
+ | negate | `boolean` | `false` | 是否对条件结果取反 |
439
+
440
+ **SecurityConfig**
441
+
442
+ | 属性 | 类型 | 默认值 | 描述 |
443
+ | ------------------------ | ---------- | ------ | -------------------- |
444
+ | blockDangerousTags | `boolean` | `true` | 是否阻止危险标签 |
445
+ | blockDangerousAttributes | `boolean` | `true` | 是否阻止危险属性 |
446
+ | allowedTags | `string[]` | - | 允许通过的标签白名单 |
447
+ | blockedTags | `string[]` | - | 自定义阻止标签列表 |
448
+ | blockedAttributes | `string[]` | - | 自定义阻止属性列表 |
449
+
450
+ ```typescript
451
+ // 基本使用
452
+ htmlInject({
453
+ rules: [{ id: 'meta-desc', content: '<meta name="description" content="My App">', position: 'head-end' }]
454
+ })
455
+
456
+ // 条件注入(仅生产环境)
457
+ htmlInject({
458
+ rules: [
459
+ {
460
+ id: 'analytics',
461
+ content: '<script src="/analytics.js"></script>',
462
+ position: 'body-end',
463
+ condition: { type: 'env', value: 'PRODUCTION' },
464
+ allowScriptInjection: true
465
+ }
466
+ ]
467
+ })
468
+
469
+ // 模板变量替换
470
+ htmlInject({
471
+ templateVars: { appName: 'My App', version: '1.0.0' },
472
+ rules: [{ id: 'meta', content: '<meta name="description" content="{{appName}}">', position: 'head-end' }]
473
+ })
474
+
475
+ // 选择器注入
476
+ htmlInject({
477
+ rules: [{ id: 'replace-title', content: '<title>New Title</title>', position: 'replace-selector', selector: '<title>.*</title>', selectorMatch: 'regex' }]
478
+ })
479
+
480
+ // 安全配置
481
+ htmlInject({
482
+ security: { blockDangerousTags: true, allowedTags: ['iframe'] },
483
+ rules: [{ id: 'embed', content: '<iframe src="https://example.com"></iframe>', position: 'body-end' }]
484
+ })
485
+ ```
486
+
382
487
  ### faviconManager
383
488
 
384
489
  在 Vite 构建过程中将网站图标链接注入到 HTML 文件的 head 中。支持字符串简写配置。
@@ -602,30 +707,40 @@ window.__LOADING_MANAGER__.disablePointerEvents()
602
707
  通过 `@meng-xi/vite-plugin/common` 导出,可在自定义插件中复用:
603
708
 
604
709
  ```typescript
605
- import { deepMerge, formatDate, parseTemplate, toCamelCase, toPascalCase, stripJsonComments, generateRandomHash, Validator } from '@meng-xi/vite-plugin/common'
606
- import { readFileContent, writeFileContent, fileExists, copySourceToTarget } from '@meng-xi/vite-plugin/common'
607
- import { injectBeforeTag, injectHtmlByPriority } from '@meng-xi/vite-plugin/common'
608
- import { makeCallback, containsScriptTag, validateIdentifierName } from '@meng-xi/vite-plugin/common'
710
+ import { deepMerge, formatDate, parseTemplate, toCamelCase, toPascalCase, stripJsonComments, generateRandomHash, escapeHtmlAttr, Validator } from '@meng-xi/vite-plugin/common'
711
+ import { readFileContent, writeFileContent, fileExists, copySourceToTarget } from '@meng-xi/vite-plugin/common/fs'
712
+ import { injectBeforeTag, injectHtmlByPriority, injectBeforeTagWithFallback, injectHeadAndBody } from '@meng-xi/vite-plugin/common/html'
713
+ import { makeCallback, containsScriptTag, validateIdentifierName } from '@meng-xi/vite-plugin/common/script'
714
+ import { validateGlobalName, validateNoScriptInTemplate, validateCallbackFields, validateNonNegativeNumber, validateNestedDuration, validateEnumValue } from '@meng-xi/vite-plugin/common/validation'
609
715
  ```
610
716
 
611
- | 函数 | 说明 |
612
- | -------------------------- | --------------------------------------------------------------- |
613
- | `deepMerge()` | 深度合并对象(undefined 不覆盖,数组直接覆盖) |
614
- | `formatDate()` | 格式化日期,支持 `{YYYY}`, `{MM}`, `{DD}` 等占位符 |
615
- | `parseTemplate()` | 解析模板字符串,替换占位符 |
616
- | `toCamelCase()` | 转换为驼峰命名(camelCase) |
617
- | `toPascalCase()` | 转换为帕斯卡命名(PascalCase) |
618
- | `stripJsonComments()` | 移除 JSON 字符串中的注释 |
619
- | `generateRandomHash()` | 生成随机哈希字符串(1-64 位) |
620
- | `readFileContent()` | 异步读取文件内容 |
621
- | `writeFileContent()` | 异步写入文件内容 |
622
- | `fileExists()` | 异步检查文件是否存在 |
623
- | `copySourceToTarget()` | 复制文件或目录,支持增量复制和并发控制 |
624
- | `injectBeforeTag()` | HTML 指定闭合标签前注入代码 |
625
- | `injectHtmlByPriority()` | 按优先级向 HTML 中注入代码(`</head>` → `</body>` → `</html>`) |
626
- | `makeCallback()` | 将回调函数体包装为安全的函数表达式(含 try-catch) |
627
- | `containsScriptTag()` | 检测字符串是否包含 `<script>` 标签 |
628
- | `validateIdentifierName()` | 验证字符串是否为合法的 JavaScript 标识符,防止原型污染 |
717
+ | 函数 | 说明 | 子路径 |
718
+ | ------------------------------- | --------------------------------------------------------------- | ------------------- |
719
+ | `deepMerge()` | 深度合并对象(undefined 不覆盖,数组直接覆盖) | `common/object` |
720
+ | `formatDate()` | 格式化日期,支持 `{YYYY}`, `{MM}`, `{DD}` 等占位符 | `common/format` |
721
+ | `parseTemplate()` | 解析模板字符串,替换占位符 | `common/format` |
722
+ | `toCamelCase()` | 转换为驼峰命名(camelCase) | `common/format` |
723
+ | `toPascalCase()` | 转换为帕斯卡命名(PascalCase) | `common/format` |
724
+ | `stripJsonComments()` | 移除 JSON 字符串中的注释 | `common/format` |
725
+ | `generateRandomHash()` | 生成随机哈希字符串(1-64 位) | `common/format` |
726
+ | `escapeHtmlAttr()` | 转义 HTML 属性值中的特殊字符,防止 XSS 注入 | `common/format` |
727
+ | `readFileContent()` | 异步读取文件内容 | `common/fs` |
728
+ | `writeFileContent()` | 异步写入文件内容 | `common/fs` |
729
+ | `fileExists()` | 异步检查文件是否存在 | `common/fs` |
730
+ | `copySourceToTarget()` | 复制文件或目录,支持增量复制和并发控制 | `common/fs` |
731
+ | `injectBeforeTag()` | HTML 指定闭合标签前注入代码 | `common/html` |
732
+ | `injectHtmlByPriority()` | 按优先级向 HTML 中注入代码(`</head>` → `</body>` → `</html>`) | `common/html` |
733
+ | `injectBeforeTagWithFallback()` | 带回退策略的 HTML 注入(`</body>` → `</html>` → 末尾) | `common/html` |
734
+ | `injectHeadAndBody()` | 双区域 HTML 注入(head + body) | `common/html` |
735
+ | `makeCallback()` | 将回调函数体包装为安全的函数表达式(含 try-catch) | `common/script` |
736
+ | `containsScriptTag()` | 检测字符串是否包含 `<script>` 标签 | `common/script` |
737
+ | `validateIdentifierName()` | 验证字符串是否为合法的 JavaScript 标识符,防止原型污染 | `common/script` |
738
+ | `validateGlobalName()` | 验证全局变量名的合法性 | `common/validation` |
739
+ | `validateNoScriptInTemplate()` | 验证模板字符串不包含 script 标签(XSS 防护) | `common/validation` |
740
+ | `validateCallbackFields()` | 验证回调字段不包含 script 标签 | `common/validation` |
741
+ | `validateNonNegativeNumber()` | 验证数值为非负数 | `common/validation` |
742
+ | `validateNestedDuration()` | 验证嵌套配置项的 duration 合法性 | `common/validation` |
743
+ | `validateEnumValue()` | 验证字符串值是否在允许的枚举列表中 | `common/validation` |
629
744
 
630
745
  ## 插件开发框架
631
746
 
@@ -752,6 +867,9 @@ validator
752
867
  | `number()` | 验证字段值是否为数字类型 |
753
868
  | `array()` | 验证字段值是否为数组类型 |
754
869
  | `object()` | 验证字段值是否为对象类型 |
870
+ | `enum()` | 验证字段值是否在允许的枚举列表中 |
871
+ | `minValue()` | 验证数字字段值是否不小于指定最小值 |
872
+ | `maxValue()` | 验证数字字段值是否不大于指定最大值 |
755
873
  | `default()` | 为字段设置默认值(仅当值为 undefined/null 时生效) |
756
874
  | `custom()` | 使用自定义函数验证字段值 |
757
875
  | `validate()` | 执行验证,失败时抛出错误 |
@@ -832,18 +950,20 @@ export const myPluginWithNormalizer = createPluginFactory(MyPlugin, opt => (type
832
950
 
833
951
  ```typescript
834
952
  // 完整导入
835
- import { buildProgress, copyFile, loadingManager, BasePlugin, Logger } from '@meng-xi/vite-plugin'
953
+ import { buildProgress, copyFile, htmlInject, loadingManager, BasePlugin, Logger } from '@meng-xi/vite-plugin'
836
954
 
837
955
  // 按模块导入
838
956
  import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin/factory'
839
957
  import { Logger } from '@meng-xi/vite-plugin/logger'
840
- import { buildProgress, copyFile, generateRouter, loadingManager } from '@meng-xi/vite-plugin/plugins'
958
+ import { buildProgress, copyFile, generateRouter, htmlInject, loadingManager } from '@meng-xi/vite-plugin/plugins'
841
959
  import { Validator, readFileContent, writeFileContent } from '@meng-xi/vite-plugin/common'
842
960
 
843
961
  // 类型导入(从子路径按需导入类型定义)
844
962
  import type { PluginWithInstance, PluginFactory, BasePluginOptions } from '@meng-xi/vite-plugin/factory'
845
- import type { BuildProgressOptions, GenerateVersionOptions, VersionUpdateCheckerOptions, FaviconManagerOptions, LoadingManagerOptions, Icon } from '@meng-xi/vite-plugin/plugins'
846
- import type { DateFormatOptions } from '@meng-xi/vite-plugin/common'
963
+ import type { BuildProgressOptions, GenerateVersionOptions, VersionUpdateCheckerOptions, HtmlInjectOptions, InjectRule, FaviconManagerOptions, LoadingManagerOptions, Icon } from '@meng-xi/vite-plugin/plugins'
964
+ import type { DateFormatOptions } from '@meng-xi/vite-plugin/common/format'
965
+ import type { HtmlInjectResult, DualInjectResult } from '@meng-xi/vite-plugin/common/html'
966
+ import type { CopyOptions, CopyResult } from '@meng-xi/vite-plugin/common/fs'
847
967
  ```
848
968
 
849
969
  ## 更新日志
@@ -0,0 +1 @@
1
+ "use strict";const crypto=require("crypto");function padNumber(e,t=2){return e.toString().padStart(t,"0")}function generateRandomHash(e=8){const t=Math.max(1,Math.min(64,e));return crypto.randomBytes(Math.ceil(t/2)).toString("hex").slice(0,t)}function getDateFormatParams(e=new Date){return{YYYY:e.getFullYear().toString(),YY:e.getFullYear().toString().slice(-2),MM:padNumber(e.getMonth()+1),DD:padNumber(e.getDate()),HH:padNumber(e.getHours()),mm:padNumber(e.getMinutes()),ss:padNumber(e.getSeconds()),SSS:padNumber(e.getMilliseconds(),3),timestamp:e.getTime().toString()}}function formatDate(e,t){const r=getDateFormatParams(e);let a=t;for(const[o,n]of Object.entries(r))a=a.replace(new RegExp(`\\{${o}\\}`,"g"),n);return a}function parseTemplate(e,t){let r=e;for(const[a,o]of Object.entries(t))r=r.replace(new RegExp(`\\{${a}\\}`,"g"),o);return r}function toCamelCase(e,t=/[/-]/){return e.replace(/^\/+/,"").split(t).filter(Boolean).map((r,a)=>a===0?r.toLowerCase():r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join("")}function toPascalCase(e,t=/[/-]/){return e.replace(/^\/+/,"").split(t).filter(Boolean).map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join("")}function stripJsonComments(e){return e.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"")}function escapeHtmlAttr(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}exports.escapeHtmlAttr=escapeHtmlAttr,exports.formatDate=formatDate,exports.generateRandomHash=generateRandomHash,exports.getDateFormatParams=getDateFormatParams,exports.padNumber=padNumber,exports.parseTemplate=parseTemplate,exports.stripJsonComments=stripJsonComments,exports.toCamelCase=toCamelCase,exports.toPascalCase=toPascalCase;