1.6.0 • Published 1 year ago
@caolinss/replace v1.6.0
@caolinss/replace
一个替换关键词的node工具
多包架构的一个子包,这里做成子包时想如果后面有时间做成vscode插件则子包功能独立,一次实现多路共用。
目的
用node+ts+eslint写一个工具cl-cli 去全局替换其他前端项目中存在的关键词,用tsdx搭建项目。
当想在项目A中替换关键词时,只需要先全局安装npm i -g @caolinss/cli
,在A项目根目录运行cl-cli rep
则开始遍历rootDir
(默认".")下的文件,并替换满足条件的关键词。
默认配置文件config.ts如下,可以通过配置根目录的.replace.config.json
覆盖:
如何使用
1. 全局安装插件
pnpm i -g @caolinss/cli
2. 打开需要替换的项目
cd [project]
3. 创建配置文件.replace.config.json
{
"white": {
"你好": "hello",
"测试": "test",
"联系人": "i18n('contact')",
"企业": "i18n('business')",
"销售机会": "i18n('opportunity')"
},
"black": ["不替换的联系人", "不替换的企业", "不替换的销售机会"]
}
内部的默认配置(config.base.ts)如下:
type IConfig = {
rootDir: string, // 以此目录为跟目录替换
ignores: string[],
fileTypes: string[],
importConfig: IImportConfig,
white: Record<string, any>,
black: string[],
createBakFile?: boolean, // 替换是是否生成备份文件
}
// 默认配置:
const baseConfig: IConfig = {
rootDir: '.',
ignores: [
'node_modules/**',
'dist/**',
'build/**',
'.history/**',
'.husky/**',
'.vscode/**',
".replaceReport/**",
"**/__test__/**",
"**/*.test.ts",
"**/*.spec.ts",
'src/api/**',
'src/webapi/**'
],
fileTypes: ['.js', '.ts', '.tsx', '.jsx', '.vue'],
importConfig: {
moduleName: '@caolinss/i18n',
importName: 'i18n',
url: `import { i18n } from '@caolinss/i18n';`
},
white: {
"你好": 'hello',
"联系人": "i18n('contact')",
},
black: ['不替换的联系人'],
createBakFile: false,
}
4. 执行
在需要替换的项目根目录执行脚手架指令:
cl-cli rep
实现
1. 先实现一个脚手架工具,子包:@caolinss/cli
脚手架工具,参考:packages/cli/src/index.ts
目的提供全局指令:cl-cli
则替换指令:
cl-cli replace
# 或者
cl-cli rep
2. 实现替换工具的核心,子包:@caolinss/replace
替换工具,参考:packages/replace/src/core/index
import getFiles from './libs/getFiles';
async function main() {
const { rootDir, ignores, fileTypes, ...args } = replaceConfig;
const files = await getFiles(rootDir, ignores, fileTypes);
for (const filePath of files) {
let content = await fsPromise.readFile(filePath, 'utf-8');
const ext = path.extname(filePath);
if (!ext) {
console.log(`Oops! Just Folder:${filePath}`)
}
if (!content) {
console.log('Not content')
}
if(ext === '.vue') {
// 替换方案试试转 AST 树
const {
templateList,
scriptList
} = replaceVueFile(content, {filePath, args});
} else if(['.js', '.ts'].includes(ext)){
// js和ts的替换方案用正则
const {repContent, repList} = replaceJSFile(content, filePath, args)
chalk.cyan(`${filePath} 成功写入`)
reportData.replaceList = [...reportData.replaceList, ...repList]
fsPromise.writeFile(filePath, repContent, 'utf-8')
} else {
chalk.red(`文件 ${filePath} 不支持转化`)
}
}
const root = process.cwd(); // 获取项目根目录路径
const outputPath = path.join(root, 'report'); // 拼接报告输出目录路径
// 如果报告输出目录不存在,则创建它
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath);
}
// await generateReport(reportData, '/replace');
}
3. @caolinss/cli 安装依赖 @caolinss/replace
@caolinss/cli 的 package.json:
"dependencies": {
"@caolinss/replace": "workspace:*",
}
替换的Demo
demo-ts.ts
/**
* 不替换多行注释的联系人
*/
const demo1 = '普普通通的文本';
const demo2 = '不替换的联系人';
const demo3 = '不替换的企业';
const rep1 = '你好啊';
const rep2 = '你好啊,这是测联系人';
const rep3 = '这是联系人&测试企业';
const bool1 = true;
const rep4 = `字符串${bool1}有联系人替换项和企业替换项`;
const rep5 = `字符串${bool1}无替换项`;
const rep6 = bool1 ? '啊你好': '企业';
// 不替换 单行注释的联系人
const obj = {
n1: '这是联系人',
n2: '不替换的联系人'
}
// 不替换 对象的key
const obj2 = {
"你好": "这种不替换key了,你好",
"联系人": "这种不替换key2了,你好",
"企业": "这种不替换key3了, 你好",
}
// 不替换 console.log
console.log('打印里面的联系人啊')
demo-vue2.vue
<template>
<div class="vue2-test">
<div>你好</div>
<div>这是联系人:test1</div>
<div>这是{{ true ? '联系人': "vue2" }}</div>
</div>
</template>
<script lang="ts">
import { useRoute } from 'vue-router';
import { ref, reactive, defineComponent } from "vue";
export default defineComponent({
setup() {
// vue2:你好,字符串
const s1 = 'vue3联系人';
const s2 = ref('不替换的联系人');
const s3 = '不替换的联系人'; // 黑名单
const s4 = `你好,我是${s1}`;
/**
* vue2:你好,对象
*/
const obj1 = {
test1: 'obj的联系人',
test2: '不替换的联系人',
"1你好": "这种不替换key了,你好",
"2联系人": "这种不替换key2了,联系人",
"3企业": "这种不替换key3了, 企业",
}
const obj2 = reactive({
test1: 'obj的联系人',
test2: '不替换的联系人',
"11你好": "这种不替换了,你好",
"22联系人": "这种不替换了,联系人",
"33企业": "这种不替换了, 企业",
})
// 其他
const demo1 = true ? '你好' : "联系人";
const demo2 = `测试${s1+'vue2'}模版字符串的联系人`;
console.log('log你好');
console.log(s1,s2,s3,s4);
console.log(obj1, obj2);
console.log(demo1, demo2);
},
});
</script>
demo-vue3.vue
<template>
<div class="vue3-test">
<h1>你好,</h1>
<div>test1: 这是联系人</div>
<div>你好,{{ true ? '联系人': "vue3" }}</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
// 你好,字符串
const s1 = 'vue3联系人';
const s2 = ref('不替换的联系人');
const s3 = '不替换的联系人'; // 黑名单
const s4 = `你好,我是${s1}`;
/**
* 你好,对象
*/
const obj1 = {
test1: 'vue3/obj的联系人',
test2: '不替换的联系人',
"1你好": "这种不替换key了,你好",
"2联系人": "这种不替换key2了,联系人",
"3企业": "这种不替换key3了, 企业",
}
const obj2 = reactive({
test1: 'vue3/obj的联系人',
test2: '不替换的联系人',
"11你好": "这种不替换了,你好",
"22联系人": "这种不替换了,联系人",
"33企业": "这种不替换了, 企业",
})
// 三目
const demo1 = true ? '你好' : "联系人";
const demo2 = `测试${s1+'vue2'}模版字符串的联系人`;
console.log('log你好');
console.log(s1,s2,s3,s4);
console.log(obj1, obj2);
console.log(demo1, demo2);
</script>