批量转换常量名称 HappyNewYear => HAPPY_NEW_YEAR

134 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

阅读本文大概需要 5 分钟

0. 背景

老 C# 代码中定义了大量常量,要拿到 Web 项目中用,所以要符合 Web 项目的命名规范。大概是这样

// 各个插件的名字
export const MainForm = 'MainForm';
export const DispatchPlugin = 'DispatchPlugin';

最终希望修改成

// 各个插件的名字
export const MAIN_FORM = 'MainForm';
export const DISPATCH_PLUGIN = 'DispatchPlugin';

1. 心路历程

最开始的思路源文件 import 进来,遍历里面的 key,对其进行修改,然后以特定的 format 把修改后的 key 和 原 value 输出,伪代码类似这种:

const outArray = [];
for (const oldKey in oldKeys) {
  // 将原来的 key 修改成“大写+下划线”的形式
  const upperLineKey = toUpperLine(oldKey);
  // 将 value 转换成字符串,方便后续拼接
  const value = JSON.stringify(names[oldKey]);
  // format 成想要的样子
  outArray.push(`export const ${upperLineKey} = ${value};`);
}
// 数组拼接成一个 string
const outStr = outArray.join('\n');
console.log('finalOut', outStr);

但后来发现有些坑:

  • 如果 value 不是 string 是个 object
  • 对于 Symbol JSON.stringify 无效
  • 注释没有了

综上所述,import 进来的方式,是不可取的,不具有普适性,且注释搞没了,就很难受。

2. 解决方案

文件的形式,将原始数据逐行读取,解析,转换。下面进入正题

2.1 从每一行数据拿到变量名

  • 输入:export const happyNewYear = "Tiger"
  • 输出:happyNewYear
function getVarName(lineStr) {
  try {
    // 如果不是 export const 开头的,则说明没有变量名,可能是注释或者其他
    if (lineStr.indexOf("export const ") < 0) return null;
    const lineWithoutExportConst = lineStr.replace("export const ", "");
    if (!lineWithoutExportConst) return null;
    return lineWithoutExportConst.split(" = ")[0];
  } catch (error) {
    return null;
  }
}

2.2 将变量名变成 大写+下划线

  1. 使用正则,遇到大写字母,就给前面加上下划线:正则表达式
  2. 将所有字母变成大写:toUpperCase()
  • 输入:happyNewYear
  • 输出:HAPPY_NEW_YEAR
function toUpperLine(varName) {
  return varName.replace(/([A-Z])/g, "_$1").toUpperCase();
}

2.3 为了加下划线,将缩写的全大写改成驼峰

实战中发现一些坑,当遇到全大写的缩写时,会多出许多下划线,例如:

  • 输入:localGPSInfo
  • 期望输出:LOCAL_GPS_INFO
  • 实际输出:LOCAL_G_P_S_INFO

由于正则的误判,无法识别 GPS 是一个单词,所以我们需要将 GPS 转换为 Gps,这时候问题就转换成了一个算法题

通过总结规律,我们可以将问题转化为:当 string 中的每个字母,他的左边的字母 和 右边的字母,都是大写的时候,那么它就应该转换成小写

localGPSInfo中,P 和 S 满足,我们就将它们变成小写,就变成了 localGpsInfo,代码如下:

function upperUpper2UpperLower(varName) {
  const length = varName.length;
  if (length == 1) return varName;
  let result = varName[0];
  for (let i = 1; i < length - 1; i++) {
    // 本身是大写,左右都是大写
    if (
      isUpper(varName[i]) &&
      isUpper(varName[i - 1]) &&
      isUpper(varName[i + 1])
    ) {
      result += String.fromCharCode(varName.charCodeAt(i) + 32);
    } else {
      result += varName[i];
    }
  }
  // 特殊处理最后一个字母
  if (isUpper(varName[length - 1]) && isUpper(varName[length - 2]))
    result += String.fromCharCode(varName.charCodeAt(length - 1) + 32);
  else result += varName[length - 1];
  return result;
}
// 是否是大写字母,ASCII 64~90 是大写字母
function isUpper(letter) {
  return 64 <= letter.charCodeAt(0) && letter.charCodeAt(0) <= 90;
}

2.4 打完收工

function getNewLine(lineStr) {
  if (!lineStr) return lineStr;
  // 1. 拿到变量名
  const varName = getVarName(lineStr);
  if (!varName) return lineStr;
  // 2. 修改变量名
  const oldVarName = getVarName(lineStr);
  // 3. 将变量的全大写的缩写,改成只有首字母大写,便于后续转换。 举例:GPS => Gps
  const upperLowerLowerVarName = upperUpper2UpperLower(oldVarName);
  // 4. 转换成 大写_大写
  const upperLineVarName = toUpperLine(upperLowerLowerVarName).slice(1);
  // 5. 新的变量,替换掉老变量
  const resultLine = lineStr.replace(varName, upperLineVarName);
  return resultLine;
}