Node手写自己的vue-cli并发布到npm

之前写vue项目都是用尤大佬固定的cli格式,自己为啥不能模仿一个呢?今天就讲怎么模仿

1.先创建一个入口文件index.js

里面先写上 #!/usr/bin/env node

2.创建package.json

npm init -y

3.配置环境变量

在这里插入图片描述

"lsh"这个名字可以随便取,入口index.js不能变
 然后npm link

4.安装commander

npm i commander

5.在index.js中

const program = require('commander')
//版本号
program.version(require('./package.json').version,'-v,--version')
program.parse(process.argv)

program.version是查看版本号,lsh --version 或者 -v,但是有的人喜欢用大V,-V,如果想用大V的话,这两个同时写

program.version(require('./package.json').version,'-v,--version')  
program.version(require('./package.json').version)

6.创建一个help.js文件

这个文件就放我们命令help,比如:npm i cli -l,npm i cli -d,npm I cli -f等等

const program = require('commander');

const helpOptions = () => {
  //增加自己的options
  program.option('-l --lsh', 'a lsh cli')
  //可选 -d,类似--save
  program.option('-d --dest <dest>', 'a destination folder,例如:-d /src/components')
  //拉取不一样的框架,例如vue,react
  program.option('-f --framework <framework>', 'your framework')

  program.on('--help', function () {
    console.log('others')
  })
}

module.exports = helpOptions

7.创建一个actions.js文件

这个文件放我们创建项目,创建路由等等代码

首先要安装一个插件download-git-repo,从git上获取克隆地址

npm i download-git-repo

8.创建一个terminal.js文件

这个文件就放我们执行终端命令相关的代码的代码

const { spawn } = require('child_process')

const commandSpawn = (...args) =>{
return new Promise((resolve,reject)=>{
  const childProcess = spawn(...args)
  childProcess.stdout.pipe(process.stdout)
  childProcess.stderr.pipe(process.stderr)
  //执行完了
  childProcess.on('close',()=>{
     resolve()
  })
})
}

module.exports = {
  commandSpawn
}

9.创建一个repo-config.js文件

我们可以先用尤大佬内置的vue-cli,创建一个项目,然后根据我们的习惯,把一些插件啊和要用的配置写好,然后上传github或者gitlab,码云都可以,把地址记住就行

地址前面要加direct
let vueRepo = 'direct:https://github.com/';

module.exports = {
  vueRepo
}

10.在actions.js中

还需要安装一个open插件,为的是我们创建好项目之后,自动运行到浏览器

npm i open

代码

const download = promisify(require('download-git-repo'));
const { vueRepo } = require('../config/repo-config');
const { commandSpawn } = require('../utils/terminal')

const createProjectAction = async (project) => {
  console.log('lsh loading create')
  // 1.clone项目
  await download(vueRepo, project, { clone: true });
  // 2.执行npm install
  //windows和其他mac,linux不同
  const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'
  await commandSpawn(command, ['install'], { cwd: `./${project}` })
  // 3.运行npm run serve
  commandSpawn(command, ['run', 'serve'], { cwd: `./${project}` })
  // 4.打开浏览器
  open("http://localhost:8080/")
}


module.exports = {
  createProjectAction
}

11.创建一个create.js

这里面就放具体创建的命令,相当于把actions.js封装了一下

const program = require('commander');
const { createProjectAction }= require('./actions')
const createCommands = () => {
       //创建
       program
              .command('create <project> [others...]')
              //描述
              .description('clone a repository into a folder')

              //执行
              .action(createProjectAction);
}
module.exports = createCommands

12.在index.js中

引入这些配置好的js
const helpOptions = require('./lib/core/help.js')
const createCommands = require('./lib/core/create.js')
//帮助和可选信息
helpOptions()
//创建其他指令
createCommands()

13.开始创建项目

lsh create demo

然后我们github上地址的项目就被创建出来了

14.命令创建组件

当我们lsh addcpn template 或者lsh addcpn template -d src/component时候,会给我们创建好一个我们写好的组件模版

前面是默认地址,后面是指定创建地址

15.创建一个vue-component.ejs组件模版

<template>
  <div class="<%= data.lowerName %>">
    <h1>{
   
   { msg }}</h1>
  </div>
</template>

<script>
export default {
  name: '<%= data.name %>',
  props: {
    msg: String
  },
  components: {
  },
  mixins: [],
  data: function() {
    return {
      message: "<%= data.name %>"
    }
  },
  created: function() {

  },
  mounted: function() {

  },
  computed: {

  },
  methods: {

  }
}
</script>

<style scoped>
  .<%= data.lowerName %> {
    
  }
</style>

16.创建一个utils.js

这个就是相当于转译我们的ejs,需要安装

npm i ejs

代码

const fs = require('fs')

const path = require('path')
const ejs = require('ejs')

const compile = (templateName,data) =>{
  const remplatePosition = `../templates/${templateName}`
  const templatePath = path.resolve(__dirname,remplatePosition)
  
  return new Promise((resolve,reject)=>{
    ejs.renderFile(templatePath,{data},{}, (err,result)=>{
      if(err){
        console.log(err)
        reject(err);
        return;
      }
      resolve(result)
    })
  })
}

//addpage的时候,同时创建一个文件夹包裹.vue和router.js
// source/components/category/why
const createDirSync = (pathName) => {
  if (fs.existsSync(pathName)) {
    return true;
  } else {
    if (createDirSync(path.dirname(pathName))) {
      fs.mkdirSync(pathName);
      return true;
    }
  }
}

const writeToFile = (path,content) =>{
  // 判断path是否存在, 如果不存在, 创建对应的文件夹
  return fs.promises.writeFile(path,content)
}

module.exports = {
  compile,
  writeToFile,
  createDirSync
}

17.actions.js中

const { compile,writeToFile,createDirSync } = require('../utils/utils')

//添加组件的action

const addCpnAction = async (name, dest) => {
  // 1.编译ejs模块result
  const data = {name, lowerName: name.toLowerCase()};
  const result = await compile("vue-component.ejs", data)
  // 2.将result写入到.vue文件中
  const targetPath = path.resolve(dest,`${name}.vue`)
  writeToFile(targetPath,result)
  // 4.放到对应到文件夹中
}
module.exports = {
  addCpnAction
}

18.create.js中

const { addCpnAction} = require('./actions')

const createCommands = () => {
       //创建
       program
              .command('addcpn <name>')
              //描述
              .description('add vue component,例如:lsh addcpn helloword -d src/components')

              //执行
              .action((name) => {
                     addCpnAction(name, program.dest || 'src/components')
              });


}

module.exports = createCommands

19.创建组件

lsh addcpn template      
lsh addcpn template -d src/pages/src

第一个是没有指定创建地址,所以默认地址就是src/components
第二个指定创建地址,地址就是src/pages/src

20.创建路由和路由

其实跟前面一个原理,我们把方法全封装了起来,所以就很方便

先写好ejs模块,创建一个vue-router.ejs文件

// 普通加载路由
// import <%= data.name %> from './<%= data.name %>.vue'
// 懒加载路由
const <%= data.name %> = () => import('./<%= data.name %>.vue')
export default {
  path: '/<%= data.lowerName %>',
  name: '<%= data.name %>',
  component: <%= data.name %>,
  children: [
  ]
}

21.actions.js中

// 添加组件和路由
const addPageAndRouteAction = async (name, dest) => {
  // 1.编译ejs模板
  const data = {name, lowerName: name.toLowerCase()};
  const pageResult = await compile('vue-component.ejs', data);
  const routeResult = await compile('vue-router.ejs', data);

  // 3.写入文件
  const targetDest = path.resolve(dest, name.toLowerCase());
  if (createDirSync(targetDest)) {
    const targetPagePath = path.resolve(targetDest, `${name}.vue`);
    const targetRoutePath = path.resolve(targetDest, 'router.js')
    writeToFile(targetPagePath, pageResult);
    writeToFile(targetRoutePath, routeResult);
  }
}
module.exports = {
addPageAndRouteAction
}

21.create.js中

const program = require('commander');

const { 
        addPageAndRouteAction,} 
        = require('./actions')

const createCommands = () => {
       program
              .command('addpage <page>')
              .description('add vue page and router config, 例如: why addpage Home [-d src/pages]')
              .action((page) => {
                     addPageAndRouteAction(page, program.dest || 'src/pages')
              })

}
module.exports = createCommands

22.创建

跟前面创建组件是一模一样的,所以就不写了,这就是封装的好处,方便

23.发布到npm

首先到npm上创建一个自己的账号,这个就不说了,创建好账号之后登录

npm login

然后到自己包的根目录打开终端

npm publish

记住上传的version版本号要比你这个大,记得修改一下

有2个问题如果出现咋解决?

1.执行npm publish 报错:403 Forbidden - PUT https://registry.npmjs.org/kunmomotest - you must verify your email before publishing a new package: https://www.npmjs.com/email-edit

你的邮箱没有验证,去你绑定的邮箱验证一下即可

2.npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/ui-com - You do not have permission to p

名字重复,改一下

结尾

我把我的包已经发到npm上去了,有兴趣的可以看一下下,写的不好请别见怪,我也是小白,使用命令跟我上面写的是一样的

npm i lsh_vuenew_cli

我这个包的源码也发到github上去了,可以看看

https://github.com/lsh555/lsh-vuecli

猜你喜欢

转载自blog.csdn.net/weixin_45389051/article/details/109285133