npm.io
0.5.2 • Published 5d ago

@zhoumutou/vite-plugin-inline

Licence
MIT
Version
0.5.2
Deps
1
Size
30 kB
Vulns
0
Weekly
76

@zhoumutou/vite-plugin-inline

npm version weekly downloads license unpacked size

一个将构建产物中的 CSS 与 JavaScript 资源内联到 HTML 的 Vite 插件。常见情况下会输出单个、可独立部署的 HTML;若动态 chunk 需要保留外链,或插件为了安全回退保留入口脚本,则会继续输出必要的外部 JavaScript 文件。

English | 中文

特性

  • 将所有 <link rel="stylesheet"> 内联为 <style> 放入 HTML
  • 将入口 JS 以内联方式注入为单个 <script type="module">
    • 若入口仍存在静态 import,会在内存中重打包以移除这些静态 import
    • 可选:通过 inlineDynamicImports 将动态 import 也一并合并进内联脚本
  • 从最终产物中删除已成功内联的 JS/CSS 文件及其 sourcemap
  • 清理指向已内联 chunk 的 <link rel="modulepreload">
  • 保留被替换的 emitted HTML 标签上的部分属性(style 保留 nonce/id/mediascript 保留 nonce/id
  • 内联内容会转义 </script></style>,避免提前闭合

安装

# npm
npm install @zhoumutou/vite-plugin-inline -D

# yarn
yarn add @zhoumutou/vite-plugin-inline -D

# pnpm
pnpm add @zhoumutou/vite-plugin-inline -D

使用

import inline from '@zhoumutou/vite-plugin-inline'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    inline({
      // 可选配置
      // removeComments: true,          // 移除内联 CSS 的块级注释
      // cdataJs: false,                // 为内联 JS 包裹 <![CDATA[ ... ]]>
      // inlineDynamicImports: false,   // 合并动态导入到内联脚本
    }),
  ],
})

开发

pnpm install
pnpm check
pnpm test
pnpm build

当前发布产物为 ESM-only。

选项

interface Options {
  /**
   * 是否移除内联 CSS 的块级注释(/* ... */)。
   * JS 不做直接注释处理;当发生重打包时,上游最小化流程仍可能移除注释。
   * @default true
   */
  removeComments?: boolean
  /**
   * 是否使用 <![CDATA[ ... ]]> 包裹内联 JS,以兼容 XML/XHTML。
   * @default false
   */
  cdataJs?: boolean
  /**
   * 是否在内存重打包时,将动态导入也合并进最终的内联脚本。
   * 为 true 时,不会再额外产出 JS chunk。
   * @default false
   */
  inlineDynamicImports?: boolean
}

工作原理

  • 扫描输出的 HTML:
    • <link rel="stylesheet" href="*.css"> 替换为 <style>...</style>
    • 查找属于本次 bundle 的主 <script src="*.mjs|*.js">(优先 type="module"
  • 生成内联 JS:
    • 若入口无静态 import,直接复用已产出的 chunk 代码
    • 若存在静态 import,则在内存中重打包以移除它们
    • inlineDynamicImportstrue 时,同时合并动态 import
    • 若动态 import 保持外链,插件会重写内联入口中的导入路径,确保仍能正确指向产物中的 chunk
    • 若重打包失败,会保留原始外链入口 script 并给出 warning,而不是输出一个可能损坏的内联模块
  • 用内联内容替换原标签,然后:
    • 从 bundle 中删除实际已被内联的 JS/CSS 及其 .map
    • 移除指向已内联 chunk 的 <link rel="modulepreload">

示例

输入 HTML:

<link rel="stylesheet" href="style.css" />
<script type="module" src="main.js"></script>

输出 HTML:

<style>
  /* inlined CSS content */
</style>
<script type="module">
  /* inlined (optionally rebundled) JS content */
</script>

注意与限制

  • 资源定位:
    • 插件会优先基于每个 HTML 文件的相对路径(以及可用时的 Vite base)来解析资源,只在必要时才回退到基本名匹配。
    • 若只剩下没有路径上下文的重复基本名,仍可能出现歧义。
  • 动态导入:
    • inlineDynamicImports=false(默认)时,动态 chunk 仍为外部文件;插件不会删除它们。
    • 若入口同时包含静态与动态 import,且 inlineDynamicImports=false,插件会保留原始外链入口 script,并发出 warning,避免产出损坏的内联模块。
  • 注释保留:
    • removeComments: false 只会关闭本插件自己的 CSS 注释剥离,不会自动关闭 Vite 或 Rolldown 的最小化。如果构建链本身仍在压缩 CSS,注释可能在标签替换前就已经被移除了。
  • 非 CSS/JS 资源:
    • 图片、字体、JSON 等不会被内联。
  • 外部 URL:
    • 不属于当前 bundle 的绝对 URL 或协议相对地址的 CSS/JS 资源会保持原样,不会被误内联。
  • HTML 解析:
    • 基于正则替换;复杂模版场景可能需要自行调整。
  • 安全/CSP:
    • 插件会保留它实际替换到的 emitted HTML 标签上的属性。若 Vite 在 HTML 转换阶段已经移除了源 HTML 中的 script 属性,插件无法在后续阶段再恢复它。
    • 内联内容转义避免提前结束标签。

类似插件 / 灵感

感谢以上项目的启发。

许可

MIT

Keywords