DOM元素与源码映射插件

# 图例

  • 开发环境下,所有的 dom 元素都被绑定上了data-sfdata-sl属性

    • data-sf代表的是这个 dom 元素在源码中对于的文件名

    • data-sl代表的是这个 dom 元素在源码中对于的文件下的文件行数

An image

# 实现思路

  • 读取本地文件名生成与编号生成的映射 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()),
            ),
          );
        }
      },
    },
  };
}