摘要:比如一个模板用于创建的组件库,一个模板用于创建的组件库,还有一个模板用于创建的工具函数类库。
缘起
最近公司内部想搭建一个私有的 npm 仓库,用于将平时用到次数相当频繁的工具或者组件独立出来,方便多带带管理,随着项目的规模变大,数量变多,单纯的复制粘粘无疑在优雅以及实用性上都无法满足我们的需求,所以进一步模块化是必然的。
但是一个组件库的建立其实是一个非常麻烦的过程,基础 webpack 的配置不用多说,接着你还要配合增加一些 es-lint 之类的工具来规范化团队成员的代码。在开发过程中,你自然需要一个目录来承载使用示例,方便 dev 这个组件,随后呢,你还得建立一个打包规范,发布到私有 npm 仓库中。
如此一来,必然大大降低我们的积极性,所以不如创建一个用于建立模块包的脚手架工具,方便我们项目的初始化。
tips:最终成品在底部
私有 NPM这里简单提及一下 私有 npm 的搭建。
npm i verdaccio -g
pm2 start verdaccio
推荐配合 nrm 使用 快速切换仓库地址
verdaccio github
还整个意大利名,属实洋气。
工具在进入正题之前,我先介绍一些要点和工具,有了这写关键点,写起来其实就相当简单了。
npm bin大家有没有想过一些全局安装的工具,他是如何做到在命令行里面自由调用的呢?
事实上这个东西是 npm 提供的链接功能
// package.json { "name": "lucky-for-you", "bin": { "lucky": "bin/lucky" } }
当这样的一个模块被发布之后,一旦有人使用 -g 参数全局安装
sudo npm i luck-for-you -g/usr/local/bin/lucky -> /usr/local/lib/node_modules/luckytiger-package-cli/bin/lucky # npm 帮你进行链接
npm 事实上会帮你进行一次链接,链接到你操作系统的 Path 之中,从而但你敲出 Lucky 这个命令的时候,能从 path 中成功找到对应的程序
另外一点就是用于链接执行的文件 一般在开头都要加上如下内容,让 bash 能够正确识别该文件应该如何执行
#!/usr/bin/env node // 意味使用 node 运行该文件 // next scriptCommander.js
tj 大神的作品,可以方便的书写命令行工具。能够自动生成帮助命令
const program = require("commander"); program.version("0.0.1").usage("[options]"); program .command("create ") .description("创建一个全新的 npm 组件模块") .action((name, cmd) => { const options = cleanArgs(cmd); require("../lib/create")(name, options); }); // 用户未输入完整命令 输出帮助 if (!process.argv.slice(2).length) { program.outputHelp(); } program.parse(process.argv);
Commander.js github
inquirer事实上当我第一次使用 vue-cli3.0 的时候,里面的命令行表单真是非常惊艳,翻了 vue-cli3 的源码 找到了这款工具,用于命令行的表单。能够更加直观的配置选项。
inquirer .prompt([ { type: "list", name: "template", message: "template: 请选择项目起始模板", choices: [ { key: "1", name: "JavaScript Library - 适用于普通 JS 库", value: "js-lib", }, { key: "2", name: "Vue-components - 适用于 Vue 组件库", value: "vue-component", }, ], }, { type: "input", name: "author", message: "author: 请输入你的名字", validate: function(value) { return !!value; }, }, { type: "input", name: "desc", message: "desc: 请输入项目描述", validate: function(value) { return !!value; }, }, { type: "confirm", name: "confirm", message: "confirm: 完成配置了?", default: false, }, ]) .then(answers => { console.log(answers.template); console.log(answers.author); console.log(answers.desc); });
还有很多的表单类型,我这里几个最简单的 list + input + confirm 就足够了。
inquire github
开始构建现在开始分享我的构建流程。由于代码量比较大,挨个文件帖出来没有什么必要,所以我这里只做简单介绍,具体的可以查看我的 github项目。
我把我的 cli 工具大致分为两部分 template模板 + 创建器
z
创建器的主要功能是吸收用户的可选项,基于模板进行复制+渲染。Vue-cli3.0对于这部分操作会更加复杂,他把模板里面具体的功能都抽象成了一个 Plugin,可以按需组建模板,对于面向普遍大众当然是更好的。
但是我这个项目因为是公司内部用,所以不太需要太过泛化的设计,一个模板直接解决一个问题,简化模型就可以了。比如一个模板用于创建 Vue 的组件库,一个模板用于创建 React 的组件库,还有一个模板用于创建JavaScript 的工具函数类库。
如此一来我们的 template模板 创建器在一定程度上可以做到解耦,也就是说日后需要更多类型的模板,不需要修改创建器部分的代码。
目录结构├── README.md ├── bin │ └── lucky #主程序 ├── lib │ ├── copy.js #复制 │ └── create.js #主创建器 ├── package-lock.json ├── package.json ├── templates │ ├── config.js #模板配置 解耦 │ ├── js-lib #预设模板1 │ └── vue-component #预设模板2 ├── utils # 工具目录 │ └── dir.jspackage.json
{ "name": "luckytiger-package-cli", "version": "1.1.14", "description": "package-cli", "bin": { "lucky": "bin/lucky" }, "scripts": { "lucky": "node bin/lucky", "bootstarp": "cnpm i && cd ./templates/js-lib/ && cnpm i && cd ../vue-component/ && cnpm i ", "dev:js-lib": "cd templates/js-lib && npm run dev", "dev:vue-component": "cd templates/vue-component && npm run dev", "dev:create": "rm -rf test-app && node bin/lucky create test-app", "clear": "sudo rm -rf node_modules && sudo rm -rf templates/js-lib/node_modules && sudo rm -rf templates/vue-component/node_modules" }, "author": "zhangzhengyi", "license": "ISC", "dependencies": { "chalk": "^2.4.2", "commander": "^2.20.0", "ejs": "^2.6.2", "inquirer": "^6.4.1", "validate-npm-package-name": "^3.0.0" } }
配置了一些脚本 方便快速 DEV 模板的效果。
这样运行
npm run dev:js-lib
就能查看和开发 js-lib 这个模板
主程序bin/lucky
#!/usr/bin/env node const program = require("commander") program.version("0.0.1").usage("[options]") program .command("create ") .description("创建一个全新的 npm 组件模块") .action((name, cmd) => { const options = cleanArgs(cmd) require("../lib/create")(name, options) }) if (!process.argv.slice(2).length) { program.outputHelp() } program.parse(process.argv) // commander passes the Command object itself as options, // extract only actual options into a fresh object. function cleanArgs(cmd) { const args = {} cmd.options.forEach(o => { const key = camelize(o.long.replace(/^--/, "")) // if an option is not present and Command has a method with the same name // it should not be copied if (typeof cmd[key] !== "function" && typeof cmd[key] !== "undefined") { args[key] = cmd[key] } }) return args }
这个文件主要是做一下基本的命令设置 利用了 commander这个库
如果用户调用了创建命令,就会转发给 lib/create.js 处理
主创建器lib/cerate.js
const path = require("path") const inquirer = require("inquirer") const validateProjectName = require("validate-npm-package-name") const chalk = require("chalk") const copy = require("./copy") const fs = require("fs") const dir = require("../utils/dir") const templates = require("../templates/config") async function create(projectName, options) { const cwd = options.cwd || process.cwd() const inCurrent = projectName === "." const name = inCurrent ? path.relative("../", cwd) : projectName const targetDir = path.resolve(cwd, projectName || ".") const result = validateProjectName(name) if (!result.validForNewPackages) { console.error(chalk.red(`无效的项目名: "${name}"`)) result.errors && result.errors.forEach(err => { console.error(chalk.red.dim("Error: " + err)) }) result.warnings && result.warnings.forEach(warn => { console.error(chalk.red.dim("Warning: " + warn)) }) return } if (!dir.isDir(targetDir)) { fs.mkdirSync(targetDir) } else { console.error(chalk.red(`该目录下已经存在该文件夹 请删除或者修改项目名`)) return } const answers = await inquirer.prompt([ { type: "list", name: "template", message: "template: 请选择项目模板", choices: templates.map((v, i) => ({ key: i, name: v.name, value: v.dir })) }, { type: "input", name: "author", message: "author: 请输入你的名字", validate: function(value) { return !!value } }, { type: "input", name: "desc", message: "desc: 请输入项目描述", validate: function(value) { return !!value } }, { type: "confirm", name: "confirm", message: "confirm: 完成配置了?", default: false } ]) // 启动复制流程 const sourceDir = path.resolve(__dirname, "..", "templates", answers.template) console.log(chalk.blue(`
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/105566.html
摘要:什么是读音,类似于是一套用于构建用户界面的渐进式框架。的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。 什么是vue.js Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化...
摘要:的官方下载地址点我进入的官方下载地址下载电脑系统对应文件,然后进行安装,安装成功之后通过命令行工具进入安装目录。注系统命令行工具通过开始菜单输入打开,系统为终端。 showImg(https://segmentfault.com/img/bVPL6q?w=200&h=200); Vue — 渐进式 JavaScript 框架 介绍 Vue.js 是什么 vue.js 是一套构建用户界面...
摘要:的官方下载地址点我进入的官方下载地址下载电脑系统对应文件,然后进行安装,安装成功之后通过命令行工具进入安装目录。注系统命令行工具通过开始菜单输入打开,系统为终端。 showImg(https://segmentfault.com/img/bVPL6q?w=200&h=200); Vue — 渐进式 JavaScript 框架 介绍 Vue.js 是什么 vue.js 是一套构建用户界面...
摘要:的官方下载地址点我进入的官方下载地址下载电脑系统对应文件,然后进行安装,安装成功之后通过命令行工具进入安装目录。注系统命令行工具通过开始菜单输入打开,系统为终端。 showImg(https://segmentfault.com/img/bVPL6q?w=200&h=200); Vue — 渐进式 JavaScript 框架 介绍 Vue.js 是什么 vue.js 是一套构建用户界面...
摘要:写在前面使用框架开发时,很多人会选择官方提供的脚手架,最新的已经更新到完全无配置,只需下载就能方便的使用构建的项目工程,但基础的并不能满足正常的项目开发,在开发中我们需要根据自己的习惯和业务功能而添加些基础功能。 写在前面 使用vue框架开发时,很多人会选择vue官方提供的cli脚手架,最新的cli已经更新到3.0完全无配置,只需下载就能方便的使用vuecli构建的项目工程,但基础的c...
阅读 1608·2023-04-26 02:11
阅读 2906·2023-04-25 16:18
阅读 3638·2021-09-06 15:00
阅读 2566·2019-08-30 15:55
阅读 1807·2019-08-30 13:20
阅读 1966·2019-08-26 18:36
阅读 3038·2019-08-26 11:40
阅读 2483·2019-08-26 10:11