node.js cli (書摘: https://medium.freecodecamp.org/how-to-create-a-real-world-node-cli-app-with-node-391b727bbed3)
- 產生簡單 node.js binary
- 產生 node.js 專案
$npm init -y
- 簡單的
index.js
module.exports = () => { console.log("Hello World!"); }
- 簡單的 bin 程式
/bin/outside
#!/usr/bin/env node require('../')()
- 將程式加入可執行
$chmod +x ./bin/outside
- 修改
package.json
{ //... "bin": { "outside": "bin/outside" }, //... }
- 產生可執行程式
$npm link
- 執行
$ outside Hello World
- 產生 node.js 專案
- 加入 command line 參數支援
- 加入 minimist 套件
$npm install --save minimist
- 修改
index.js
const minimist = require('minimist'); module.exports = () => { const args = minimist(process.argv.slice(2)); const cmd = args._[0] || 'help'; switch (cmd) { case "today": require('./cmd/today')(args); break; default: console.log(`${cmd} is not a valid command!`); break; } }
- 加入
cmd/today.js
module.exports = (args) => { console.log("Today is sunny"); }
- 執行
$outside today Today is sunny $outside help help is not a valid command!
- 加入 minimist 套件
- 加入 version 跟 help
- 修改
index.js
const minimist = require('minimist'); module.exports = () => { const args = minimist(process.argv.slice(2)); const cmd = args._[0] || 'help'; switch (cmd) { case "today": require('./cmd/today')(args); break; case "help": require('./cmd/help')(args); break; case "version": require('./cmd/version')(args); break; default: console.log(`${cmd} is not a valid command!`); break; } }
- 加入
cmd/help
const menus = { main: ` outside [command] <options> today .............. show weather for today version ............ show package version help ............... show help menu for a command`, today: ` outside today <options> --location, -l ..... the location to use` } module.exports = (args) => { const subCmd = args._[0] === 'help' ? args._[1] : args._[0] console.log(menus[subCmd] || menus.main) }
- 加入
cmd/version
const { version } = require('../package.json'); module.exports = (args) => { console.log(`v${version}`); }
- 執行
$ outside outside [command] <options> today .............. show weather for today version ............ show package version help ............... show help menu for a command $ outside help today outside today <options> --location, -l ..... the location to use $ outside version v1.1.0
- 修改
- 這樣架構可以彈性增加功能,例如加入 10-days forecast
- 修改
index.js
const minimist = require('minimist'); module.exports = () => { const args = minimist(process.argv.slice(2)); const cmd = args._[0] || 'help'; switch (cmd) { case "today": require('./cmd/today')(args); break; case "help": require('./cmd/help')(args); break; case "version": require('./cmd/version')(args); break; case "forecast": require('./cmd/forecast')(args); break; default: console.log(`${cmd} is not a valid command!`); break; } }
- 加入
cmd/forecast
module.exports = (args) => { console.log("This is forecast"); }
- 修改
cmd/help
const menus = { main: ` outside [command] <options> today .............. show weather for today forecast ........... show 10-day weather forecast version ............ show package version help ............... show help menu for a command`, today: ` outside today <options> --location, -l ..... the location to use`, forecast: ` outside forecast <options> --location, -l ... the location to use `, } module.exports = (args) => { const subCmd = args._[0] === 'help' ? args._[1] : args._[0] console.log(menus[subCmd] || menus.main) }
- 執行
$ outside help forecast outside forecast <options> --location, -l ... the location to use
- 修改
- 實際加入氣象預測,使用 yahoo api
- 加入 axios 跟 ora 套件
$npm install --save axios ora
- 加入
utils/weather.js
const axios = require('axios'); module.exports = async (location) => { const results = await axios({ method: 'get', url: 'https://query.yahooapis.com/v1/public/yql', params: { format: 'json', q: `select item from weather.forecast where woeid in (select woeid from geo.places(1) where text="${location}")`, }, }); return results.data.query.results.channel.item; }
- 修改
cmd/today.js
const ora = require('ora'); const getWeather = require('../utils/weather'); module.exports = async (args) => { const spinner = ora().start(); try { const location = args.location || args.l ; const weather = await getWeather(location); spinner.stop(); console.log(`current location is ${location}`); console.log(`\t ${weather.condition.temp} ${weather.condition.text}`); } catch (err) { spinner.stop(); console.log(err); } }
- 執行
$outside today --location "Taipei" current location is Taipei 91 Cloudy
- 照樣修改
cmd/forecast.js
const ora = require('ora'); const getWeather = require('../utils/weather'); module.exports = async (args) => { const spinner = ora().start(); try { const location = args.location || args.l; const weather = await getWeather(location); spinner.stop(); console.log(`Forecast for ${location}`); weather.forecast.forEach(item => console.log(`\t ${item.date} - Low ${item.low} |High ${item.high} | ${item.text}`)); } catch (err) { spinner.stop(); console.log(err); } }
- 執行
$outside forecast --location "Taipei" Forecast for Taipei 03 Jul 2018 - Low 83 |High 95 | Cloudy 04 Jul 2018 - Low 83 |High 96 | Scattered Thunderstorms 05 Jul 2018 - Low 81 |High 97 | Thunderstorms 06 Jul 2018 - Low 82 |High 96 | Thunderstorms 07 Jul 2018 - Low 82 |High 95 | Thunderstorms 08 Jul 2018 - Low 82 |High 94 | Thunderstorms 09 Jul 2018 - Low 81 |High 94 | Thunderstorms 10 Jul 2018 - Low 81 |High 93 | Thunderstorms 11 Jul 2018 - Low 81 |High 93 | Thunderstorms 12 Jul 2018 - Low 82 |High 94 | Thunderstorms
- 加入 axios 跟 ora 套件
- 加入預設區域
- 使用 ~ipdata.io~,但須申請 API key
- 加入
utils/location
const axios = require('axios'); module.exports = async () => { const results = await axios({ method: 'get', url: 'https://api.ipdata.co?api-key=API_KEY' }); const { city, region } = results.data; return `${city}, ${region}`; }
- 修改
cmd/today.js
const ora = require('ora'); const getWeather = require('../utils/weather'); const getLocation = require('../utils/location'); module.exports = async (args) => { const spinner = ora().start(); try { const location = args.location || args.l || await getLocation(); const weather = await getWeather(location); spinner.stop(); console.log(`current location is ${location}`); console.log(`\t ${weather.condition.temp} ${weather.condition.text}`); } catch (err) { spinner.stop(); console.log(err); } }
- 修改
cmd/forecast.js
const ora = require('ora'); const getWeather = require('../utils/weather'); const getLocation = require('../utils/location'); module.exports = async (args) => { const spinner = ora().start(); try { const location = args.location || args.l || await getLocation(); const weather = await getWeather(location); spinner.stop(); console.log(`Forecast for ${location}`); weather.forecast.forEach(item => console.log(`\t ${item.date} - Low ${item.low} |High ${item.high} | ${item.text}`)); } catch (err) { spinner.stop(); console.log(err); } }
- 執行
$ outside outside [command] <options> today .............. show weather for today forecast ........... show 10-day weather forecast version ............ show package version help ............... show help menu for a command $ outside today current location is Hsinchu, Hsinchu 89 Breezy $ outside forecast Forecast for Hsinchu, Hsinchu 03 Jul 2018 - Low 84 |High 92 | Breezy 04 Jul 2018 - Low 84 |High 93 | Breezy 05 Jul 2018 - Low 83 |High 95 | Breezy 06 Jul 2018 - Low 82 |High 94 | Breezy 07 Jul 2018 - Low 81 |High 94 | Breezy 08 Jul 2018 - Low 81 |High 93 | Mostly Cloudy 09 Jul 2018 - Low 81 |High 93 | Thunderstorms 10 Jul 2018 - Low 79 |High 92 | Thunderstorms 11 Jul 2018 - Low 80 |High 91 | Thunderstorms 12 Jul 2018 - Low 81 |High 92 | Thunderstorms