开发一个问答式的node脚本

news2024/11/25 0:27:50

前言

我们公司一般有早上知识分享的规定,那天有个同事分享了如何通过Node脚本实现国际化替换
起因是这样的,有一个已经成熟的项目了,突然被要求实现中英文切换。前端中英文切换基本上就是通过
vue-i18n 来实现(不熟悉的可以看一下 使用vue-i18n实现中英文切换) 。

如果在项目刚开始开发的时候就开始做,问题自然不大;但是如果是一个完成的项目做,那工作量有多大可想而知。中英文词库已经做好了,但是替换是一个很艰巨的问题,如果是我的话我可能就傻傻的用 vscode 提供的全局替换功能挨个替换了。

然后就是开头说的那样,同事写了个node脚本 来做这件事,关键这个同事还是java后台。突然认识到了差距,突然绝对自己好傻逼,知识白学了。

不过还好,我之前不会,但是我写完这篇博客后,那我就会了,思路也打开了。感谢这个同事的分享(毕竟很多同事都是随便分享点没什么用的知识)

实现

言归正传,我会基于同事的分享来学习,后面会分享出来一个简单demo

初始化项目

npm init

创建相应文件夹
在这里插入图片描述
script 目录下存放脚本和词典库,src目录就相当于我们项目的业务代码

词库

script目录下创建一个js文件来存放词库

module.exports = {
    'entertainment news': '娱乐新闻',
    'home news': '国内新闻',
    'message settings': '消息设置',
    'privacy settings': '隐私设置'
}

脚本

既然要搞就不能单纯搞个简单的,当然是弄个问答式的。毕竟咱们才是前端吗。

这里推荐一个库:https://github.com/SBoudrias/Inquirer.js,网上挺多教程的可以自己百度

安装

npm install --save inquirer@^8.0.0

简单使用

//引入问答库
const inquirer = require('inquirer');
// 问题
var questions = [
  {
    type: 'input',
    name: 'name',
    message: "你叫什么名字?"
  }
]


inquirer.prompt(questions).then(answers => {
  console.log(`你好 ${answers['name']}!`)
})

在这里插入图片描述

处理词库

首先处理这个字典,key改为value,value改为key,便于我们快速判断字典中是否包含当前中文词组。

let jsonData = require('./word-stock')
/**
 * 字典处理,将key变成value,value变成key便于检索
 */
//key全部为中文的map
let cnKeyMap = new Map();
//key包含特殊字符的map
let otherKeyMap = new Map();

function genMap() {
    for (const key in jsonData) {
        //是否全中文
        if (/^[\u4e00-\u9fa5]+$/.test(jsonData[key])) {
            cnKeyMap.set(jsonData[key], key)
        } else {
            otherKeyMap.set(jsonData[key], key)
        }
    }
    console.log("cnKeyMapSize:" + cnKeyMap.size)
    console.log("otherKeyMap:" + otherKeyMap.size)
}

文件替换逻辑

/**
 * 中英文替换的脚本
 */

//引入问答库
const inquirer = require("inquirer");
//引入fs模块
const fs = require("fs");

let jsonData = require("./word-stock");
/**
 * 字典处理,将key变成value,value变成key便于检索
 */
//key全部为中文的map
let cnKeyMap = new Map();
//key包含特殊字符的map
let otherKeyMap = new Map();

function genMap() {
    for (const key in jsonData) {
        //是否全中文
        if (/^[\u4e00-\u9fa5]+$/.test(jsonData[key])) {
            cnKeyMap.set(jsonData[key], key);
        } else {
            otherKeyMap.set(jsonData[key], key);
        }
    }
    console.log("cnKeyMapSize:" + cnKeyMap.size);
    console.log("otherKeyMap:" + otherKeyMap.size);
}

/**
 * 是否包含中文
 * @param {*} str
 * @returns
 */
function isChinese(str) {
    var reg = /[\u4e00-\u9fa5]/;
    return reg.test(str);
}

/**
 * 是否是注解 左侧有/*或//或<!--,
 * 在他们之后出现的中文就不用替换了
 * @param {*} str
 * @returns
 */
function isNotes(str) {
    let first = str.trimStart();
    return (
        first.startsWith("/*") ||
        str.indexOf("//") != -1 ||
        first.startsWith("<!--")
    );
}


inquirer.prompt([
    {
        type: "input",
        name: "folder",
        message: "请输入完整的文件路径?",
    }
]).then((answers) => {
    //文件路径
    let folder = answers["folder"];
    fs.access(folder, fs.constants.F_OK, (err) => {
        if (err) {
            console.log(`你好, ${folder}不是文件夹或文件!`);
        } else {
            inquirer.prompt([
                {
                    type: "rawlist",
                    name: "fileType",
                    message: "请选择要处理的文件类型",
                    choices: ["All", "vue", "js"],
                },
            ]).then((answers) => {
                //处理字典值
                genMap()
                //文件类型
                let fileType = answers["fileType"];
                replaceWord(folder, fileType);
            })

        }
    });
});

/**
 * 单词替换
 * dir:目录
 * type:需要处理的文件类型
 */

function replaceWord(dir, type) {
    // 记录匹配和没有匹配到的文字
    let matchKey = ''
    let noMatchKey = '';
    //读取当前目录
    fs.readdir(dir, (err, files) => {
        if (err) {
            console.error("读取失败:" + err);
            return;
        }
        //遍历当前目录下的所有文件
        files.forEach((filename) => {
            //拼接文件路径
            let filepath = dir + "/" + filename;
            //判断文件类型
            fs.stat(filepath, (err, stats) => {
                if (err) {
                    console.error("读取失败:" + err);
                    return;
                }
                if (stats.isDirectory()) {
                    //文件夹,继续遍历
                    replaceWord(filepath, type);
                } else {
                    // 文件,读取指定类型的文件
                    let pattList = {
                        All: new RegExp(/[vue,js]$/),
                        vue: new RegExp(/vue$/),
                        js: new RegExp(/js$/)
                    }
                    if (pattList[type].test(filename)) {
                        //如果是指定格式的文件,读取文件并替换词组
                        fs.readFile(filepath, 'utf-8', (err, data) => {
                            //console.log("文件:" + filepath);
                            if (err) {
                                console.error("读取失败:" + err);
                                return;
                            }
                            let lines = [];
                            // 按行读取
                            data.split('\n').forEach((line, index) => {
                                //如果是注释或者这行都没有中文就可以下一行了
                                if (isNotes(line) || !isChinese(line)) {
                                    lines.push(line);
                                    return;
                                }
                                // 首先全词匹配特殊key,如"已开票(元)"这类正则写不来,就循环的看这行有没有这种词就好了,有了就换掉
                                otherKeyMap.forEach((val, key) => {
                                    // replaceAll好像只有15.0.0及以上存在,这个自行百度
                                    line = line.replace(new RegExp(key, 'g'), (match, index) => {
                                        // 判断前边是否有注解,从头开始截取,如果是注解则截取到字符串里一定存在注解符号
                                        if (isNotes(line.slice(0, index))) {
                                            //注解不替换
                                            return match
                                        }
                                        //将匹配到关键字替换为英文
                                        matchKey += '文件' + filepath + ':' + key + "->" + otherKeyMap.get(match) + "\n";
                                        return otherKeyMap.get(match)
                                    })
                                })

                                //匹配中文key,使用正则查找行内的中文词组,有了就替换
                                line = line.replace(/[\u4e00-\u9fa5]+/g, (match, index) => {
                                    if (cnKeyMap.has(match)) {
                                        // 注解不提货
                                        if (isNotes(line.slice(0, index))) {
                                            return match
                                        }
                                        //非注解替换,并记录日志
                                        matchKey += '文件' + filepath + ':' + match + "->" + cnKeyMap.get(match) + "\n";
                                        return cnKeyMap.get(match)
                                    } else {
                                        noMatchKey += '文件' + filepath + ':' + match + "\n";
                                    }
                                    return match
                                })
                                lines.push(line);
                            })
                            //存储日志
                            // console.log(" 匹配字符:\n" + matchKey);
                            // console.error(" 未匹配字符:\n" + noMatchKey);
                            let matchlogpath = './log/匹配.txt'
                            let nomatchlogpath = './log/未匹配.txt'
                            fs.appendFile(matchlogpath, matchKey, 'utf-8', err => {
                                if (err) {
                                    console.log("追加失败!")
                                    return
                                }
                            })
                            fs.appendFile(nomatchlogpath, noMatchKey, 'utf-8', err => {
                                if (err) {
                                    console.log("追加失败!")
                                    return
                                }
                            })
                            //将多行数据重新拼接起来
                            let content = lines.join('\n')
                            //将替换后的文件重新写入文件
                            fs.writeFile(filepath, content, (err) => {
                                if (err) {
                                    console.error(`文件${filepath}写入失败:${err}`)
                                    return
                                }
                                console.log(`文件${filepath}替换成功`)
                            })
                        })

                    }
                }
            });
        });
    })
}

效果图
在这里插入图片描述
注: 关于inquirer的使用没太搞明白,由于文件读取是异步的,导致文件读取还没完成就自动进入第二个提问了,因此目前只能将提问给拆开。

打包

如果只是单纯的一个js文件,是可以直接运行的,但是当你引入了第三方包后,就没法直接运行,否则就会如下图
在这里插入图片描述
所以需要将所引入的依赖一块进行打包。查了一下最简单的是esbuild,一行代码就可以搞定

安装

npm install esbuild

添加命令

 "build": "./node_modules/.bin/esbuild ./script/index.js --bundle --minify --outfile=dist.js --platform=node"

大体意思就是将依赖和项目的启动文件一起打包到dist.js文件中,因此只需要主要

// 启动文件,你项目的启动文件,路径要填写正确
./script/index.js
// 打包后的文件
dist.js

打包

npm run build

打包后的文件
在这里插入图片描述
该文件是可以直接进行运行的
在这里插入图片描述

源码下载

关注公众号回复关键词:问答式的node脚本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/373753.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安装配置DHCP

本次实验采用CentOS71.检查在安装DHCP之前先使用rpm命令查看系统中已有的DHCP软件包rpm -qa | grep dhcp由此可知&#xff0c;系统中尚未安装DHCP软件包2.安装我们可以使用yum命令为系统安装DHCP软件包yum -y install dhcp安装完成后再次检查可以看到DHCP软件包3.配置dhcp配置文…

20230225在WIN10下安装PR2023失败的解决

20230225在WIN10下安装PR2023失败的解决 2023/2/25 23:42 对于Adobe Premiere Pro 2023&#xff0c;就算你安装在早起的Windows 10上&#xff0c;也会安装失败的&#xff01; 对于WIN7&#xff0c;就不要再想安装PR2023了&#xff0c;根本不支持呀&#xff01; Adobe Installer…

php 基于ICMP协议实现一个ping命令

php 基于ICMP协议实现一个ping命令 网络协议是什么ICMP 协议什么是ICMP?ICMP 的主要功能ICMP 在 IPv4 和 IPv6 的封装Wireshark抓包ICMP 请求包分析PHP构建 ICMP 数据包php中的 pack & unpack 函数字节和字符packunpackICMP计算校验和步骤总结网络协议是什么 网络协议&…

_hand-1

实现防抖函数&#xff08;debounce&#xff09; 防抖函数原理&#xff1a;把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数&#xff0c;如果在指定的时间内又触发了该事件&#xff0c;则回调函数的执行时间会基于此刻重新开始计算 防抖动和节流本质是不一…

Socket通信详解

Socket通信详解 文章目录Socket通信详解Socket流程介绍函数介绍编程实例Socket流程介绍 socket通信类似于电话通信&#xff0c;其服务器基本流程就是 Created with Raphal 2.3.0安装电话socket()分配电话号码bind()连接电话线listen()拿起话筒accept()函数介绍 socket() 其中…

行测-判断推理-图形推理-样式规律-加减异同

图1图2图3选D图1图2都有的线&#xff0c;则消除图1图2只有一幅图里有的线&#xff0c;则保留选C第一列和第二列都有的线&#xff0c;则消除第一列和第二列只有一幅图里有的线&#xff0c;则保留选A第一列顺时针旋转90&#xff0c;再与第二列去同存异选D第一列和第二列去同存异&…

二叉树、队列、栈、广义表(二)数据结构与算法(十八)

数据结构与算法&#xff08;一&#xff09;-软件设计&#xff08;十七&#xff09;https://blog.csdn.net/ke1ying/article/details/129220378 线性表-队列与栈 队列&#xff1a;先进先出。 栈&#xff1a;先进后出。 循环队列&#xff1a;队投和队尾连接起来。 队空的条件&…

LeetCode 21.剑指 Offer II 078. 合并两个有序链表 | C语言版

LeetCode 21. 合并两个有序链表 | C语言版LeetCode 21. 合并两个有序链表题目描述解题思路思路一&#xff1a;使用栈代码实现运行结果参考文章&#xff1a;思路二&#xff1a;减少遍历节点数代码实现运行结果参考文章&#xff1a;[]()LeetCode 剑指 Offer II 078. 合并排序链表…

《MySQL系列-InnoDB引擎25》表-InnoDB逻辑存储结构

InnoDB逻辑存储结构 从InnoDB存储引擎的逻辑存储结构看&#xff0c;所有数据都被逻辑地存放在一个空间中&#xff0c;称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block)&#xff0c;InnoDB存储引擎的逻辑存储结构…

JVM系统优化实践(4):以支付系统为例

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;前面说过&#xff0c;JVM会将堆内存划分为年轻代、老年代两个区域。年轻代会将创建和使用完之后马上就要回收的对象放在里面&#xff0c;而老年代则将创建之后需要…

python刷题

目录标题1、输出前三同学的名字-input().split()2、字典的使用3、DA12 牛客网不同语言使用人数4、DA16 用户常用语言有多少5、python变量1、输出前三同学的名字-input().split() s1 input().split() print(tuple(s1)[:3])2、字典的使用 注意点&#xff1a;1&#xff0c;对字典…

如何使用 FreeSql 无缝接替 EF Core ?

如何使用 FreeSql 无缝接替 EF Core&#xff0c;并实现数据表的 CRUD 操作项目说明DB & 数据表结构DB & 数据表创建数据表 User 实体模型创建使用 EF Core 实现 User 表新增用户信息添加 EF Core 相关的 nuget 包编写 EF Core 操作 User 表的 CRUD 代码FreeSql 使用 Db…

系统启动太慢,调优后我直呼Nice

问题背景最近在负责一个订单系统的业务研发&#xff0c;本来不是件困难的事。但是服务的启动时间很慢&#xff0c;慢的令人发指。单次启动的时间约在10多分钟左右&#xff0c;基本一次迭代、开发&#xff0c;大部分的时间都花在了启动项目上。忍无可忍的我&#xff0c;终于决定…

链路追踪——【Brave】第一遍小结

前言 微服务链路追踪系列博客&#xff0c;后续可能会涉及到Brave、Zipkin、Sleuth内容的梳理。 Brave 何为Brave&#xff1f; github地址&#xff1a;https://github.com/openzipkin/brave Brave是一个分布式追踪埋点库。 #mermaid-svg-riwF9nbu1AldDJ7P {font-family:"…

大数据Hadoop教程-学习笔记05【Apache Hive DML语句与函数使用】

视频教程&#xff1a;哔哩哔哩网站&#xff1a;黑马大数据Hadoop入门视频教程 总时长&#xff1a;14:22:04教程资源: https://pan.baidu.com/s/1WYgyI3KgbzKzFD639lA-_g 提取码: 6666【P001-P017】大数据Hadoop教程-学习笔记01【大数据导论与Linux基础】【17p】【P018-P037】大…

一文带你搞定线程池原理

1.使用线程池的意义何在&#xff1f;项目开发中&#xff0c;为了统一管理线程&#xff0c;并有效精准地进行排错&#xff0c;我们经常要求项目人员统一使用线程池去创建线程。因为我们是在受不了有些人动不动就去创建一个线程&#xff0c;使用的多了以后&#xff0c;一旦报错就…

Android从屏幕刷新到View的绘制(一)之 Window、WindowManager和WindowManagerService之间的关系

0. 相关分享 Android从屏幕刷新到View的绘制&#xff08;一&#xff09;之 Window、WindowManager和WindowManagerService之间的关系 Android从屏幕刷新到View的绘制&#xff08;二&#xff09;之Choreographer、Vsync与屏幕刷新 1. 相关类 WindowManagerService&#xff0c…

Linux安装Redis步骤

1 下载安装包并解压 官网&#xff1a;https://download.redis.io 下载安装包&#xff1a; wget https://download.redis.io/redis-stable.tar.gz 解压 tar -zxvf redis-stable.tar.gz* 2 安装 安装 cd redis-stable make install PREFIX/opt/install/redis6 设置环境变量 vi …

Python学习-----项目设计1.0(设计思维和ATM环境搭建)

目录 前言&#xff1a; 项目开发流程 MVC设计模式 什么是MVC设计模式&#xff1f; ATM项目要求 ATM项目的环境搭建 前言&#xff1a; 我个人学习Python大概也有一个月了&#xff0c;在这一个月中我发布了许多关于Python的文章&#xff0c;建立了一个Python学习起步的专栏…

企业级信息系统开发学习1.3——利用注解配置取代Spring配置文件

文章目录一、利用注解配置类取代Spring配置文件&#xff08;一&#xff09;打开项目&#xff08;二&#xff09;创建新包&#xff08;三&#xff09;拷贝类与接口&#xff08;四&#xff09;创建注解配置类&#xff08;五&#xff09;创建测试类&#xff08;六&#xff09;运行…