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",
      }
    );
  }
}
相关推荐
程序员老赵2 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
前端·docker·开源
Swot2 小时前
Asciidoctor 公式数字下标错位问题:原因分析与解决方案
前端
Swot2 小时前
leo-editor 中将 Markdown 内容转成 asciidoc 格式
前端
狒狒AI2 小时前
Remotion / GSAP+Three / HyperFrames / Skia 四栈实测:代码渲染知识视频的选栈方法(附开源技能)
前端·人工智能·aigc
修炼的dance2 小时前
浏览器里导出H.265,8.5秒和55.5秒差在硬件
前端
补码缩写2 小时前
WebCodecs选编码器,探测之后还要验解码和播放
前端
大文说跨境2 小时前
亚马逊多账号管理浏览器怎么选?云登/紫鸟/AdsPower 对比
java·前端·自动化
hunter_0762 小时前
iOS 首页复杂嵌套滚动技术方案:头部固定 + 网格滚动 + Tab 吸顶 + 每 Tab 独立下拉刷新
前端
CoderYanger2 小时前
Java EE 进阶:4.Spring Web MVC 入门(第一弹)
java·开发语言·前端·spring·面试·java-ee·mvc
用户2788607285672 小时前
fallback 会盖掉平台提示词:system、空数组和 systemAppend 的差别
前端·后端