adonis命令模块学习笔记
来源:互联网 发布:网络教学英语作文优点 编辑:程序博客网 时间:2024/06/07 16:42
基本类(文件):
adonis命令脚本,bin目录下
Kernel类,用来暂时注册命令并且执行命令最外层
Command类,Kernel类直接包含的对象,adonis自己的命令类,包含各种执行命令相关方法
Commander类(在commander模块中也叫Command类):Command类的属性,是一个事件发射器
1.adonis脚本
const path = require('path')//cli模块下包含所有命令的对象const Commands = require('./src/Commands')//命令名称数组const commandNames = []//需要供应商的命令,应该在app.js文件中设置命令供应商,并且使用ace命令来调用这些命令const needProviders = ['repl', 'route:list', 'run:instructions']//获取ace模块const ace = require('./lib/ace')Object.keys(Commands).forEach((name) => {//将命令名称加入数组 commandNames.push(name)//ace加入命令模块导出的对象 ace.addCommand(Commands[name])})try {//获取需要执行的命令名称,如node adonis new中的newconst command = process.argv[2]//如果命令名称有效,且不需要供应商if (commandNames.indexOf(command) > -1 && needProviders.indexOf(command) <= -1) {//这一步将回调方法注册到commander上,对应的事件为commander:xxx,xxx为命令名称 ace.wireUpWithCommander()//这一步通过parse方法、parseArgs方法解析参数,根据命令在commander上触发指定事件 ace.invoke(require('./package')) } else {//执行当前目录下ace文件 require(path.join(process.cwd(), 'ace')) }} catch (error) { if (error.code !== 'ENOENT' && error.code !== 'MODULE_NOT_FOUND') { throw error }//如果是上述两个错误码,继续执行命令 ace.wireUpWithCommander() ace.invoke(require('./package'))}@adonisjs/cli/src/Commands/index.js包含了所有有效命令与对应的导出对象module.exports = {//new命令 new: require('./New'),//install命令 install: require('./Install'),//serve命令 serve: require('./Serve'),//各种帮助名命令 //生成应用程序密钥 'key:generate': require('./KeyGenerate'), //生成控制器 'make:controller': require('./Make/Controller'), //生成模型 'make:model': require('./Make/Model'), //生成模型特性 'make:trait': require('./Make/Trait'), //生成视图 'make:view': require('./Make/View'), //生成中间件 'make:middleware': require('./Make/Middleware'), //生成命令 'make:command': require('./Make/Command'), //生成异常 'make:exception': require('./Make/Exception'), //生成模型钩子 'make:hook': require('./Make/Hook'), //生成数据库迁移类,即Schema类 'make:migration': require('./Make/Migration'), //生成监听器 'make:listener': require('./Make/Listener'), //在此环境下可以直接在命令行使用数据库查询构建器与数据库操作,不过必须在项目根目录下执行,且项目要启动 'repl': require('./Repl'), //生成异常处理器 'make:ehandler': require('./Make/ExceptionHandler'), 'make:seed': require('./Make/Seed'), //路由列表 'route:list': require('./RouteList'), 'run:instructions': require('./Instructions')}
2.@adonisjs/ace/src/Kernel/index.js中的Kernel类
//鲁大师const _ = require('lodash')//颜色输出const chalk = require('chalk')const isArrowFunction = require('is-arrow-function')//ace的Command类const Command = require('../Command')//原生命令模块const commander = require('../../lib/commander')//空白const WHITE_SPACE = ''//用来注册、调用命令的类class Kernel { constructor () { //注册的命令对象,键为命令名称,值为命令对象,包含handle处理方法 this.commands = {} }//添加一个命令对象 addCommand (command) {//如果是字符串,从容器中获取,一般此时Ioc容器还未初始化,这个不是为一般情况准备的 if (typeof (command) === 'string' && global.use) { command = global.use(command) }//不是Command的子类,抛出异常 if (command.prototype instanceof Command === false) { throw new Error(`Make sure ${command.name} extends the base command`) }//调用引导方法 command.boot()//作为键值对存储 this.commands[command.commandName] = command }//将每个Command命令链接到Commander模块对应的类上 wireUpWithCommander () { _.each(this.commands, (command) => {//这一步会创建一个Commander实例,设置到Command实例中去,并且在Commander实例上为命令设置监听器 command.wireUpWithCommander() }) }//调用当前Kernel中对应的Command invoke (packageJson) { process.env.NO_ANSI = 'false'//从下标为2的元素开始选择 const commandName = process.argv.slice(2)//如果这个数组不存在,或者原参数数组中下标为2的元素不存在,输出帮助信息 if (!commandName || !commandName[0]) {//如果不存在,使用底层命令模块输出帮助信息 return commander.outputHelp() }//打印adonis版本 if (packageJson && packageJson.version) { commander.version(packageJson.version) }//使用commander来解析参数,这一步会触发Command对象包含的Commander(在commander模块中也叫Command类,为了区分)类上的相应事件 commander.parse(process.argv) }}//为command:*命令事件注册回调函数,即当使用的命令无效时触发这个事件commander .command('*') .action(function (command) { console.log(`\n error: \`${command}\` is not a registered command \n`) process.exit(1) })
3.@adonisjs/ace/src/Command/index.js中的Command类
//命令基类class Command { constructor () { //可以在shell上输出颜色字体的方法 this.chalk = new chalk.constructor({ enabled: process.env.NO_ANSI === 'false' })//各种状态图标 this.iconsMain = { info: this.chalk.cyan('ℹ'), success: this.chalk.green('✔'), warn: this.chalk.yellow('⚠'), error: this.chalk.red('✖') }//windows系统使用的状态图标 this.iconsWin = { info: this.chalk.cyan('i'), success: this.chalk.green('√'), warn: this.chalk.yellow('‼'), error: this.chalk.red('×') } }//初始化类属性 static _initiate () { this._booted = false//未被引导 this._name = ''//commonder模块下的Command类实例 this.command = null//参数 this.args = []//选项 this.options = [] }//绑定commonder模块下的Commond类实例 static _bindCommander () {//参数为命令名称 this.command = commander.command(this.commandName).description(this.description) }//注册所有参数到commander static _registerArgsWithCommander () { this.command.arguments(this._stringifyArgs().trim()) }//将选项注册到commonder实例 static _registerOptionsWithCommander () { _.each(this.options, (option) => { this.command.option(this._getArgOrOptionName(option), option.description) }) }//命令名 static get commandName () { return this._name }//命令签名 static get signature () { return '' }//命令描述 static get description () { return '' }//添加一个参数对象,可以在引导方法里调用//参数与选项不是随意的,必须使用方法添加,或者在签名中说明才可以在处理方法中使用static addArgument (arg = {}) {//确保引导了 this._ensureIsBooted() let mergedArg = {} _.merge(mergedArg, defaults, arg) this._validateName(mergedArg.name) this.args.push(mergedArg) return this }//添加选项 static addOption (option = {}) { this._ensureIsBooted() let mergedOption = {} _.merge(mergedOption, defaults, option) this._validateName(mergedOption.name) this.options.push(mergedOption) return this }//解析签名 static parseSignature () { if (this.signature) {//获取命令名称、参数、选项的数组 const [name, ...tokens] = this.signature.trim().split(' ')//设置命令名称 this._name = name.trim() const parsedTokens = parser.parseSignature(tokens.join(' '))//添加签名得到的参数 _.each(parsedTokens.args, this.addArgument.bind(this))//添加签名得到的选项 _.each(parsedTokens.options, this.addOption.bind(this)) } }//打印帮助信息 static outputHelp (colorize = process.env.NO_ANSI === 'false') { const ctx = new chalk.constructor({ enabled: colorize }) const stringifiedArgs = this._stringifyArgs() const maxWidth = this.biggestArg() const strings = [] strings.push(ctx.magenta.bold('Usage:')) const args = stringifiedArgs.length ? ` ${stringifiedArgs}` : '' strings.push(` ${this.commandName}${args} [options]`) if (this.args.length) { strings.push(WHITE_SPACE) strings.push(ctx.magenta.bold('Arguments:')) _.each(this.args, (arg) => { strings.push(` ${ctx.blue(_.padEnd(arg.name, maxWidth))} ${arg.description}`) }) } if (this.options.length) { strings.push(WHITE_SPACE) strings.push(ctx.magenta.bold('Options:')) _.each(this.options, (option) => { strings.push(` ${ctx.blue(_.padEnd(this._getArgOrOptionName(option), maxWidth))} ${option.description}`) }) } if (this.description) { strings.push(WHITE_SPACE) strings.push(ctx.magenta.bold('About:')) strings.push(` ${this.description}`) } strings.push(WHITE_SPACE) return strings.join('\n') }//引导方法 static boot () { if (this._booted) { return this }//初始化 this._initiate() this._booted = true//解析签名 this.parseSignature() if (!this.commandName) { throw new Error('Make sure to define the command name') } return this }//将自身链接到commonder实例 static wireUpWithCommander () {//绑定一个comonder实例,这一步只是创建并且设为属性 this._bindCommander()//注册参数到commonder this._registerArgsWithCommander()//注册选项到commonder this._registerOptionsWithCommander()//在commander上注册回调函数,即commander:{commandName}事件的回调函数//被注册回调函数为Command类的commanderAction方法,间接调用了handle方法this.command.action(this.commanderAction.bind(this)) return this }//命令动作,也是被注册的回调函数 static commanderAction (...input) {// const command = _.last(input) const args = _.transform(_.initial(input), (result, arg, index) => { result[this.args[index].name] = arg || this.args[index].defaultValue || null return result }, {}) const options = _.transform(command.opts(), (result, option, name) => { result[name] = result[name] = command.hasOwnProperty(name) ? command[name] : null return result }, {}) return this.exec(args, options, true) }//执行命令对象的handle方法 static exec (args, options, viaAce) {//检测make是否是全局,并且新建一个Command实例 const commandInstance = typeof (global.make) === 'function' ? global.make(this) : new this()//是否通过ace执行 commandInstance.viaAce = viaAce//执行handle方法 return commandInstance.handle(args, options) }//默认处理方法为空 handle () { throw new Error(`Make sure to implement handle method for ${this.constructor.commandName} command`) }//输出各种颜色信息 info (...input) { console.log(this.chalk.cyan(...input)) } warn (...input) { console.warn(this.chalk.yellow(...input)) } success (...input) { console.log(this.chalk.green(...input)) } error (...input) { console.error(this.chalk.red(...input)) } completed (action, message) { console.log(`${this.chalk.green(action + ':')} ${message}`) } failed (action, message) { console.error(`${this.chalk.red(action + ':')} ${message}`) }//输出表信息 table (head, body, style = { head: ['cyan'] }) { const table = new Table({head, style}) if (_.isArray(body)) { _.each(body, (item) => { table.push(item) }) } else if (_.isPlainObject(body)) { _.each(body, (value, key) => { table.push([key, value]) }) } console.log(table.toString()) }//获取图标 icon (type) { return process.platform === 'win32' ? this.iconsWin[type] : this.iconsMain[type] }//文件操作 writeFile (file, content, options) { return fs.outputFile(file, content, options) } }}//为命令类注册了一些提问方法,可以与用户交互QUESTION_METHODS.forEach((method) => { Command.prototype[method] = function (...input) { return new Question()[method](...input) }})Command类提供了一些执行命令的工具方法,它将命令相关事件注册到了commander实例中去,并且将自己的handle方法作为回调,可以说完全委托commander来执行命令了
4.commander模块
//Commond类function Command(name) {//子对象数组 this.commands = [];//选项数组 this.options = [];//可执行命令名称 this._execs = {};//允许的未知选项 this._allowUnknownOption = false; this._args = []; this._name = name || '';}/继承了事件发射器Command.prototype.__proto__ = EventEmitter.prototype;//添加一个name代表的命令并返回它Command.prototype.command = function(name, desc, opts) { if(typeof desc === 'object' && desc !== null){ opts = desc; desc = null; } opts = opts || {}; var args = name.split(/ +/);//使用命令名称创建命令对象 var cmd = new Command(args.shift());//有描述,执行描述方法 if (desc) { cmd.description(desc);//可执行的 this.executables = true;//将执行命令名称添加到数组 this._execs[cmd._name] = true; if (opts.isDefault) this.defaultExecutable = cmd._name; } cmd._noHelp = !!opts.noHelp;//将自身添加到数组 this.commands.push(cmd); cmd.parseExpectedArgs(args);//父对象 cmd.parent = this; if (desc) return this; return cmd;};//注册回调函数Command.prototype.action = function(fn) { var self = this;//定义监听器方法 var listener = function(args, unknown) { // Parse any so-far unknown options args = args || []; unknown = unknown || [];//解析选项 var parsed = self.parseOptions(unknown);//必要时输出帮助信息 outputHelpIfNecessary(self, parsed.unknown);//如果有未知选项,停止进程 if (parsed.unknown.length > 0) { self.unknownOption(parsed.unknown[0]); } // Leftover arguments need to be pushed back. Fixes issue #56 if (parsed.args.length) args = parsed.args.concat(args); self._args.forEach(function(arg, i) { if (arg.required && null == args[i]) { self.missingArgument(arg.name); } else if (arg.variadic) { if (i !== self._args.length - 1) { self.variadicArgNotLast(arg.name); } args[i] = args.splice(i); } }); if (self._args.length) { args[self._args.length] = self; } else { args.push(self); }//!!!!!!!!!!!!//给回调函数绑定commander实例,与参数 fn.apply(self, args); };//有没有父命令 var parent = this.parent || this; var name = parent === this ? '*' : this._name;//当发生command:xxxx事件时激活监听器 parent.on('command:' + name, listener);//需要时注册别名 if (this._alias) parent.on('command:' + this._alias, listener); return this;};/解析参数,设置选项,当定义之后调用命令Command.prototype.parse = function(argv) { // implicit help//如果可执行 if (this.executables) this.addImplicitHelpCommand();//存储原始参数 // store raw args this.rawArgs = argv; // guess name//猜测命令名词 this._name = this._name || basename(argv[1], '.js'); // github-style sub-commands with no sub-command if (this.executables && argv.length < 3 && !this.defaultExecutable) { // this user needs help//添加帮助参数 argv.push('--help'); } // process argv//解析格式化后的选项 var parsed = this.parseOptions(this.normalize(argv.slice(2))); var args = this.args = parsed.args;//解析参数,返回结果//!!!!!!!!!!!!!!注意在这一步中会根据命令触发相应的command:事件,然后调用相应的回调方法 var result = this.parseArgs(this.args, parsed.unknown); // executable sub-commands var name = result.args[0]; var aliasCommand = null; // check alias of sub commands if (name) { aliasCommand = this.commands.filter(function(command) { return command.alias() === name; })[0]; }//执行子命令 if (this._execs[name] && typeof this._execs[name] != "function") { return this.executeSubCommand(argv, args, parsed.unknown); } else if (aliasCommand) { // is alias of a subCommand args[0] = aliasCommand._name; return this.executeSubCommand(argv, args, parsed.unknown); } else if (this.defaultExecutable) { // use the default subcommand args.unshift(this.defaultExecutable); return this.executeSubCommand(argv, args, parsed.unknown); } return result;};//解析命令参数Command.prototype.parseArgs = function(args, unknown) { var name;//参数长度存在 if (args.length) { name = args[0];//如果命令对应的监听器存在,发射指定命令的事件 if (this.listeners('command:' + name).length) { this.emit('command:' + args.shift(), args, unknown); } else {//如果监听器不存在发射command:*的事件,在Kernel中已经注册 this.emit('command:*', args); } } else {//如果参数长度部队,输出帮助信息 outputHelpIfNecessary(this, unknown); // If there were no args and we have unknown options, // then they are extraneous and we need to error. if (unknown.length > 0) { this.unknownOption(unknown[0]); } } return this;};
可见主要的路线为:
Kernel.wireUpWithCommander()->
Command.wireUpWithCommander()->
Commander(这里是模块名称,其实类名还是Command).action(fn)
注册了Command.commanderAction()->exec()->handle()回调方法到command:+name命名空间
Kernel.invoke()->
Commander.parse()->
parseArgs()通过参数激发回调
阅读全文
0 0
- adonis命令模块学习笔记
- adonis命令new
- adonis命令serve
- MM模块学习笔记
- MM模块学习笔记
- FUNC模块学习笔记
- Python学习笔记----模块
- 登陆模块学习笔记
- time 模块学习笔记
- ngx_lua模块学习笔记
- Python学习笔记----模块
- Python学习笔记--模块
- 【Python学习笔记】模块
- Python学习笔记-模块
- Python模块学习笔记
- [学习笔记]Python_os模块
- logging模块学习笔记
- Qt学习笔记:Qt模块模块简介
- ubuntu16.04下安装matlab2015b
- Angularjs date过滤器获得当前时间
- HTML项目实战程序
- virtualbox虚拟机与主机共享文件
- 【十八掌●基本功篇】第一掌:Java之多线程--2-join、同步、死锁、等待
- adonis命令模块学习笔记
- 查看混淆后的日志
- Java中的代理机制
- 算法入门
- Java实现——将数据存入剪切板
- Android学习随笔(13)------内容提供器
- 最长上升子序列问题(LIS)
- kubernet 在centos 搭建的集群上的实践 -- 《一》
- Gradle GradleWapper AndroidBuildTools版本兼容问题