0-1开发一个vite插件,原来项目上手可以这么快!
先说用法,按住 ctrl
左击页面节点,可以跳转到编辑器中对应行,no bb 看效果演示
git地址 github.com/kongweigen/… npm地址 www.npmjs.com/package/vit…
说一下背景,平时开发过程中,不会都是从 0-1 的项目,一些中途接手的项目,页面组件等熟悉起来就会比较费劲。有没有一个插件,来实现一键跳转到指定代码呢?那肯定是有的,npm上也有类似的插件,原理类似,就想着实现一个一键跳转, vite-plugin-jump-code
应运而生。鼠标点击,一键直达🚀🚀🚀
实现思路
- 通过 vite 的 transform 在页面节点插入源码路径
- 通过 vite 的 transformIndexHtml 插入脚本
- 监听鼠标点击事件,调用中间件服务,dom 上的路径作为参数
- 调用命令来拉起编辑器,并定位到指定位置。
具体实现
Talk is cheap, show me the code 多说无益,展示代码
1. vite 的 transform
transform
钩子是一个自定义插件的生命周期钩子,用于在构建过程中对模块进行转换或修改。例如自定义的代码转译、动态注入代码等。我们可以利用这个钩子函数,来实现源码地址的插入。
...省略
/**
*
* @param code 字符串,表示当前模块的源代码
* @param path 字符串,表示当前模块的标识符,绝对路径
* @returns 返回修改后的代码
*/
transform(code, path) {
return transform(code, path)
}
...省略
transform 里面进行代码转换
- 对
vue
文件进行转换,通过后缀辨别是否vue
文件 - 分割行,提取
dom
的tagName
,拼接文件源码路径和行号 - 拼接好路径插入的当前
dom
import path from 'path'
import { normalizePath } from 'vite'
function generate(code: string, filePath: string) {
// 根据回车拆分代码文件
let codeArr = code.split('\n')
let cwd = normalizePath(process.cwd())
codeArr.forEach((codeRow, rowIndedx) => {
// 获取相对路径
filePath = filePath.replace(cwd, '')
let tagName = getTagName(codeRow)
if (tagName) {
codeArr[rowIndedx] = codeRow.replaceAll(
`<${tagName}`,
`<${tagName} data-loc="${filePath}:${rowIndedx + 1}"`
)
}
})
code = codeArr.join('\n')
return code
}
function getTagName(codeRow: string) {
codeRow = codeRow.trim()
// 判断是否 < 开头
if (!/^<[a-zA-z]/.test(codeRow)) return ''
// /\b/ 分词 '<div class="card">'.replace(/\b/g, "#")
// '<#div# #class#="#card#">'
return codeRow.replace(/\b/g, '#').split('#')[1]
}
function transform(code: string, filePath: string) {
let ext = path.extname(filePath)
switch (ext) {
case '.vue':
return generate(code, filePath)
default:
return code
}
}
export default transform
2. vite 的 transformIndexHtml
transformIndexHtml 转换 index.html
的专用钩子。通过这个方法,注入发起命令的脚本
...省略
transformIndexHtml: {
transform() {
return [
{
injectTo: 'head', // 指定插入位置 'head' | 'body' | 'head-prepend' | 'body-prepend'
tag: 'script', // string 插入的节点名称
children: inject // string | HtmlTagDescriptor[] 插入的内容
}
]
}
},
...省略
// inject 的具体内容
let injectFn = `
(function(){
document.addEventListener('click', (e) => {
// 在不同操作系统上对应不同的键,如 Command 键或 Windows 键
// shift 或者 ctrl 按键
if (e.metaKey || e.shiftKey || e.ctrlKey) {
e.preventDefault();
e.stopPropagation();
let loc = getFilePath(e.target)
// 调用中间件的服务,传入path
loc && fetch('/jumpcode?path=' + loc)
}
})
function getFilePath(el){
if(!el) return
let { loc } = el.dataset || {}
// 三方的组件没有插入path,寻找父节点
if(!loc){
return getFilePath(el.parentElement)
}else{
return loc
}
}
})()
`
export default injectFn
3. vite 的 configureServer 和中间件
configureServer 是用于配置开发服务器的钩子。函数接收一个参数,即 server
对象
具体实现可以查看 connect
configureServer(server) {
// 方法还采用与传入请求 URL 的开头相匹配的可选路径字符串。这允许基本的路由
server.middlewares.use('/jumpcode', middleware)
},
// middlewar e具体实现
function middleware(req, res) {
let cwd = normalizePath(process.cwd())
const queryParams = url.parse(req.url!, true)
let path = queryParams.query.path as string
let pathList = path.split(':')
// 行号
let rowIndex = pathList[pathList.length - 1]
// path: E:/xxx/vite-vue3/src/components/HelloWorld.vue
path = cwd + pathList[0]
try {
openEditor(path, Number(rowIndex))
// 定义状态码返回
res.statusCode = 200
res.end('ok')
} catch (error) {
console.log('打开失败', error)
res.statusCode = 500
res.end('ok')
}
}
4. 编辑器的另一种打开方式
不知道大家是否知道,cmd 是可以直接打开文件,并且可以直接调用 vscode,打开指定文件的某一行。
cmd 打开文件,直接输入文件的绝对路径即可;还可以通过 code
命令调用 vscode
来打开指定文件的指定行。
对应的文件就被打开,并且定位到对应行
在VSCode中,"-g" 是一个命令行选项,用于在打开文件时定位到指定的行号。它的完整形式是 "--goto"。当然只有在使用 "code" 命令调用 VSCode 时有效。但是我们可能会有不同的编辑器、系统,手动兼容会很烦,所以我们可以直接使用
create-react-app
项目组的launchEditor.js
launchEditor.js
会检查系统中已安装的编辑器,并尝试使用默认的编辑器打开文件。它会尝试使用以下编辑器(按优先级顺序):Visual Studio Code、Atom、Sublime Text、WebStorm。并定位到错误的文件和行号。地址
打开跳转的前置条件,vscode 需要先配置
效果
最终我们会看到实际的dom上,会插入doc-loc=/xxx/xxx/*vue:11
,这就是我们用来定位文件的核心。
总结
通过这次插件的实现,了解了不少vite插件的执行流程和中间件的使用,当然还没完成,现阶段仅支持vite,后续计划完善一下,支持webpack和cli。 分享的过程也是学习巩固的过程,一起进步,共勉。
转载自:https://juejin.cn/post/7281118263966072844