jscodeshift 工程运用

  1. 那么具体这个jscodeshift 是怎么去修改源代码的

generate 函数中,将节点类型直接转换为代码的过程是通过一个 switch 语句实现的。switch 语句根据节点类型的不同,生成相应的代码字符串。

例如,对于一个标识符节点,generate 函数会生成一个表示标识符名称的字符串,如 foo。这个过程可以通过以下代码实现:

js 复制代码
case 'Identifier':
  return node.name;

对于一个函数节点,generate 函数会生成一个包含函数名称、参数列表和函数体的字符串,如 function foo(a, b) { return a + b; }。这个过程可以通过以下代码实现:

js 复制代码
case 'FunctionDeclaration':
  return 'function ' + node.id.name + '(' + generate(node.params) + ') {' +
         generate(node.body) + '}';

在这个例子中,generate 函数会递归调用自身来生成函数参数列表和函数体的代码字符串 总之,generate 函数通过一个 switch 语句将节点类型直接转换为代码的过程。根据节点类型的不同,generate 函数会生成相应的代码字符串,并在需要时递归调用自身来生成子节点的代码字符串。 其实还是去判断当前元素节点是什么样的类型然后去返回对应的字符串。

i18n

js 复制代码
function transform(fileInfo, language) {
  const content = fs.readFileSync(fileInfo, {
    encoding: "utf-8",
  });
  const parse = fileInfo.substr(fileInfo.lastIndexOf(".") + 1);
  //不同类型解析器
  const j = jscodeshift.withParser(parse);
  const root = j(content);
  const StringLiteralNodes = root.find(j.Literal);
  const JsxLiteralNodes = root.find(j.JSXAttribute);
  const TemplateLiteralNodes = root.find(j.TemplateElement);
  const JsxTextNodes = root.find(j.JSXText);
  const callExpressionNodes = root.find(j.CallExpression);

  console.log("fileInfo", fileInfo);
  // 函数类型检测
  callExpressionNodes
    ?.filter((path) => {
      if (path?.value?.callee?.name === "intl") {
        intlIndex++;
        if (!supplement) {
          const noParamsCommonBlock =
            path?.node?.arguments?.[0]?.comments?.[0]?.type === "CommentBlock";
          const paramsCommonBlock =
            path?.node?.arguments?.[0]?.trailingComments?.[0]?.type ===
            "CommentBlock";
          const i18nId = path?.node?.arguments?.[0]?.value;
          const i18nValue = geti18nValue(language, i18nId);
          if (!(paramsCommonBlock || noParamsCommonBlock) && i18nValue) {
            return true;
          } else {
            return false;
          }
        } else {
          return false;
        }
      } else {
        return false;
      }
    })
    .replaceWith((item) => {
      const i18nId = item?.node?.arguments?.[0]?.value;
      const i18nValue = geti18nValue(language, i18nId);
      const locStart = item?.node;
      const { loc } = locStart;
      if (i18nValue) {
        currentReplace(
          `${fileInfo}:${loc?.start?.line}:${loc?.start?.column + 1} `,
          `(${loc?.start?.line}, ${loc?.start?.line})`,
          trimUtils(i18nValue)
        );
        return `intl('${i18nId}'${"/* "}${i18nValue}${" */"})`;
      }
    });

  // 模板字符串
  TemplateLiteralNodes?.filter((path) => {
    const { callee, leadingComments } = path?.parentPath?.node;
    return (
      path?.node?.type === "TemplateElement" &&
      chineseRegx.test(trimUtils(path.node?.value?.raw)) &&
      !i18nDisabled(callee, leadingComments)
    );
  }).replaceWith((item) => {
    let totalString = "";
    // const params = item?.parentPath?.parentPath?.value?.expressions?.[0]?.name;
    const totalValue = item?.parentPath?.value;
    const { loc } = item?.value;
    //拼接模板里面的字符串
    totalValue?.forEach((item) => {
      if (typeof item === "object") {
        if (item?.value) {
          totalString += item?.value?.cooked;
        }
      } else {
        totalString += item;
      }
    });
    const value = geti18nIdValue(
      language,
      trimUtils(item?.value?.value?.cooked),
      j
    );
    chineseInfo(
      `${fileInfo}:${loc?.start?.line}:${loc?.start?.column + 1}`,
      `(${loc?.start?.line}, ${loc?.start?.column})`,
      trimUtils(totalString)
    );
    chineseIndex(trimUtils(totalString));
    if (value) {
      return "$" + `{intl(${value})}`;
    } else {
      return item.value.value.cooked;
    }
  });

  // jsx属性
  JsxLiteralNodes?.forEach((path) => {
    const leadingCommen = path?.value?.value?.expression;
    // 规避掉无需国际化项
    const i18nDisabled = leadingCommen?.leadingComments?.some(
      (item) => item.type === "CommentBlock" && item.value === "i18n-disable"
    );
    const { loc } = path.node;
    chineseInfo(
      `${fileInfo}:${loc?.start?.line}:${loc?.start?.column + 1} `,
      `(${loc?.start?.line}, ${loc?.start?.line})`,
      trimUtils(path?.node?.value?.raw)
    );
    if (
      path?.node?.value?.type !== "JSXExpressionContainer" &&
      chineseRegx.test(trimUtils(path.node?.value?.value))
    ) {
      chineseIndex(trimUtils(path.node?.value?.value));
      const jsxContainer = geti18nId(
        language,
        trimUtils(path.node?.value?.value),
        j
      );
      if (
        tesI18nLanguage(trimUtils(path.node?.value?.value), language) &&
        !i18nDisabled
      ) {
        // 统计当前替换内容
        currentReplace(
          `${fileInfo}:${loc?.start?.line}:${loc?.start?.column + 1} `,
          `(${loc?.start?.line}, ${loc?.start?.line})`,
          trimUtils(path.node?.value?.value)
        );
        path.node.value = j.jsxExpressionContainer(jsxContainer);
      }
    }
  });
  StringLiteralNodes?.filter((path) => {
    const { value, leadingComments, loc } = path.node;
    const { callee } = path?.parentPath?.node;
    chineseIndex(trimUtils(value));
    // 函数参数,也不应该被替换
    const CallExpressionType =
      path?.parentPath?.node?.type === "CallExpression";
    // 三元表达式
    const conditionType =
      path?.parentPath?.node?.type === "ConditionalExpression";
    // if 表达式
    const ifCondition =
      path?.parentPath?.parentPath?.value?.type === "IfStatement";
    // switch 表达式
    const switchCondition = path?.parentPath?.value?.type === "SwitchCase";
    // 排除 jsxText 以及jsx 属性
    const jsxTextAttribute =
      path?.parentPath?.value?.type === "JSXAttribute" &&
      path?.node?.type === "JSXText";

    if (conditionType || ifCondition || switchCondition || CallExpressionType) {
      uncertainChineseInfo(
        `${fileInfo}:${loc?.start.line}:${loc?.start?.column + 1}`,
        `(${loc?.start?.line}, ${loc?.start?.column})`,
        trimUtils(path?.node?.value)
      );
    } else if (!i18nDisabled(callee, leadingComments)) {
      chineseInfo(
        `${fileInfo}:${loc?.start.line}:${loc?.start?.column + 1}`,
        `(${loc?.start?.line}, ${loc?.start?.column})`,
        trimUtils(path?.node?.value)
      );
    }

    const filternoI18n =
      chineseRegx.test(trimUtils(value)) &&
      tesI18nLanguage(trimUtils(value), language) &&
      !i18nDisabled(callee, leadingComments);
    // 规避掉if,switch  语句中的中文,避免被误替换导致异常
    return (
      !jsxTextAttribute &&
      !switchCondition &&
      !ifCondition &&
      !switchCondition &&
      !conditionType &&
      filternoI18n &&
      !CallExpressionType
    );
  }).replaceWith((item) => {
    const { loc } = item.node;
    // 统计当前替换内容
    currentReplace(
      `${fileInfo}:${loc?.start?.line}:${loc?.start?.column + 1} `,
      `(${loc?.start?.line}, ${loc?.start?.line})`,
      trimUtils(item?.value?.value)
    );
    return geti18nId(language, item.value.value, j);
  });

  // jsx 文本节点
  JsxTextNodes?.filter((path) => {
    const { loc } = path.node;
    chineseInfo(
      `${fileInfo}:${loc?.start.line}:${loc?.start?.column + 1}`,
      `(${loc?.start?.line}, ${loc?.start?.column})`,
      trimUtils(path?.node?.value)
    );
    chineseIndex(trimUtils(path.node?.value));
    return (
      chineseRegx.test(trimUtils(path.node?.value)) &&
      tesI18nLanguage(trimUtils(path.node?.value), language)
    );
  }).replaceWith((item) => {
    const { loc } = item.node;
    const jsxContainer = geti18nId(language, trimUtils(item.node?.value), j);
    if (jsxContainer) {
      currentReplace(
        `${fileInfo}:${loc?.start?.line}:${loc?.start?.column + 1} `,
        `(${loc?.start?.line}, ${loc?.start?.line})`,
        trimUtils(item?.node?.value)
      );
      return j.jsxExpressionContainer(jsxContainer);
    }
  });
  if (scan || supplement) {
    fs.writeFileSync(
      fileInfo,
      root.toSource({
        quto: "single",
      }),
      {
        encoding: "utf-8",
      }
    );
  }
}
相关推荐
excel1 小时前
HLS TS 文件损坏的元凶:Git 提交与拉取
前端
Aphasia3111 小时前
https连接传输流程
前端·面试
徐小夕1 小时前
万字长文!千万级文档 RAG 知识库系统落地实践
前端·算法·github
threelab1 小时前
Three.js 物理模拟着色器 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
kyriewen2 小时前
CSS Container Queries:彻底告别 @media 写到手软,附 5 个真实布局案例
前端·css·面试
Python私教2 小时前
把开源 Agent 打包成"解压双击即用"的 Windows 便携包:一条命令的完整实现
node.js
小小小小宇3 小时前
OpenMemory MCP
前端
和平宇宙4 小时前
AI笔记005. hermes-DeepSeek V4 Pro, 128K上下文引发的探索
前端·人工智能·笔记
IT_陈寒4 小时前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端
没事别瞎琢磨4 小时前
十一、审计与 Run Session——每一步操作都被记录
人工智能·node.js