1. 脚手架设计和框架搭建

通用组件库

commander

import { program, Command } from 'commander'

export function commandTest(commandName, version) {
  program
    .name(commandName)
    .usage(`<command> [options]`)
    .version(version)
    .option('-d, --debug', '是否开启调试模式', false)
    .option('-e, --envName <char>', '环境变量')
    .createHelp()

  program
    .command('clone <source> [destination]')
    .description('克隆')
    .action((source, destination) => {
      console.log('clone...', source, destination)
    })

  // 脚手架参数,监听所有命令劫持,当该命令没有注册时触发
  program
    .arguments('<cmd> [options]')
    .description('test command')
    .action((cmd, env) => {
      console.log(cmd, env)
    })

  // 注册子命令
  const service = new Command('service');
  service.command('start [port]')
    .description('启动服务')
    .action((port) => {
      console.log('start service ', port)
    })

  program.addCommand(service)

  program.parse(process.argv)

}

  1. 命令更换,实现多个脚手架串行互相调用
// 当添加第二个参数后,组合成新的命令:vue-cli-install
  program
    .command('install [name]', 'install package', {
	// 更换为vue-install命令
      executableFile: 'vue-install',
      // 默认命令
      isDefault: true,
      // 隐藏
      hidden: true,
    })
    .alias('i')
  1. 自定义help信息
// help info
program.helpInformation()
program.helpInformation = () => ''
program.on('--help', () => {
	console.log('your help info')
})
// 解析debug参数
program.on('option:debug', () => {
  console.log('开启了debug')
})
  1. 注册子命令
const service = new Command('service');
  service.command('start [port]')
    .description('启动服务')
    .action((port) => {
      console.log('start service ', port)
    })

  program.addCommand(service)
  1. 监听未知命令
program.on('command:*', function(obj) {
  console.log(obj)
})
console.log('可用命令', program.commands.map(cmd => cmd.name()))

url-jion

用于拼接url

npmlog 打印日志

'use strict';
// const log = require('npmlog')
import log from 'npmlog'
// 日志级别
log.level = process.env.LOG_LEVEL ? process.env.LOG_LEVEL : 'info'

log.addLevel("success", 2000, {fg: "green", bold: true})
log.addLevel("error", 5000, {fg: "red", bold: true})

// 前缀
log.heading = 'vue-cli'
log.headingStyle = { fg: 'white', bg: 'black' }

export default {
  success(msg) {
    log.success(msg)
  },
  error(msg) {
    log.error(msg)
  },
  info(msg) {
    log.info(msg)
  },
  debug(msg) {
    log.verbose(msg)
  },
  notice(msg) {
    log.notice(msg)
  },
};

fs-extra 文件操作

semver 版本比对

import semver from 'semver'
if (!semver.gte(currentVersion, lowestVersion)) {
    throw new Error(`vue cli 需要安装 v${lowestVersion}以上版本的node.js`)
  }

colors 打印颜色的文本

log.error(colors.red(e.message))

user-home 快速拿到主目录

import userHome from "user-home"
console.log(userHome)

dotenv 获取环境变量

import dotenv from 'dotenv'
const env = dotenv.config({
  path: path.resolve(userHome, ".env")
})
console.log(env)
  console.log(process.env.CLI_HOME)

root-check root检查和角色降级

import rootCheck from 'root-check'
rootCheck()

minimist

parse argument options


脚手架简介

脚手架本质上是一个操作系统的客户端,通过命令行执行,如:

vue create vue-test-app

上面的命令有三个部分组成:

  • 主命令
  • command:create
  • param:参数
vue create vue-test-app --force   --> --force true
vue create vue-test-app --force -r https://taobao.xxx

--force为option

-r也是option,为简称

vue命令:

$ ll vue 
lrwxr-xr-x  1 zhx  admin    39B  1 16  2022 vue -> ../lib/node_modules/@vue/cli/bin/vue.js

是一个超链接,执行了../lib/node_modules/@vue/cli/bin/vue.js, 该目录为node的全局依赖,所有通过npm i -g全局安装的依赖都会在这里。如:

ll ../lib/node_modules/
total 0
drwxr-xr-x   3 zhx  admin    96B  3  2  2022 @jh4j-cloud
drwxr-xr-x   4 zhx  admin   128B  1 16  2022 @vue
drwxr-xr-x   9 zhx  admin   288B  4  1  2022 lerna
drwxr-xr-x  10 zhx  admin   320B  1 15  2022 nrm
drwxr-xr-x  21 zhx  admin   672B  2 20  2022 pm2
drwxr-xr-x   7 zhx  admin   224B  7 27  2022 pnpm
  • 在终端输入vue create vue-test-app
  • 终端解析vue命令
  • 在环境变量中找到vue命令
  • 终端根据link找到实际文件vue.js
  • 终端利用node执行vue.js
  • vue.js解析command和param
  • 执行命令

脚手架实现原理

  1. 为什么通过npm全局安装了@vue/cli后,node下的bin目录就多了一个可执行文件链接到 项目的bin/vue.js上?
    观察项目的package.json
"bin": {
  "vue": "bin/vue.js"
}
  1. 执行vue命令的时候,vue指向了js文件,为什么可以直接执行?
    查看vue.js的第一行:
#!/usr/bin/env node  # 标识在环境变量中寻找node
#!/xxx/node  # 表示使用node命令执行该文件

到usr/bin/env环境变量中寻找node,然后通过node执行该文件

创建软链接

ln -s /xxx/xxx.js vue
unlink /xx/xx.js

脚手架本身是一个可执行文件 js, 就是操作系统的客户端,依赖node命令执行的一个客户端

脚手架开发流程

  1. 创建npm项目
mkdir lc-cli
cd lc-cli
npm init -y
  1. package.json
"bin": {
    "lc-cli": "bin/index.js"
  },
  1. touch bin/index.js
#!/usr/bin/env node

lc-cli
env: node\r: No such file or directory

vim设置换行符::set ff=unix

本地调试项目 npm link

# 将当前项目链接到到全局node_modules下作为一个库文件,并解析bin配置创建可执行文件
$ npm link
# e.g
$ npm link
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN @vue-lc-cli/cli@0.0.0 No repository field.

removed 1 package and audited 1 package in 2.497s
found 0 vulnerabilities

/opt/homebrew/bin/vue-cli -> /opt/homebrew/lib/node_modules/@vue-lc-cli/cli/bin/index.js
/opt/homebrew/lib/node_modules/@vue-lc-cli/cli -> /Users/zhx/study/web架构/cli/vue-lc-cli/core/cli

# 将当前项目中node_modules下的库文件链接到node全局node_modules下的库文件
npm link xx


# 解除链接 全局node库链接到当前项项目
npm unlink

# 取消连接 当前项目链接到全局node库
npm unlink xx

# 删除本地安装
npm remove -g xx

lerna

lerna create

创建包

lerna create core

lerna add

安装依赖

# 给每个模块安装依赖
lerna add vue
# 指定模块
lerna add vue --scope=@vue-lowcode/core

将所有相互依赖的包符号链接在一起,一般使用bootstrap命令

lerna link

lerna bootstrap

将本地包链接在一起并安装剩余的包依赖项

lerna bootstrap

lerna exec

# 在每个包下执行命令
lerna exec -- rm -rf node_modules
lerna exec -- rm -rf node_modules --scope=@vue-lowcode/core

安装依赖

check

require加载资源类型原理

  • js 寻找module.exports/exports
  • json JSON.parse()

require("xxx.txt") 对于非js、非json文件默认当成js文件解析

通过api获取npm包的信息

这里研究的是私有仓库,是通过 Verdaccio 搭建的。

  1. 命令
npm view @someScope/packageName versions --json
  1. 接口
https://private.registry.com/@someScope/packageName

es module

node支持es module的方式

  1. 通过webpack,webpack使用commonjs语法
    webpack.config.js
module.exports = {
  entry: "",
  output: {
    path: '',
	filename: ''
  },
  target: 'node'
}

webpack的构建环境target默认为'web',可以修改为node

babel为了兼容较低版本的node环境,如:低版本的node不认识 Promise,await等es7引入的语法

npm i -D babel-loader @babel/core @babel/preset-env
# 防止一个报错,regenerator not defined
npm i -D @babel/plugin-transform-runtime
# babel-runtime-corejs3
npm i -D @babel/babel-runtime-corejs3

在webpack.config.js中的module属性,引入loader

{
  module: {
    rules: [{
	// js结尾的文件
	  test: /\.js$/,
	  exclude: /(node_modules|disct)/,
	  use: {
	    loader: 'babel-loader',
		options: {
		// 转换,直接把babel的配置文件省略在这里配置
		  presets: ['@babel/preset-env'],
		  plugins: [
		    ['@babel/plugin-transform-runtime'], 
			{
			  corejs: 3,
			  regenerator: true,
			  useESModules: true,
			  helpers: true
			}
		  ]
		}
	  }
	}]
  }
}
  1. type="module"
posted @ 2023-02-17 20:57  fight139  阅读(66)  评论(0)    收藏  举报