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",
      }
    );
  }
}
相关推荐
popoxf2 分钟前
在新版本的微信开发者工具中使用npm包
前端·npm·node.js
爱编程的喵28 分钟前
React Router Dom 初步:从传统路由到现代前端导航
前端·react.js
每天吃饭的羊1 小时前
react中为啥使用剪头函数
前端·javascript·react.js
Nicholas681 小时前
Flutter帧定义与60-120FPS机制
前端
多啦C梦a1 小时前
【适合小白篇】什么是 SPA?前端路由到底在路由个啥?我来给你聊透!
前端·javascript·架构
薛定谔的算法1 小时前
《长安的荔枝·事件流版》——一颗荔枝引发的“冒泡惨案”
前端·javascript·编程语言
中微子1 小时前
CSS 的 position 你真的理解了吗?
前端·css
谜构1 小时前
【0编码】我使用Trae AI开发了一个【随手记账单格式化工具】
前端
G_whang2 小时前
jenkins部署前端vue项目使用Docker+Jenkinsfile方式
前端·vue.js·jenkins
ZhangApple2 小时前
微信自动化工具:让自己的微信变成智能机器人!
前端·后端