vite-plugin-dev-sourcemaps
v0.1.0
Published
Externalize Vite dev-server inline source maps into separate .map responses to avoid Chrome/Edge DevTools freezes (vitejs/vite#23549).
Maintainers
Readme
vite-plugin-dev-sourcemaps
把 Vite 开发服务器注入的内联 base64 source map 外置为独立 .map 响应,避免 Chrome/Edge DevTools 因解析超大单行而冻结。
规避上游问题:vitejs/vite#23549
上游修复:vitejs/vite#23596。升级到包含修复的 Vite 版本后即可移除本插件。
为什么需要它
Vite 8 在开发服务器响应阶段会把 source map 以内联 data:application/json;base64,... 的形式写入 JS/CSS:
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJ...一个稍大的模块就可能产生几十万字符的单行内容。Chrome/Edge DevTools 在解析这类超大单行时会长时间占用主线程,表现为元素面板卡死、页面元素无法拾取。
本插件在 res.end 前把内联 source map 改写为独立 URL:
//# sourceMappingURL=/__dev_sourcemaps__/3f2a9c1b7d4e8a60.map?v=1a2b3c4d5e原始 source map 文本保存在开发服务器内存中,由独立端点返回。映射能力保持不变,但响应体不再包含超大单行。
安装
pnpm add -D vite-plugin-dev-sourcemaps
# 或
npm i -D vite-plugin-dev-sourcemaps使用
// vite.config.ts
import { defineConfig } from 'vite'
import devSourceMaps from 'vite-plugin-dev-sourcemaps'
export default defineConfig({
plugins: [
devSourceMaps(),
],
})也可以使用命名导出(与默认导出等价):
import { createDevSourceMapPlugin } from 'vite-plugin-dev-sourcemaps'
export default defineConfig({
plugins: [createDevSourceMapPlugin()],
})插件仅在 vite serve 阶段生效,不会改变生产构建产物。
配置
export interface DevSourceMapPluginOptions {
/**
* 外置 source map 的 URL 前缀,必须以 `/` 开头和结尾。
* @default '/__dev_sourcemaps__/'
*/
route?: string
}例如沿用旧项目的路由:
devSourceMaps({ route: '/__engineer_sourcemap__/' })路由会自动补齐结尾的 /。根路径 / 以及包含查询字符串/hash 的取值会被拒绝。
从 ext-engineer 本地实现迁移
本包由 ext-engineer 的 scripts/vite-plugin-dev-sourcemaps.mts 抽取而来,createDevSourceMapPlugin() 的名称与调用方式保持不变:
// 旧:import { createDevSourceMapPlugin } from './scripts/vite-plugin-dev-sourcemaps.mts'
import { createDevSourceMapPlugin } from 'vite-plugin-dev-sourcemaps'
export default defineConfig({
plugins: [
// 如需保持旧路由 /__engineer_sourcemap__/,显式传入 route
createDevSourceMapPlugin({ route: '/__engineer_sourcemap__/' }),
],
})不传 route 时使用通用的 /__dev_sourcemaps__/。
工作方式
configureServer注册两个中间件:- 独立 map 响应端点(注册在前,避免被二次改写)。
- 响应改写中间件,在
res.end前处理 JS/CSS。
- 只处理
Content-Type为 JavaScript/ECMAScript 或text/css的响应。 - 解析 base64 或 URL 编码的
data:application/jsonsource map,写入内存表。 - 生成稳定的 map ID(去掉 HMR 的
?t=时间戳),因此同一模块热更新会覆盖同一个槽位。 - map 响应使用
Cache-Control: no-cache,内容版本通过?v=<hash>区分;未命中返回 404,避免落到 SPA fallback 返回 HTML。 - 改写响应体时会移除
Content-Length与ETag,防止浏览器截断响应或复用旧缓存。
注意事项
- 内存中的 source map 会随开发服务器重启而清空,不需要手动清理。
- 插件依赖 Vite 开发服务器把完整响应一次性交给
res.end(Vite 的标准行为)。如果其他插件把 JS/CSS 改成了流式分块输出,只有最终传给res.end的内容会被改写。 - 本插件不会关闭 source map,只是改变 map 的传输方式。
- 上游修复合入并升级 Vite 后,建议移除本插件。
API
import {
createDevSourceMapPlugin,
DEFAULT_DEV_SOURCE_MAP_ROUTE,
DEV_SOURCE_MAP_ROUTE,
externalizeInlineSourceMaps,
handleDevSourceMapRequest,
normalizeDevSourceMapRoute,
} from 'vite-plugin-dev-sourcemaps'| 导出 | 说明 |
| --- | --- |
| createDevSourceMapPlugin(options?) | 创建 Vite 插件(默认导出也是它)。 |
| externalizeInlineSourceMaps(code, type, requestUrl, maps, route?) | 把一段 JS/CSS 中的内联 map 改写为独立 URL,并把原始 map 写入 maps。 |
| handleDevSourceMapRequest(requestUrl, response, maps, route?) | 处理独立 map 请求;未命中返回 404。 |
| normalizeDevSourceMapRoute(route) | 规范化路由前缀,非法值抛出 TypeError。 |
| DEFAULT_DEV_SOURCE_MAP_ROUTE | 默认路由 /__dev_sourcemaps__/。 |
| DEV_SOURCE_MAP_ROUTE | DEFAULT_DEV_SOURCE_MAP_ROUTE 的兼容别名。 |
开发
pnpm install
pnpm typecheck
pnpm lint
pnpm test:run
pnpm build
pnpm check:package构建产物输出到 dist/(ESM + 类型声明 + source map)。
发布与版本管理
本地直接发布到 npm
适用于版本号已经确定、只想把当前 package.json 版本上传到 npm 的场景:
# 首次发布前登录 npmjs(本项目默认发布到 npmjs,而不是 npmmirror)
pnpm login --registry https://registry.npmjs.org
# 检查版本/登录状态,执行 lint、typecheck、test、build、publint,确认后发布
pnpm publish:npm
# 预演:执行全部检查并在本地打包,但不真正上传
pnpm publish:npm --dry-run
# 发布预发布版本到 next tag
pnpm publish:npm --tag next
# 跳过发布前检查(不推荐)
pnpm publish:npm --skip-checkspublish:npm 的行为:
- 默认发布到
https://registry.npmjs.org,不受本机 npm 镜像源影响;可用--registry或NPM_REGISTRY覆盖。 - 发布前检查 npm 上是否已存在同一版本,避免覆盖失败。
- 检查当前 npm 登录状态(
--dry-run跳过)。 - 默认执行
lint、typecheck、test:run、build、check:package。 --tag、--otp、--provenance、--force等参数会原样传给pnpm publish。- 不修改版本号、不生成 CHANGELOG、不创建 commit/tag;完整发布流程请使用
pnpm release。
如果已经登录并且不需要额外检查,也可以直接使用原生命令:
pnpm publish --access public --registry https://registry.npmjs.org原生 pnpm publish 会通过 prepublishOnly 自动构建,但不会执行 lint/测试/版本存在性检查。
完整发布流程
发布脚本与 ext-engineer 项目保持一致的交互方式:
# 本地发布:更新版本号 / CHANGELOG / 测试 / 构建 / 打包 / commit / tag,
# 然后可选择立即 pnpm publish 与推送远程。
pnpm release
# GitHub Actions 发布:只更新版本号 / CHANGELOG / commit / tag / push,
# 由 .github/workflows/release.yml 在 CI 端检查、构建、发布 npm 与 GitHub Release。
pnpm release:github
# 从 CHANGELOG 提取指定版本的发布说明到 dist/release-notes.md
pnpm release:notes v0.1.0- 版本号支持语义化版本与预发布标识,例如
0.2.0、0.2.0-beta.1。 - 可通过
RELEASE_VERSION=0.2.0 pnpm release预设版本号。 - 可通过
RELEASE_YES=1跳过确认,便于自动化。 - 可通过
RELEASE_SKIP_BUILD=1跳过测试/构建/打包(不推荐)。 - GitHub Actions 发布需要在仓库 Secrets 中配置
NPM_TOKEN。 - 本地发布后推送 tag 时,CI 会检测 npm 上是否已存在该版本;已存在则跳过
publish,避免重复发布失败。 - 提交信息遵循 Conventional Commits,
pnpm install会安装commit-msg钩子;subject 必须包含中文,以便自动生成 CHANGELOG。
License
MIT © 171h
