ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ var MAX_LINE_WIDTH = process.stdout.columns || 200; var MIN_OFFSET = 25; var errorHandler; var commandsPath; var reAstral = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; var ansiRegex = /\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[m|K]/g; var hasOwnProperty = Object.prototype.hasOwnProperty; function stringLength(str){ return str .replace(ansiRegex, '') .replace(reAstral, ' ') .length; } function camelize(name){ return name.replace(/-(.)/g, function(m, ch){ return ch.toUpperCase(); }); } function assign(dest, source){ for (var key in source) if (hasOwnProperty.call(source, key)) dest[key] = source[key]; return dest; } function returnFirstArg(value){ return value; } function pad(width, str){ return str + Array(Math.max(0, width - stringLength(str)) + 1).join(' '); } function noop(){ // nothing todo } function parseParams(str){ // params [..] [..[optional]] // - require // [foo] - optional var tmp; var left = str.trim(); var result = { minArgsCount: 0, maxArgsCount: 0, args: [] }; do { tmp = left; left = left.replace(/^<([a-zA-Z][a-zA-Z0-9\-\_]*)>\s*/, function(m, name){ result.args.push(new Argument(name, true)); result.minArgsCount++; result.maxArgsCount++; return ''; }); } while (tmp != left); do { tmp = left; left = left.replace(/^\[([a-zA-Z][a-zA-Z0-9\-\_]*)\]\s*/, function(m, name){ result.args.push(new Argument(name, false)); result.maxArgsCount++; return ''; }); } while (tmp != left); if (left) throw new SyntaxError('Bad parameter description: ' + str); return result.args.length ? result : false; } /** * @class */ var SyntaxError = function(message){ this.message = message; }; SyntaxError.prototype = Object.create(Error.prototype); SyntaxError.prototype.name = 'SyntaxError'; SyntaxError.prototype.clap = true; /** * @class */ var Argument = function(name, required){ this.name = name; this.required = required; }; Argument.prototype = { required: false, name: '', normalize: returnFirstArg, suggest: function(){ return []; } }; /** * @class * @param {string} usage * @param {string} description */ var Option = function(usage, description){ var self = this; var params; var left = usage.trim() // short usage // -x .replace(/^-([a-zA-Z])(?:\s*,\s*|\s+)/, function(m, name){ self.short = name; return ''; }) // long usage // --flag // --no-flag - invert value if flag is boolean .replace(/^--([a-zA-Z][a-zA-Z0-9\-\_]+)\s*/, function(m, name){ self.long = name; self.name = name.replace(/(^|-)no-/, '$1'); self.defValue = self.name != self.long; return ''; }); if (!this.long) throw new SyntaxError('Usage has no long name: ' + usage); try { params = parseParams(left); } catch(e) { throw new SyntaxError('Bad paramenter description in usage for option: ' + usage, e); } if (params) { left = ''; this.name = this.long; this.defValue = undefined; assign(this, params); } if (left) throw new SyntaxError('Bad usage description for option: ' + usage); if (!this.name) this.name = this.long; this.description = description || ''; this.usage = usage.trim(); this.camelName = camelize(this.name); }; Option.prototype = { name: '', description: '', short: '', long: '', beforeInit: false, required: false, minArgsCount: 0, maxArgsCount: 0, args: null, defValue: undefined, normalize: returnFirstArg }; // // Command // function createOption(usage, description, opt_1, opt_2){ var option = new Option(usage, description); // if (option.bool && arguments.length > 2) // throw new SyntaxError('bool flags can\'t has default value or validator'); if (arguments.length == 3) { if (opt_1 && opt_1.constructor === Object) { for (var key in opt_1) if (key == 'normalize' || key == 'defValue' || key == 'beforeInit') option[key] = opt_1[key]; // old name for `beforeInit` setting is `hot` if (opt_1.hot) option.beforeInit = true; } else { if (typeof opt_1 == 'function') option.normalize = opt_1; else option.defValue = opt_1; } } if (arguments.length == 4) { if (typeof opt_1 == 'function') option.normalize = opt_1; option.defValue = opt_2; } return option; } function addOptionToCommand(command, option){ var commandOption; // short if (option.short) { commandOption = command.short[option.short]; if (commandOption) throw new SyntaxError('Short option name -' + option.short + ' already in use by ' + commandOption.usage + ' ' + commandOption.description); command.short[option.short] = option; } // long commandOption = command.long[option.long]; if (commandOption) throw new SyntaxError('Long option --' + option.long + ' already in use by ' + commandOption.usage + ' ' + commandOption.description); command.long[option.long] = option; // camel commandOption = command.options[option.camelName]; if (commandOption) throw new SyntaxError('Name option ' + option.camelName + ' already in use by ' + commandOption.usage + ' ' + commandOption.description); command.options[option.camelName] = option; // set default value if (typeof option.defValue != 'undefined') command.setOption(option.camelName, option.defValue, true); // add to suggestions command.suggestions.push('--' + option.long); return option; } function findVariants(obj, entry){ return obj.suggestions.filter(function(item){ return item.substr(0, entry.length) == entry; }); } function processArgs(command, args, suggest){ function processOption(option, command){ var params = []; if (option.maxArgsCount) { for (var j = 0; j < option.maxArgsCount; j++) { var suggestPoint = suggest && i + 1 + j >= args.length - 1; var nextToken = args[i + 1]; // TODO: suggestions for options if (suggestPoint) { // search for suggest noSuggestions = true; i = args.length; return; } if (!nextToken || nextToken[0] == '-') break; params.push(args[++i]); } if (params.length < option.minArgsCount) throw new SyntaxError('Option ' + token + ' should be used with at least ' + option.minArgsCount + ' argument(s)\nUsage: ' + option.usage); if (option.maxArgsCount == 1) params = params[0]; } else { params = !option.defValue; } //command.values[option.camelName] = newValue; resultToken.options.push({ option: option, value: params }); } var resultToken = { command: command, args: [], literalArgs: [], options: [] }; var result = [resultToken]; var suggestStartsWith = ''; var noSuggestions = false; var collectArgs = false; var commandArgs = []; var noOptionsYet = true; var option; commandsPath = [command.name]; for (var i = 0; i < args.length; i++) { var suggestPoint = suggest && i == args.length - 1; var token = args[i]; if (collectArgs) { commandArgs.push(token); continue; } if (suggestPoint && (token == '--' || token == '-' || token[0] != '-')) { suggestStartsWith = token; break; // returns long option & command list outside the loop } if (token == '--') { resultToken.args = commandArgs; commandArgs = []; noOptionsYet = false; collectArgs = true; continue; } if (token[0] == '-') { noOptionsYet = false; if (commandArgs.length) { //command.args_.apply(command, commandArgs); resultToken.args = commandArgs; commandArgs = []; } if (token[1] == '-') { // long option option = command.long[token.substr(2)]; if (!option) { // option doesn't exist if (suggestPoint) return findVariants(command, token); else throw new SyntaxError('Unknown option: ' + token); } // process option processOption(option, command); } else { // short flags sequence if (!/^-[a-zA-Z]+$/.test(token)) throw new SyntaxError('Wrong short option sequence: ' + token); if (token.length == 2) { option = command.short[token[1]]; if (!option) throw new SyntaxError('Unknown short option name: -' + token[1]); // single option processOption(option, command); } else { // short options sequence for (var j = 1; j < token.length; j++) { option = command.short[token[j]]; if (!option) throw new SyntaxError('Unknown short option name: -' + token[j]); if (option.maxArgsCount) throw new SyntaxError('Non-boolean option -' + token[j] + ' can\'t be used in short option sequence: ' + token); processOption(option, command); } } } } else { if (command.commands[token] && (!command.params || commandArgs.length >= command.params.minArgsCount)) { if (noOptionsYet) { resultToken.args = commandArgs; commandArgs = []; } if (command.params && resultToken.args.length < command.params.minArgsCount) throw new SyntaxError('Missed required argument(s) for command `' + command.name + '`'); // switch control to another command command = command.commands[token]; noOptionsYet = true; commandsPath.push(command.name); resultToken = { command: command, args: [], literalArgs: [], options: [] }; result.push(resultToken); } else { if (noOptionsYet && command.params && commandArgs.length < command.params.maxArgsCount) { commandArgs.push(token); continue; } if (suggestPoint) return findVariants(command, token); else throw new SyntaxError('Unknown command: ' + token); } } } if (suggest) { if (collectArgs || noSuggestions) return []; return findVariants(command, suggestStartsWith); } else { if (!noOptionsYet) resultToken.literalArgs = commandArgs; else resultToken.args = commandArgs; if (command.params && resultToken.args.length < command.params.minArgsCount) throw new SyntaxError('Missed required argument(s) for command `' + command.name + '`'); } return result; } function setFunctionFactory(name){ return function(fn){ var property = name + '_'; if (this[property] !== noop) throw new SyntaxError('Method `' + name + '` could be invoked only once'); if (typeof fn != 'function') throw new SyntaxError('Value for `' + name + '` method should be a function'); this[property] = fn; return this; } } /** * @class */ var Command = function(name, params){ this.name = name; this.params = false; try { if (params) this.params = parseParams(params); } catch(e) { throw new SyntaxError('Bad paramenter description in command definition: ' + this.name + ' ' + params); } this.commands = {}; this.options = {}; this.short = {}; this.long = {}; this.values = {}; this.defaults_ = {}; this.suggestions = []; this.option('-h, --help', 'Output usage information', function(){ this.showHelp(); process.exit(0); }, undefined); }; Command.prototype = { params: null, commands: null, options: null, short: null, long: null, values: null, defaults_: null, suggestions: null, description_: '', version_: '', initContext_: noop, init_: noop, delegate_: noop, action_: noop, args_: noop, end_: null, option: function(usage, description, opt_1, opt_2){ addOptionToCommand(this, createOption.apply(null, arguments)); return this; }, shortcut: function(usage, description, fn, opt_1, opt_2){ if (typeof fn != 'function') throw new SyntaxError('fn should be a function'); var command = this; var option = addOptionToCommand(this, createOption(usage, description, opt_1, opt_2)); var normalize = option.normalize; option.normalize = function(value){ var values; value = normalize.call(command, value); values = fn(value); for (var name in values) if (hasOwnProperty.call(values, name)) if (hasOwnProperty.call(command.options, name)) command.setOption(name, values[name]); else command.values[name] = values[name]; command.values[option.name] = value; return value; }; return this; }, hasOption: function(name){ return hasOwnProperty.call(this.options, name); }, hasOptions: function(){ return Object.keys(this.options).length > 0; }, setOption: function(name, value, isDefault){ if (!this.hasOption(name)) throw new SyntaxError('Option `' + name + '` is not defined'); var option = this.options[name]; var oldValue = this.values[name]; var newValue = option.normalize.call(this, value, oldValue); this.values[name] = option.maxArgsCount ? newValue : value; if (isDefault && !hasOwnProperty.call(this.defaults_, name)) this.defaults_[name] = this.values[name]; }, setOptions: function(values){ for (var name in values) if (hasOwnProperty.call(values, name) && this.hasOption(name)) this.setOption(name, values[name]); }, reset: function(){ this.values = {}; assign(this.values, this.defaults_); }, command: function(nameOrCommand, params){ var name; var command; if (nameOrCommand instanceof Command) { command = nameOrCommand; name = command.name; } else { name = nameOrCommand; if (!/^[a-zA-Z][a-zA-Z0-9\-\_]*$/.test(name)) throw new SyntaxError('Wrong command name: ' + name); } // search for existing one var subcommand = this.commands[name]; if (!subcommand) { // create new one if not exists subcommand = command || new Command(name, params); subcommand.end_ = this; this.commands[name] = subcommand; this.suggestions.push(name); } return subcommand; }, end: function() { return this.end_; }, hasCommands: function(){ return Object.keys(this.commands).length > 0; }, version: function(version, usage, description){ if (this.version_) throw new SyntaxError('Version for command could be set only once'); this.version_ = version; this.option( usage || '-v, --version', description || 'Output version', function(){ console.log(this.version_); process.exit(0); }, undefined ); return this; }, description: function(description){ if (this.description_) throw new SyntaxError('Description for command could be set only once'); this.description_ = description; return this; }, init: setFunctionFactory('init'), initContext: setFunctionFactory('initContext'), args: setFunctionFactory('args'), delegate: setFunctionFactory('delegate'), action: setFunctionFactory('action'), extend: function(fn){ fn.apply(null, [this].concat(Array.prototype.slice.call(arguments, 1))); return this; }, parse: function(args, suggest){ if (!args) args = process.argv.slice(2); if (!errorHandler) return processArgs(this, args, suggest); else try { return processArgs(this, args, suggest); } catch(e) { errorHandler(e.message || e); } }, run: function(args, context){ var commands = this.parse(args); if (!commands) return; var prevCommand; var context = assign({}, context || this.initContext_()); for (var i = 0; i < commands.length; i++) { var item = commands[i]; var command = item.command; // reset command values command.reset(); command.context = context; command.root = this; if (prevCommand) prevCommand.delegate_(command); // apply beforeInit options item.options.forEach(function(entry){ if (entry.option.beforeInit) command.setOption(entry.option.camelName, entry.value); }); command.init_(item.args.slice()); // use slice to avoid args mutation in handler if (item.args.length) command.args_(item.args.slice()); // use slice to avoid args mutation in handler // apply regular options item.options.forEach(function(entry){ if (!entry.option.beforeInit) command.setOption(entry.option.camelName, entry.value); }); prevCommand = command; } // return last command action result if (command) return command.action_(item.args, item.literalArgs); }, normalize: function(values){ var result = {}; if (!values) values = {}; for (var name in this.values) if (hasOwnProperty.call(this.values, name)) result[name] = hasOwnProperty.call(values, name) && hasOwnProperty.call(this.options, name) ? this.options[name].normalize.call(this, values[name]) : this.values[name]; for (var name in values) if (hasOwnProperty.call(values, name) && !hasOwnProperty.call(result, name)) result[name] = values[name]; return result; }, showHelp: function(){ console.log(showCommandHelp(this)); } }; // // help // /** * Return program help documentation. * * @return {String} * @api private */ function showCommandHelp(command){ function breakByLines(str, offset){ var words = str.split(' '); var maxWidth = MAX_LINE_WIDTH - offset || 0; var lines = []; var line = ''; while (words.length) { var word = words.shift(); if (!line || (line.length + word.length + 1) < maxWidth) { line += (line ? ' ' : '') + word; } else { lines.push(line); words.unshift(word); line = ''; } } lines.push(line); return lines.map(function(line, idx){ return (idx && offset ? pad(offset, '') : '') + line; }).join('\n'); } function args(command){ return command.params.args.map(function(arg){ return arg.required ? '<' + arg.name + '>' : '[' + arg.name + ']'; }).join(' '); } function commandsHelp(){ if (!command.hasCommands()) return ''; var maxNameLength = MIN_OFFSET - 2; var lines = Object.keys(command.commands).sort().map(function(name){ var subcommand = command.commands[name]; var line = { name: chalk.green(name) + chalk.gray( (subcommand.params ? ' ' + args(subcommand) : '') // (subcommand.hasOptions() ? ' [options]' : '') ), description: subcommand.description_ || '' }; maxNameLength = Math.max(maxNameLength, stringLength(line.name)); return line; }); return [ '', 'Commands:', '', lines.map(function(line){ return ' ' + pad(maxNameLength, line.name) + ' ' + breakByLines(line.description, maxNameLength + 4); }).join('\n'), '' ].join('\n'); } function optionsHelp(){ if (!command.hasOptions()) return ''; var hasShortOptions = Object.keys(command.short).length > 0; var maxNameLength = MIN_OFFSET - 2; var lines = Object.keys(command.long).sort().map(function(name){ var option = command.long[name]; var line = { name: option.usage .replace(/^(?:-., |)/, function(m){ return m || (hasShortOptions ? ' ' : ''); }) .replace(/(^|\s)(-[^\s,]+)/ig, function(m, p, flag){ return p + chalk.yellow(flag); }), description: option.description }; maxNameLength = Math.max(maxNameLength, stringLength(line.name)); return line; }); // Prepend the help information return [ '', 'Options:', '', lines.map(function(line){ return ' ' + pad(maxNameLength, line.name) + ' ' + breakByLines(line.description, maxNameLength + 4); }).join('\n'), '' ].join('\n'); } var output = []; var chalk = require('chalk'); chalk.enabled = module.exports.color && process.stdout.isTTY; if (command.description_) output.push(command.description_ + '\n'); output.push( 'Usage:\n\n ' + chalk.cyan(commandsPath ? commandsPath.join(' ') : command.name) + (command.params ? ' ' + chalk.magenta(args(command)) : '') + (command.hasOptions() ? ' [' + chalk.yellow('options') + ']' : '') + (command.hasCommands() ? ' [' + chalk.green('command') + ']' : ''), commandsHelp() + optionsHelp() ); return output.join('\n'); }; // // export // module.exports = { color: true, Error: SyntaxError, Argument: Argument, Command: Command, Option: Option, error: function(fn){ if (errorHandler) throw new SyntaxError('Error handler should be set only once'); if (typeof fn != 'function') throw new SyntaxError('Error handler should be a function'); errorHandler = fn; return this; }, create: function(name, params){ return new Command(name || require('path').basename(process.argv[1]) || 'cli', params); }, confirm: function(message, fn){ process.stdout.write(message); process.stdin.setEncoding('utf8'); process.stdin.once('data', function(val){ process.stdin.pause(); fn(/^y|yes|ok|true$/i.test(val.trim())); }); process.stdin.resume(); } };