npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@caolinss/replace

v1.6.0

Published

> 一个替换关键词的node工具

Readme

@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>