DOM元素与源码映射插件
# 图例
开发环境下,所有的 dom 元素都被绑定上了
data-sf
和data-sl
属性data-sf
代表的是这个 dom 元素在源码中对于的文件名data-sl
代表的是这个 dom 元素在源码中对于的文件下的文件行数

# 实现思路
读取本地文件名生成与编号生成的映射 Map,这个是从
其他插件
里面生成的 json 文件里面读取的- 生成映射的好处是
data-sf
不会跟着一大串的文件名,如果每一个 dom 元素都有一个属性是data-sf="src/pages/KlinePart/..."
,一眼望去,没眼看了,太影响开发体验了
- 生成映射的好处是
核心操作是:JSX 元素的打开标签(JSXOpeningElement),并为每个 JSX 元素添加两个属性:
data-sf
data-sl
<></>
和React.Fragment
,这种情况会跳过,这个并不是实际存在的 dom 元素,不需要添加额外的信息重复添加问题,如果已经有这两个属性了,那么就不需要再添加了,否则
公共组件
或多次引用的组件
会被重复添加分别注入
data-sf
data-sl
属性需要注意的是: 这个插件只在
开发环境
生效,生产环境是没有fileMap.json
文件的,也不会给 dom 元素添加data-sf
data-sl
属性
# 实现代码
点击 展开/收起 jsx 代码
import { types as t } from '@babel/core';
import { readFileSync } from 'fs';
import { join } from 'path';
// 读取文件映射
const fileMap =
process.env.NODE_ENV === 'development'
? JSON.parse(readFileSync(join(process.cwd(), 'fileMap.json'), 'utf8'))
: {};
export default function injectSourceInfo() {
return {
visitor: {
JSXOpeningElement(path, state) {
// 检查是否为 React.Fragment
if (
path.node.name.name === 'Fragment' ||
(path.node.name.object &&
path.node.name.object.name === 'React' &&
path.node.name.property.name === 'Fragment')
) {
return;
}
const fileName = state.file.opts.filename;
// 查找文件名对应的编号
const fileNumber = Object.keys(fileMap).find(
(key) => join(process.cwd(), fileMap[key]) === fileName,
);
const hasSourceFile = path.node.attributes.some(
(attr) => attr.name && attr.name.name === 'data-sf',
);
const hasSourceLine = path.node.attributes.some(
(attr) => attr.name && attr.name.name === 'data-sl',
);
if (!hasSourceFile && fileNumber !== undefined) {
path.node.attributes.push(
t.jsxAttribute(
t.jsxIdentifier('data-sf'),
t.stringLiteral(fileNumber),
),
);
}
const location = path.node.loc;
if (location && !hasSourceLine) {
path.node.attributes.push(
t.jsxAttribute(
t.jsxIdentifier('data-sl'),
t.stringLiteral(location.start.line.toString()),
),
);
}
},
},
};
}