You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#!/usr/bin/env node
'use strict';constcurrentNodeVersion=process.versions.node;constsemver=currentNodeVersion.split('.');constmajor=semver[0];if(major<10){console.error('You are running Node '+currentNodeVersion+'.\n'+'pmCli requires Node 10 or higher. \n'+'Please update your version of Node.');process.exit(1);}require('../packages/initialization')();
// 创建工程program.usage("[command]").command("init").option("-f,--force","overwrite current directory").description("initialize your project").action(initProject);// 新增页面program.command("add-page <page-name>").description("add new page").action(addPage);// 新增模块program.command("add-mod [mod-name]").description("add new mod").action(addMod);// 添加/修改 .pmConfig.jsonprogram.command("modify-config").description("modify/add config file (.pmCli.config)").action(modifyCon);program.parse(process.argv);
/** * 校验名称合法性 * @param {string} name 传入的名称 modName/pageName * @param {Array}} validateNameList 非法名数组 */constcheckNameValidate=(name,validateNameList=[])=>{constvalidationResult=validatePageName(name);if(!validationResult.validForNewPackages){console.error(chalk.red(`Cannot create a mod or page named ${chalk.green(`"${name}"`)} because of npm naming restrictions:\n`));[
...(validationResult.errors||[]),
...(validationResult.warnings||[]),].forEach((error)=>{console.error(chalk.red(` * ${error}`));});console.error(chalk.red("\nPlease choose a different project name."));process.exit(1);}constdependencies=["rax","rax-view","rax-text","rax-app","rax-document","rax-picture",].sort();validateNameList=validateNameList.concat(dependencies);if(validateNameList.includes(name)){console.error(chalk.red(`Cannot create a project named ${chalk.green(`"${name}"`)} because a page with the same name exists.\n`)+chalk.cyan(validateNameList.map((depName)=>` ${depName}`).join("\n"))+chalk.red("\n\nPlease choose a different name."));process.exit(1);}};
// execute a single shell command where "cmd" is a stringexports.exec=function(cmd,cb){// this would be way easier on a shell/bash script :Pvarchild_process=require("child_process");varparts=cmd.split(/\s+/g);varp=child_process.spawn(parts[0],parts.slice(1),{stdio: "inherit"});p.on("exit",function(code){varerr=null;if(code){err=newError('command "'+cmd+'" exited with wrong status code "'+code+'"');err.code=code;err.cmd=cmd;}if(cb)cb(err);});};// execute multiple commands in series// this could be replaced by any flow control libexports.seriesAsync=(cmds)=>{returnnewPromise((res,rej)=>{varexecNext=function(){letcmd=cmds.shift();console.log(chalk.blue("run command: ")+chalk.magenta(cmd));shell.exec(cmd,function(err){if(err){rej(err);}else{if(cmds.length)execNext();elseres(null);}});};execNext();});};
前言
脚手架其实是大多数前端都不陌生的东西,基于前面写过的两篇文章:
大概呢,就是介绍下,目前我的几个项目页面的代码组织形式。
用了几个项目后,发现也挺顺手,遂想着要不搞个
cli
工具,统一下源码的目录结构吧。这样不仅可以减少一个机械的工作同时也能够统一源码架构。同学间维护项目的陌生感也会有所降低。的确是有一部分提效的不是。虽然我们大多数页面都走的大黄蜂搭建🥺。。。
bin
效果
工程目录
代码实现
这里是入口文件,比较简单,就是配置个入口,顺便校验
node
的版本号这个文件主要是配置一些命令,其实也比较简单,大家从
commander
里面查看自己需要的配置,然后配置出来就可以了就是根据自己需求去配置这里就不赘述了,除了以上,就以下两点实现:
所谓兜底就是输入
pm-cli
后没有跟任何命令![](/Users/nealyang/Library/Application Support/typora-user-images/image-20200708113141542.png)
pm-cli init
在说
init
之前呢,这里有个技术背景。就是我们的rax
工程,基于 def 平台初始化出来的,所以说自带一个脚手架。但是我们在源码开发中呢,会对其进行一些改动。为了避免认知重复呢,init
我分为两个功能:init projectName
从 0 创建一个def init rax projectName
项目init projectName
init
至于这里的一些问题的交互就不介绍了,就是
inquirer
配置的一些问题而已。没有太大的参考价值 。入口方法较为简单,其实就是区分当前运行
pm-cli init
到底是基于已有项目初始化,还是新建一个rax
项目 ,判断依据也非常简单,就是判断当前目录下是否有package.json
来虽然这么判断感觉是草率了点,但是,你细品也确实如此!对于有 package.json 的当前目录,我还会去校验别的不是。
如果当前目录存在 package.json,那么我认为你是一个项目,想在此项目中,初始化拍卖源码架构的配置。所以我会去判断当前项目是否已经初始化过了。
也就是这个
PM_CLI_CONFIG_FILE_NAME
的内容。那么则给出提示。毕竟不需要重复初始化嘛。如果你想强行再初始化一次,也可以!准备工作坐在前期,最终运行的功能都在
run
方法里面。校验名称合法性
这里还有个功能函数非常的通用,也就提前拿出来说了吧。
其实就是校验名称合法性以及排除重名。这个工具函数可以直接 CV。
如上的流程图,我们已经走到
run
方法了,剩下的就是里面的一些判断。因为这些判断也不是非常的具有参考价值,这里就简单跳过了,然后在重点介绍下一些公共方法的编写。
addTsConfig
上面的代码大家都能读的懂,粘贴这一段代码的目的就是,希望大家写cli 的时候,一定要多考虑边界情况,存在性判断,以及一些异常兜底。避免不必要的 bug 产生
rewriteAppJson
别的重写方法就不粘贴了,因为也是比较枯燥且重复的。下面说一下公共方法和用处吧
下载模板
下载模板这里我直接用的 shell 脚本,因为这里涉及到很多权限的问题。
shell
copyFiles
在将远程代码拷贝到源码目录
temps/
下,进行一波修改后,还是需要copy
到项目目录中的,所以这里封装了一个方法。配置文件
配置文件是我为了标识出当前项目,是否为
pmCli
初始化所得。因为在addPage
的时候,page
中的一些页面会使用到外部的组件,比如loadingPage
如上,
initProject:true|false
用来标识当前仓库。[pageName]
用来表示有哪些页面是用pmCli
新建的。属性type:'simpleSource'|'withContext'|'customStateManage'
则用来告诉后续add-mod
到底添加哪种类型的模块。同时呢,对内容进行了加密,因为配置页面,是放在用户的项目下的
加密
基本上如上,初始化项目的功能就介绍完了,后面的功能都是换汤不换药的这些操作。咱们走马观花,提个要点。
pm-cli add-page
流程图
上面的功能,其实就是跟
initProject
里面的代码相似,就是一些“业务”情况的判断不同而已。pm-cli add-mod
其实模块的新增也没有特别的技术点。先选择页面列表,然后读取
.pmCli.config
中的页面的类型。根据类型去新增页面矫正 CURR_DIR
在添加模块的时候,我还做了个人性化处理。防止好心人以为要到
cd
到指定pages
下才能addMod
,所以我支持只要你在src
、pages
或者项目根目录下,都可以执行add-mod
pm-cli modify-config
因为之前介绍过源码的页面架构,同时我也应用到了项目开发中。开发
pmCli
的时候,又新增了新增了配置文件,存在本地还是加密的。那么岂不是我之前的项目需要新增页面还不能用这个pmCli
?所以,就新增了这个功能:
modify-config
:pmCli
,没有则新建,有,则修改注意点(总结)
fs-extra
+shell
就能玩起来,非常简单THE LAST TIME
TODO
工具
所谓工欲善其事必先利其器,在
cli
避免不了使用非常多的工具,这里我主要是使用一些开源包以及从CRA
里面 copy 过来的方法。commander
Inquirer
fs-extra
semver
chalk
clui
download-git-repo
ora
shelljs
validate-npm-package-name
blessed-contrib
本来这些工具打算单独写一篇文章的,但是堆 list 的文章的确不是很有用。容易忘主要是,所以这里就带过了。功能和效果,大家自行查看和测试吧。然后
CRA
中的比较不错的方法,我也在文章末尾列出来了。关于CRA
的源码阅读,也可以查看我以往的文章:github/Nealyang
CRA 中不错的方法/包
commander
:概述一下,Node
命令接口,也就是可以用它代管Node
命令。npm地址envinfo
:可以打印当前操作系统的环境和指定包的信息。 npm地址fs-extra
:外部依赖,Node
自带文件模块的外部扩展模块 npm地址semver
:外部依赖,用于比较Node
版本 npm地址checkAppName()
:用于检测文件名是否合法,isSafeToCreateProjectIn()
:用于检测文件夹是否安全shouldUseYarn()
:用于检测yarn
在本机是否已经安装checkThatNpmCanReadCwd()
:用于检测npm
是否在正确的目录下执行checkNpmVersion()
:用于检测npm
在本机是否已经安装了validate-npm-package-name
:外部依赖,检查包名是否合法。npm地址printValidationResults()
:函数引用,这个函数就是我说的特别简单的类型,里面就是把接收到的错误信息循环打印出来,没什么好说的。execSync
:引用自child_process.execSync
,用于执行需要执行的子进程cross-spawn
:Node
跨平台解决方案,解决在windows
下各种问题。用来执行node
进程。npm地址dns
:用来检测是否能够请求到指定的地址。npm地址参考
xBuild
深度解析
create-react-app
源码create-react-app 源码解析之react-scripts
50 个最好用的命令行工具
The text was updated successfully, but these errors were encountered: