新生代小鲜肉之代码生成器

WX20210509-175440@2x.png

如果你能耐心看完这个对话,
也许你会想动动手配置一个代码生成器,
这样你的撸码生活可能从此就会有所变化,变得愈发轻松。

从一个脚手架说起

丹尼尔: 最近要搞个代码生成器,能够快速生成项目代码那种,蛋兄有什么推荐?

蛋先生: 过往一直使用 yeoman,快超 10k star 的开源项目。但是,今天要推荐给你的,并非 yeoman,而是一个新生代的小鲜肉 ncgen,它可能显得更加的平易近人。

丹尼尔: 我最最喜欢简单的了,这个咋用呢?

蛋先生: 老规矩,你说你的需求,我试着一一解答

首先,你需要一个项目模板

丹尼尔: 我有一个项目模板(比如:vue3-ncgen-demo),我希望新建的项目都来自于这个项目模板,这样我只需专心维护好这个项目模板即可。

蛋先生: OK,这就是项目脚手架的功能了。我们来看下 ncgen 是如何处理的。

第一步 安装 ncgen

$ npm i ncgen -g # yarn global add ncgen

第二步 生成配置文件 ncgen-config.js,该配置文件描述了代码生成器的逻辑

$ ncgen genConf

第三步 修改 ncgen-config.js 中的 main.tmplSource 为项目模板的地址。

export default {
  main: {
    tmplSource: "https://github.com/daniel-dx/vue3-ncgen-demo.git",
  },
};

运行一下试试:

ncgen ncgen-config.js
image

简单复制还不行,小修小改是常态

丹尼尔: 哎呦不错哦。不过目前生成的项目跟项目模板是一模一样的,但它总会有属于自己的跟项目模板不一些的信息,比如项目名称,作者姓名等。这些我可不希望每次生成完项目还要手工修改哦。

蛋先生: OK,要求非常合理。由于这些信息是创建项目的人才能提供,所以我们需要通过一些问题来收集这些信息,然后就可以根据这些信息对生成的项目作一些修改。我们修改下 ncgen-config.js 中的 main.promptmain.updateFiles

示例说明:
对生成项目中的 package.json 文件进行字符串替换,规则如下:
vue3-ncgen-demo 字符串替换成用户录入的项目名称
Daniel.xiao 字符串替换成用户录入的作者名称

export default {
  main: {
    prompt: [
      {
        type: "input",
        name: "author",
        message: "What is the author's name",
      },
    ],

    ...

    updateFiles: {
      "package.json": function (content, options) {
        const answers = this.$answers
        return api.replace(content, {
          "vue3-ncgen-demo": answers.projectNameObj.kebabCase,
          "Daniel.xiao": answers.author,
        });
      },
    },
  }
};
image

丹尼尔: 咦,我注意到这里并没使用模板引擎,而是直接使用字符串替换

蛋先生: 是的,这个设计有很大的意义。用了模板引擎来替换文件,可能会导致项目模板自身无法正常运行,因为模板引擎需要占位符,而占位符可能会导致代码解析错误

丹尼尔: 也是,这样一来项目模板就是一个普通的项目而已,也不用特意去做一些模板占位符的改造

多余的文件昨办?请删除

丹尼尔: 那我继续。我的项目模板里面放了一些模板目录和文件(比如模块模板目录,组件模板文件),但我不想在生成的项目里面看到这些模板的东西。

蛋先生: OK,没问题,就是需要删除指定的文件和目录。我们修改下 ncgen-config.js 中的 main.removeFiles

示例说明:
删除生成项目中的 ncgen-config.jssrc/components/base/Template.vue 文件

export default {
  main: {
    removeFiles: ["ncgen-config.js", "src/components/base/Template.vue"],
  },
};

一条龙服务:自动安装依赖

丹尼尔: 我刚刚有注意到,上面的例子在运行的时候会自动安装依赖,应该是用 npm 安装的吧,这个能支持 yarn 吗?如果我是非 NodeJS 项目,比如 Python,Go 等,也能做到吗?

蛋先生: 嗯,没错,生成的 ncgen-config.js 默认是使用 npm i 来安装依赖,看下边的示例。如果你想换成 yarn,只需把 command 改成 yarn install。而如果是 Python,Go 等其它语言,也只需将 command 改成对应的依赖安装命令即可

export default {
  main: {
    installDependencies: {
      skip: false,
      tips: "Dependencies are being installed, it may take a few minutes",
      command: "npm i",
    },
  },
};

最后,给点友好的提示

丹尼尔: 太棒了,项目脚手架就这么配几下就完成了,我想最后需要来个友好的欢迎和漂亮的 ending

蛋先生: 如你所愿,简单修改下 main.welcomemain.complete 即可

export default {
  main: {
    welcome: "Welcome to use (Vue 3 + TypeScript + Vite) project generator",
    
    ...
    
    complete: "Congratulations, the operation is successful",
  },
};

高频使用的并非脚手架

丹尼尔: 脚手架是搞定了,但它只在新建项目时才使用,高频操作还是部分代码的增加,比如加一个功能模块,加一个组件,加一个 API 之类的

蛋先生: 我理解你的意思。老规矩,你问我答

代码模板存在于项目模板中

丹尼尔: 我现在要在一个项目中新加一个组件,我并不希望复制一个已有组件,然后进行各种修改删除代码的操作。事实上项目模板中有组件的代码模板

蛋先生: OK。我们先在 ncgen-config.js 中增加一个叫 add-component 的子命令。

示例说明(假设 category 和 name 的值分别为 'busi' 和 'demo'):
description 用于描述子命令的功能。
api.listDirs 这个API,在让用户选择将代码插入到哪个位置时非常有用。
addFilesTo 的配置会将项目模板中的 src/components/base/Template.vue 插入到项目中的 src/components/busi/Demo.vue 文件。

export default {
  sub: {
    "add-component": {
      description: "Add vue component",
      
      prompt: [
        {
          type: "list",
          choices: function () {
            return api.listDirs("src/components/");
          },
          name: "category",
          message: "Please select the category",
        },
        {
          type: "input",
          name: "name",
          message: "What is the component name",
          validate(input) {
            if (!input) return "The component name is required";
            return true;
          },
        },
      ],
      
      tmplSource: "https://github.com/daniel-dx/vue3-ncgen-demo.git",
      
      addFilesTo: function () {
        const answers = this.$answers;
        return {
          "src/components/base/Template.vue": `src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue`,
        };
      },
    },
  },
};
image

代码模板不存在于项目模板中

丹尼尔: 漂亮。不过对于已经存在的项目,这些项目并非来自项目模板,而我也想加一些子命令来为项目生成部分代码,昨整?

蛋先生: 子命令支持两种添加文件的方式,一种就是上面提到的来自于项目模板的代码模板,还有一种就是你动态创建。两者可同时使用。以下示例演示如何动态创建代码文件

示例说明(假设 category 和 name 的值分别为 'busi' 和 'demo'):
addFiles 的配置会在项目中创建 src/components/busi/Demo.md 文件,这个文件的内容为 # Demo

export default {
  sub: {
    "add-component": {
      addFiles: function () {
        const answers = this.$answers;
        return {
          [`src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.md`]: function () {
            return `# ${answers.nameObj.upperFirstCamelCase}`;
          },
        };
      },
    },
  },
};

墙裂推荐的替换技巧

丹尼尔: 接着,又是进行一些文件内容的修改(比如增加了页面,会自动修改路由规则文件为页面注册路由),跟主命令一样的操作是吧。

蛋先生: 悟性很高嘛。这里推荐一个小技巧,就是在需要插入片段代码的地方加入一些标识注释,如 src/App.vue 代码所示:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
  <!-- Don't touch me - place component -->
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import HelloWorld from './components/busi/HelloWorld.vue'
// <!-- Don't touch me - import component -->
export default defineComponent({
  name: 'App',
  components: {
    HelloWorld,
    // <!-- Don't touch me - register component -->
  }
})
</script>

再配合 api.insertBefore 这个API在文件的指定匹配位置前插入指定的内容

export default {
  sub: {
    updateFiles: function () {
      const answers = this.$answers;
      return {
        "src/App.vue": function (content, options) {
          return api.insertBefore(content, {
            "// <!-- Don't touch me - import component -->": `import ${answers.nameObj.upperFirstCamelCase} from './components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue'`,
            "// <!-- Don't touch me - register component -->": `${answers.nameObj.upperFirstCamelCase},`,
            "<!-- Don't touch me - place component -->": `<${answers.nameObj.upperFirstCamelCase}/>`,
          });
        },
        [`src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue`]: function (
          content,
          options
        ) {
          return api.replace(content, {
            Template: `${answers.nameObj.upperFirstCamelCase}`,
          });
        },
      };
    },
  },
};

丹尼尔: 完美。谢了蛋兄,我感觉我已经打道任督二脉,跃跃欲试我的第一个代码生成器了

蛋先生:不谢,期待你的反馈

写在最后

以上 - 完整配置

例子中 ncgen-config.js 的完整配置请查看:https://github.com/daniel-dx/vue3-ncgen-demo/blob/master/ncgen-config.js

以下 - ncgen官网


关键字:ncgen, scaffolding, generator, 代码生成器, 脚手架

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容