简体中文 | English
全局的命令行工具 CR
迁移警告:@opq741/global-cli 已经迁移至 @xiao-edu/cr,请直接使用新的 cr 命令行工具:
npm i -g @xiao-edu/cr
@xiao-edu/cr的文档:@xiao-edu/cr
1. cr
用于规范化生成任意文件的命令行工具
cr 是一个兼容多平台的文件操作的命令行工具,它可以基于 cr.template.js 来生成任意文件。
可以保证多人开发 vue 或 react 应用时,大家产生的 views、components 下的页面或组件,符合一致的组件规范。
同时解放了双手,避免一个个的去创建文件并编辑一些重复性的代码。
cr 可以保证任意文件的规范,包括 markdown, scss, txt, tsx, vue 等文本类文件,它也同样适用于创建 rar, zip, jpeg, png 等二进制文件。
它也可以用来下载并更新文件,上传文件,批量生成文件夹、甚至是创建整个项目或者小型爬虫。
特性
-
基于 cr.template.js,可以任意的自定义文件模板,来生成任意数量的符合代码规范的文件
-
跨平台兼容 Linux 的 shell 的文件操作的命令:如 ls、touch、mkdir、rm、cp、mv、which 等 前面加上 cr 即可使用,如 cr ls -a ./bin
文件操作
平常也可以用 cr 来作文件操作,cr 是跨平台兼容 shell 的,你可以在 windows 下运行,如:
cr ls -a ./bin
# 类似于shell中的touch,但它可以递归创建文件夹
cr prettier.config.js .env eslint.config.json README.md src/routes/index.js
# 与touch命令保持一致
cr touch index.js
# 与mkdir命令保持一致
cr mkdir -p src/assets/imgs
# 与rm命令保持一致
cr rm -rf src/temp
# 与cp命令保持一致
cr cp src/temp src/test
# 与which命令保持一致
cr which npm
# 可以执行当前环境支持的命令
cr exec git status
环境要求
- nodejs(7.6+)
- npm
安装
npm install -g @opq741/global-cli
如果 npm 官方源下载过慢,你可以尝试淘宝源安装:
npm install -g @opq741/global-cli --registry https://registry.npm.taobao.org
如果你确实不想将 cr 命令暴露到全局,只想在项目的路径下使用 cr 命令,你可以只在项目中安装:
npm install -D @opq741/global-cli
注意:这样就只能在项目文件夹下使用 cr 命令
使用
除了像上面将命令安装到全局使用外,个人更加推荐的方式是通过 npx 来运行 cr 。npx 会自动查找本机是否有安装相应模块,如果没有的话,自动去远端查找并执行。通过 npx 就不用安装到本地,每次运行都可以使用远端最新的版本。
npx cr path/to/folderName
升级
每次执行 cr 命令时,都会自动检查更新,你只要根据提示运行即可:
npm install -g @opq741/global-cli
教程
在命令行下:
cr
即可输出帮助信息
在当前目录下,自动创建 cr.template.js
cr init ./
打开模板文件进行编辑:
vim cr.template.js
然后来生成文件吧!
cr ./report/student-count
代码生成
如你在 src 目录下运行:
cr init ./views
即会在 src/views 中生成一个默认的 cr.template.js, 你可以对其进行编辑
cr.template.js 示例:
// 之后的option会与该默认选项合并
const defaultOption = {
// 是否忽略生成该文件
ignore: false,
// 如果文件存在,是否强制覆盖
isForceOverwrite: false
}
// 导出生成文件的描述数组
const fileList = [
{
// 文件的路径
path: `prettier.config.js`,
// 写入文件中的内容,可以是模板字符串,也可以是函数,返回值会被写入该文件
content: `
// build at ${new Date().toDateString()}
`
},
{
// 如果没有后缀名,则认为是文件夹
path: `src`,
// 可以无限递归生成子目录和子文件
children: [
{
path: 'index.js',
// 文件内容可以由函数返回,返回的任何内容都会被toString()
// 可以在函数中作任何同步的操作,包括获取数据,筛选数据,甚至上传文件
content() {
// 你可以发起网络请求,
// 这里的axios是当前文件所在的项目中所能require到的npm包
const axios = require('axios')
const data = axios.get('http://example.com/example.json')
const obj = {
name: 'Alice',
data
}
return obj
}
}
]
}
]
module.exports = {
fileList,
defaultOption
}
然后当你运行
cr ./views/report/student-count
时,cr 会从你指定的./views/report/student-count 目录递归向外寻找 cr.template.js,根据其中导出的 fileList 来自动生成对应的文件。
cr.template.js 创建完后,是可以任意共享的,你可以选取别人写好的模板,也可以将模板分享给团队,这里也分享一个项目中可用的 vue 组件模板:
// cr.template.js
// 转成大驼峰
function toUpper(str) {
const camelizeRE = /-(\w)/g
// 把每个横杆分割的转成大写
var temp = str.replace(camelizeRE, ($0, $1) => {
return $1 ? $1.toUpperCase() : ''
})
// 再把首字母转成大写
return temp[0].toUpperCase().concat(temp.slice(1))
}
// 转成横划线
function toCenterLine(str) {
const hyphenateRE = /\B([A-Z])/g
return str.replace(hyphenateRE, '-$1').toLowerCase()
}
// 可以利用axios请求网络上的数据
const axios = require('axios')
// 之后的option会与该默认选项合并
const defaultOption = {
// 是否忽略生成该文件
ignore: false,
// 如果文件存在,是否强制覆盖
isForceOverwrite: false
}
const fileList = [
path: `index.vue`,
content: componentName => {
const centerName = toCenterLine(componentName)
const upperName = toUpper(componentName)
return `
// Generated by @xiao-edu/cr\n\n
<template>
<!-- ${centerName}的容器 -->
<div class="${centerName}-wrap">
{{ $route.meta.title }}
</div>
</template>
<script>
export default {
name: '${upperName}',
data() {
return {
}
},
methods: {
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
</style>
`
}
]
即可在指定文件夹下生成 index.vue,内容为你返回的 content
这里也分享一个可用的创建 react 页面组件的 cr.template.js: (基于 typescript 和 css module)
// template.js you can generate
// 转成大驼峰
function toUpper(str) {
const camelizeRE = /-(\w)/g
// 把每个横杆分割的转成大写
var temp = str.replace(camelizeRE, ($0, $1) => {
return $1 ? $1.toUpperCase() : ''
})
// 再把首字母转成大写
return temp[0].toUpperCase().concat(temp.slice(1))
}
// 可以利用axios请求网络上的数据
const axios = require('axios')
// 之后的option会与该默认选项合并
const defaultOption = {
// 是否忽略生成该文件
ignore: false,
// 如果文件存在,是否强制覆盖
isForceOverwrite: false
}
// 导出生成文件的描述数组
const fileList = [
{
// 生成导出组件的js文件
label: 'jsxTemplate',
// 生成文件的路径
filePath: './index.tsx',
// isForceOverwrite: true,
// content为函数,返回文件的内容
content: componentName => {
const name = toUpper(componentName)
const interfaceName = `I${name}Props`
const content = `
// Generated by @xiao-edu/cr\n\n
import React, { useState, useEffect } from 'react'
import styles from './index.module.scss'
import classnames from 'classnames'
// import { Button } from 'antd-mobile'
interface ${interfaceName} {
}
const ${name}: React.FC<${interfaceName}> = (props) => {
// 声明一个叫 'count' 的 state 变量。
// const [count, setCount] = useState(0)
useEffect(() => {
return () => {
}
})
const pageClass = classnames({
[styles.wrap]: true,
'with-padding': true,
'with-bg': false,
'with-scroll': false,
})
return (
<React.StrictMode>
<div className={pageClass}>
{'${name}页面'}
</div>
</React.StrictMode>
)
}
export default ${name}
`
return content.trim()
}
},
{
// 生成组件对应的scss文件
label: 'scssTemplate',
// 如果临时不想生成这个文件,可以设置ignore为true
// ignore: true,
filePath: './index.module.scss',
content: componentName => {
const content = `
@import '@/styles/vars.scss';
.wrap{
width: 100%;
height: 100%;
box-sizing: border-box;
}
`
return content
}
}
]
module.exports = {
fileList,
defaultOption
}
将其放置在 src/views 下,假设你想生成的页面文件夹名为:student-count,每次运行:
cr src/views/student-count
cr 会找到最近的祖先的 cr.template.js,根据其中导出的 fileList 来生成文件夹 student-count 及其内部的所有文件。
即可根据模板自动生成对应的页面组件,且页面会遵循一致的组件结构。
使用情况
- 个人指标(移动端)
- 大数据平台(PC 端)
Authors
License
MIT