coffee-crypto-cli
Advanced tools
Comparing version 1.2.1-canary-6117a1f.0 to 1.2.1-canary-6a61b55.0
{ | ||
"semi": true, | ||
"singleQuote": true, | ||
"arrowParens": "avoid", | ||
"bracketSpacing": true, | ||
"printWidth": 80, | ||
"bracketSpacing": true, | ||
"semi": false, | ||
"tabWidth": 2, | ||
"useTabs": false, | ||
"trailingComma": "none", | ||
"arrowParens": "avoid" | ||
"trailingComma": "none" | ||
} |
import { format, logSuccess } from '../utils.js'; | ||
export const priceStats = ({ name, current_price, total_volume, high_24h, low_24h, percent24h, athPrice, athPercent }, { price, priceChange, high, low, volume, ath, athChange }) => { | ||
const priceRes = `${name}: ${format(current_price)}`; | ||
let priceChangeRes; | ||
let volumeRes; | ||
let highRes; | ||
let lowRes; | ||
let athRes; | ||
let athChangeRes; | ||
if (priceChange) { | ||
priceChangeRes = `change (24H): ${percent24h.toFixed(2)}%`; | ||
export const priceStats = ({ results, flags }) => { | ||
for (const result of results) { | ||
const priceRes = `${result.name}: ${format(result.current_price)}`; | ||
let priceChangeRes; | ||
let volumeRes; | ||
let highRes; | ||
let lowRes; | ||
let athRes; | ||
let athChangeRes; | ||
if (flags.priceChange) { | ||
priceChangeRes = `change (24H): ${result.price_change_24h.toFixed(2)}%`; | ||
} | ||
if (flags.high) { | ||
highRes = `high (24H): ${format(result.high_24h)}`; | ||
} | ||
if (flags.low) { | ||
lowRes = `low (24H): ${format(result.low_24h)}`; | ||
} | ||
if (flags.volume) { | ||
volumeRes = `volume (24H): ${format(result.total_volume)}`; | ||
} | ||
if (flags.ath) { | ||
athRes = `ATH: ${format(result.ath)}`; | ||
} | ||
if (flags.athChange) { | ||
athChangeRes = `ATH (%): ${result.atl_change_percentage.toFixed(2)}%`; | ||
} | ||
logSuccess([ | ||
priceRes, | ||
priceChangeRes, | ||
volumeRes, | ||
highRes, | ||
lowRes, | ||
athRes, | ||
athChangeRes | ||
] | ||
.filter(Boolean) | ||
.join(' - ')); | ||
} | ||
if (high) { | ||
highRes = `high (24H): ${format(high_24h)}`; | ||
} | ||
if (low) { | ||
lowRes = `low (24H): ${format(low_24h)}`; | ||
} | ||
if (volume) { | ||
volumeRes = `volume (24H): ${format(total_volume)}`; | ||
} | ||
if (ath) { | ||
athRes = `ATH: ${format(athPrice)}`; | ||
} | ||
if (athChange) { | ||
athChangeRes = `ATH (%): ${athPercent.toFixed(2)}%`; | ||
} | ||
logSuccess([priceRes, priceChangeRes, volumeRes, highRes, lowRes, athRes, athChangeRes] | ||
.filter(Boolean) | ||
.join(' - ')); | ||
}; |
import fs from 'fs'; | ||
import { parseAsync } from 'json2csv'; | ||
import { logError, logSuccess } from '../utils.js'; | ||
import { formatFileName, logError, logSuccess } from '../utils.js'; | ||
import { CSVEXT, JSONEXT } from '../constants.js'; | ||
export const saveCoinData = async (options, exportData) => { | ||
export const saveCoinData = async ({ options, results }) => { | ||
if (!options) { | ||
return; | ||
} | ||
const fileExts = options.toLowerCase().split(','); | ||
@@ -10,2 +13,6 @@ if (!fileExts.some(fileExt => fileExt.toLowerCase() === JSONEXT || fileExt.toLowerCase() === CSVEXT)) { | ||
} | ||
const exportData = []; | ||
for (const result of results) { | ||
exportData.push(result); | ||
} | ||
logSuccess('Exporting coin data...'); | ||
@@ -33,7 +40,2 @@ if (fileExts.includes(JSONEXT)) { | ||
}; | ||
const formatFileName = (coinName, fileExt) => { | ||
/* use unix timestamp, resolves conflict of same filenames */ | ||
const timestamp = new Date().valueOf(); | ||
return `${coinName.toLowerCase()}-${timestamp}.${fileExt}`; | ||
}; | ||
const formatCsvFile = async (coin) => { | ||
@@ -70,3 +72,3 @@ return await parseAsync(coin, { | ||
label: 'All Time High', | ||
value: 'all_time_high' | ||
value: 'ath' | ||
}, | ||
@@ -73,0 +75,0 @@ { |
@@ -1,3 +0,2 @@ | ||
/** contstants */ | ||
export const JSONEXT = 'json'; | ||
export const CSVEXT = 'csv'; |
#!/usr/bin/env node | ||
import meow from 'meow'; | ||
import { app } from './app.js'; | ||
import CoinGeckoAPI from '@crypto-coffee/coingecko-api'; | ||
import { logError } from './utils.js'; | ||
import { saveCoinData } from './actions/saveCoinData.js'; | ||
import { priceStats } from './actions/priceStats.js'; | ||
const cli = meow(` | ||
Usage: | ||
$ crypto --price <coin name> <additional flags> | ||
$ crypto <coin ticker(s)> <additional flags> | ||
Options: | ||
--price, --p - coin name | ||
--priceChange, --pc - coin price change (%) in the past 24 hours | ||
--volume, --v - coin volume in the past 24 hours | ||
--high - highest price sold in the past 24 hours | ||
--low - lowest price sold in the past 24 hours | ||
--ath - coin all time high price | ||
--athChange, --athc - percent price change from ATH | ||
--version - current version of the crypto-cli tool | ||
--save - export all coin data to CSV and/or JSON | ||
--price-change, --pc Coin price change (%) in the past 24 hours | ||
--volume, --v Coin volume in the past 24 hours | ||
--ath-change, -athc Percent price change from the all time high | ||
--high, --h Highest price sold in the past 24 hours | ||
--low, --l Lowest price sold in the past 24 hours | ||
--ath Coin all time high price | ||
--save json,csv Save coin data via JSON and/or CSV | ||
--version Current version | ||
Examples: | ||
$crypto --price bitcoin --pc | ||
$crypto bitcoin --pc | ||
>> bitoin: $1337 - change (24H): 13.37% | ||
$crypto bitcoin,ethereum | ||
>> bitcoin: $1337 | ||
>> ethereum: $1337 | ||
Save coin data: | ||
$crypto --save json | ||
$crypto --save json,csv | ||
$crypto bitcoin --save json | ||
$crypto bitcoin --save json,csv | ||
`, { | ||
importMeta: import.meta, | ||
flags: { | ||
price: { | ||
type: 'string', | ||
isMultiple: true, | ||
alias: 'p' | ||
}, | ||
priceChange: { | ||
@@ -43,6 +44,8 @@ type: 'boolean', | ||
high: { | ||
type: 'boolean' | ||
type: 'boolean', | ||
alias: 'h' | ||
}, | ||
low: { | ||
type: 'boolean' | ||
type: 'boolean', | ||
alias: 'l' | ||
}, | ||
@@ -61,2 +64,28 @@ ath: { | ||
}); | ||
app(cli.input[0], cli.flags); | ||
const app = async () => { | ||
const { save } = cli.flags; | ||
const coinTickers = cli.input[0]; | ||
if (!coinTickers) { | ||
logError('No coin name provided. Check `crypto --help` for help'); | ||
} | ||
const gecko = new CoinGeckoAPI.default(); | ||
const results = await gecko.coinMarkets({ | ||
vs_currency: 'usd', | ||
ids: coinTickers | ||
}); | ||
if (!results.length) { | ||
logError(`Unknown coin: ${coinTickers}`); | ||
} | ||
priceStats({ | ||
results, | ||
flags: cli.flags | ||
}); | ||
await saveCoinData({ | ||
options: save, | ||
results | ||
}); | ||
process.exit(0); | ||
}; | ||
app().catch(error => { | ||
logError(`An error occured: ${error.message}\n Please report the issue here: https://github.com/Zidious/crypto-cli`); | ||
}); |
@@ -18,1 +18,6 @@ import chalk from 'chalk'; | ||
}; | ||
export const formatFileName = (coinName, fileExt) => { | ||
/* use unix timestamp, resolves conflict of same filenames */ | ||
const timestamp = new Date().valueOf(); | ||
return `${coinName.toLowerCase()}-${timestamp}.${fileExt}`; | ||
}; |
{ | ||
"name": "coffee-crypto-cli", | ||
"version": "1.2.1-canary-6117a1f.0", | ||
"version": "1.2.1-canary-6a61b55.0", | ||
"description": "Cryptocurrency CLI price tool", | ||
@@ -22,2 +22,3 @@ "main": "dist/index.js", | ||
"test": "mocha 'src/*.test.ts'", | ||
"lint": "eslint --fix", | ||
"prepare": "husky install", | ||
@@ -32,4 +33,4 @@ "precommit": "lint-staged" | ||
"@crypto-coffee/coingecko-api": "^1.2.0", | ||
"chalk": "5.2.0", | ||
"json2csv": "^5.0.7", | ||
"chalk": "^5.3.0", | ||
"json2csv": "^6.0.0-alpha.2", | ||
"meow": "^11.0.0" | ||
@@ -41,21 +42,22 @@ }, | ||
"@types/mocha": "^10.0.1", | ||
"@types/node": "^18.14.6", | ||
"@typescript-eslint/eslint-plugin": "^5.54.0", | ||
"@typescript-eslint/parser": "^5.54.0", | ||
"chai": "^4.3.7", | ||
"eslint": "^8.35.0", | ||
"execa": "^7.0.0", | ||
"@types/node": "^20.6.0", | ||
"@typescript-eslint/eslint-plugin": "^6.13.1", | ||
"@typescript-eslint/parser": "^6.13.1", | ||
"chai": "^4.3.8", | ||
"eslint": "^8.49.0", | ||
"execa": "^8.0.1", | ||
"husky": "^8.0.3", | ||
"lint-staged": "^13.1.2", | ||
"lint-staged": "^14.0.1", | ||
"mocha": "^10.2.0", | ||
"prettier": "^2.8.4", | ||
"prettier": "^3.0.3", | ||
"rimraf": "^4.3.0", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^4.9.5" | ||
"typescript": "^5.2.2" | ||
}, | ||
"lint-staged": { | ||
"*.{md,ts}": [ | ||
"*.ts": [ | ||
"eslint --fix", | ||
"prettier --write" | ||
] | ||
], | ||
"*.md": "prettier --write" | ||
}, | ||
@@ -62,0 +64,0 @@ "keywords": [ |
@@ -24,11 +24,7 @@ # crypto-cli | ||
```sh | ||
yarn add global coffee-crypto-cli | ||
``` | ||
## Usage | ||
```sh | ||
$ crypto --price bitcoin --volume --ath | ||
>> Bitcoin: $20,000 - volume: $13,337 - ATH: $68,000 | ||
$ crypto bitcoin | ||
>> Bitcoin: $20,000 | ||
``` | ||
@@ -38,66 +34,17 @@ | ||
Note: `--price, --p` is required for any of the subsequent flags. | ||
| Name | Description | | ||
| ------------------------ | ------------------------------------------- | | ||
| `--price-change`, `--pc` | Coin price change (%) in the past 24 hours | | ||
| `--volume`, `--v` | Coin volume in the past 24 hours | | ||
| `--ath-change`, `--athc` | Percent price change from the all time high | | ||
| `--high`, `--h` | Highest price sold in the past 24 hours | | ||
| `--low`, `--l` | Lowest price sold in the past 24 hours | | ||
| `--ath` | Coin all time high price | | ||
| `--save json,csv` | Save coin data via JSON and/or CSV | | ||
| `--help` | Flag description and usage examples | | ||
| `--version` | Current version | | ||
```sh | ||
--price, --p - coin name | ||
``` | ||
Coin price change (%) in the past 24 hours. | ||
```sh | ||
--priceChange, --pc | ||
``` | ||
Coin volume in the past 24 hours. | ||
```sh | ||
--volume, --v | ||
``` | ||
Highest price sold in the past 24 hours. | ||
```sh | ||
--high | ||
``` | ||
Lowest price sold in the past 24 hours. | ||
```sh | ||
--low | ||
``` | ||
Coin all time high price. | ||
```sh | ||
--ath | ||
``` | ||
Percent price change from the all time high. | ||
```sh | ||
--athChange, --athc | ||
``` | ||
Save coin data via JSON and/or CSV | ||
```sh | ||
--save json | ||
--save json,csv | ||
``` | ||
CLI help message. | ||
```sh | ||
--help | ||
``` | ||
Current version. | ||
```sh | ||
--version | ||
``` | ||
## Local Development | ||
First things first, we'll need to clone the repo, install the dependencies, and, build the project. | ||
Clone the repo, install the dependencies, and, build the project. | ||
@@ -109,9 +56,9 @@ ```sh | ||
```sh | ||
yarn install && yarn build | ||
yarn install --frozen-lockfile && yarn build | ||
``` | ||
To run the CLI locally, use the below command followed by the flag you want to run. | ||
To run the CLI locally: | ||
```sh | ||
node dist/index.js --price bitcoin | ||
node dist/index.js bitcoin | ||
``` | ||
@@ -118,0 +65,0 @@ |
@@ -1,67 +0,52 @@ | ||
import { format, logSuccess } from '../utils.js'; | ||
import type { Flags } from '../constants.js'; | ||
import { format, logSuccess } from '../utils.js' | ||
import type { PriceStatsParams } from '../types.js' | ||
interface PriceStatsParams { | ||
name: string; | ||
current_price: number; | ||
total_volume: number; | ||
high_24h: number; | ||
low_24h: number; | ||
percent24h: number; | ||
athPrice: number; | ||
athPercent: number; | ||
} | ||
export const priceStats = ({ results, flags }: PriceStatsParams): void => { | ||
for (const result of results) { | ||
const priceRes = `${result.name}: ${format(result.current_price)}` | ||
let priceChangeRes | ||
let volumeRes | ||
let highRes | ||
let lowRes | ||
let athRes | ||
let athChangeRes | ||
type PriceStatFlags = Omit<Flags, 'save'>; | ||
if (flags.priceChange) { | ||
priceChangeRes = `change (24H): ${result.price_change_24h.toFixed(2)}%` | ||
} | ||
export const priceStats = ( | ||
{ | ||
name, | ||
current_price, | ||
total_volume, | ||
high_24h, | ||
low_24h, | ||
percent24h, | ||
athPrice, | ||
athPercent | ||
}: PriceStatsParams, | ||
{ price, priceChange, high, low, volume, ath, athChange }: PriceStatFlags | ||
): void => { | ||
const priceRes = `${name}: ${format(current_price)}`; | ||
let priceChangeRes; | ||
let volumeRes; | ||
let highRes; | ||
let lowRes; | ||
let athRes; | ||
let athChangeRes; | ||
if (flags.high) { | ||
highRes = `high (24H): ${format(result.high_24h)}` | ||
} | ||
if (priceChange) { | ||
priceChangeRes = `change (24H): ${percent24h.toFixed(2)}%`; | ||
} | ||
if (flags.low) { | ||
lowRes = `low (24H): ${format(result.low_24h)}` | ||
} | ||
if (high) { | ||
highRes = `high (24H): ${format(high_24h)}`; | ||
} | ||
if (flags.volume) { | ||
volumeRes = `volume (24H): ${format(result.total_volume)}` | ||
} | ||
if (low) { | ||
lowRes = `low (24H): ${format(low_24h)}`; | ||
} | ||
if (flags.ath) { | ||
athRes = `ATH: ${format(result.ath)}` | ||
} | ||
if (volume) { | ||
volumeRes = `volume (24H): ${format(total_volume)}`; | ||
} | ||
if (flags.athChange) { | ||
athChangeRes = `ATH (%): ${result.atl_change_percentage.toFixed(2)}%` | ||
} | ||
if (ath) { | ||
athRes = `ATH: ${format(athPrice)}`; | ||
logSuccess( | ||
[ | ||
priceRes, | ||
priceChangeRes, | ||
volumeRes, | ||
highRes, | ||
lowRes, | ||
athRes, | ||
athChangeRes | ||
] | ||
.filter(Boolean) | ||
.join(' - ') | ||
) | ||
} | ||
if (athChange) { | ||
athChangeRes = `ATH (%): ${athPercent.toFixed(2)}%`; | ||
} | ||
logSuccess( | ||
[priceRes, priceChangeRes, volumeRes, highRes, lowRes, athRes, athChangeRes] | ||
.filter(Boolean) | ||
.join(' - ') | ||
); | ||
}; | ||
} |
@@ -1,13 +0,18 @@ | ||
import fs from 'fs'; | ||
import { parseAsync } from 'json2csv'; | ||
import { logError, logSuccess } from '../utils.js'; | ||
import { CSVEXT, JSONEXT } from '../constants.js'; | ||
import type { ExportData } from '../constants.js'; | ||
import fs from 'fs' | ||
import { parseAsync } from 'json2csv' | ||
import { formatFileName, logError, logSuccess } from '../utils.js' | ||
import { CSVEXT, JSONEXT } from '../constants.js' | ||
import { CoinMarkets } from '@crypto-coffee/coingecko-api/dist/types.js' | ||
import type { SaveCoinDataParams } from '../types.js' | ||
export const saveCoinData = async ( | ||
options: string, | ||
exportData: ExportData[] | ||
) => { | ||
const fileExts = options.toLowerCase().split(','); | ||
export const saveCoinData = async ({ | ||
options, | ||
results | ||
}: SaveCoinDataParams) => { | ||
if (!options) { | ||
return | ||
} | ||
const fileExts = options.toLowerCase().split(',') | ||
if ( | ||
@@ -21,25 +26,35 @@ !fileExts.some( | ||
'Unable to export, unsupported file extension.\nPlease Check `crypto --help` for help' | ||
); | ||
) | ||
} | ||
logSuccess('Exporting coin data...'); | ||
const exportData: Partial<CoinMarkets>[] = [] | ||
for (const result of results) { | ||
exportData.push(result) | ||
} | ||
logSuccess('Exporting coin data...') | ||
if (fileExts.includes(JSONEXT)) { | ||
writeFile(exportData, JSONEXT); | ||
writeFile(exportData, JSONEXT) | ||
} | ||
if (fileExts.includes(CSVEXT)) { | ||
await writeFile(exportData, CSVEXT); | ||
await writeFile(exportData, CSVEXT) | ||
} | ||
logSuccess('Export complete.'); | ||
}; | ||
logSuccess('Export complete.') | ||
} | ||
const writeFile = async (exportData: ExportData[], fileExt: string) => { | ||
const writeFile = async ( | ||
exportData: Partial<CoinMarkets>[], | ||
fileExt: string | ||
) => { | ||
for (const coin of exportData) { | ||
const data = | ||
fileExt === JSONEXT ? JSON.stringify(coin) : await formatCsvFile(coin); | ||
fileExt === JSONEXT ? JSON.stringify(coin) : await formatCsvFile(coin) | ||
try { | ||
fs.writeFileSync(formatFileName(coin.name as string, fileExt), data, { | ||
encoding: 'utf8' | ||
}); | ||
}) | ||
} catch (error) { | ||
@@ -50,16 +65,9 @@ logError( | ||
}` | ||
); | ||
) | ||
} | ||
} | ||
}; | ||
} | ||
const formatFileName = (coinName: string, fileExt: string): string => { | ||
/* use unix timestamp, resolves conflict of same filenames */ | ||
const timestamp = new Date().valueOf(); | ||
return `${coinName.toLowerCase()}-${timestamp}.${fileExt}`; | ||
}; | ||
const formatCsvFile = async (coin: ExportData): Promise<string> => { | ||
return await parseAsync(coin as Readonly<ExportData>, { | ||
const formatCsvFile = async (coin: Partial<CoinMarkets>): Promise<string> => { | ||
return await parseAsync(coin, { | ||
delimiter: ',', | ||
@@ -94,3 +102,3 @@ excelStrings: false, | ||
label: 'All Time High', | ||
value: 'all_time_high' | ||
value: 'ath' | ||
}, | ||
@@ -102,3 +110,3 @@ { | ||
] | ||
}); | ||
}; | ||
}) | ||
} |
@@ -1,17 +0,2 @@ | ||
/** contstants */ | ||
export const JSONEXT = 'json'; | ||
export const CSVEXT = 'csv'; | ||
/** types */ | ||
export type ExportData = Record<string, string | number>; | ||
export interface Flags { | ||
price: string[]; | ||
priceChange: boolean; | ||
volume: boolean; | ||
high: boolean; | ||
low: boolean; | ||
ath: boolean; | ||
athChange: boolean; | ||
save: string; | ||
} | ||
export const JSONEXT = 'json' | ||
export const CSVEXT = 'csv' |
@@ -1,29 +0,30 @@ | ||
import 'mocha'; | ||
import { assert } from 'chai'; | ||
import { execa, ExecaError, ExecaReturnValue } from 'execa'; | ||
import path from 'path'; | ||
import { fileURLToPath } from 'url'; | ||
import 'mocha' | ||
import { assert } from 'chai' | ||
import { execa, ExecaError, ExecaReturnValue } from 'execa' | ||
import path from 'path' | ||
import { fileURLToPath } from 'url' | ||
const __filename = fileURLToPath(import.meta.url); | ||
const CLI = path.resolve(path.dirname(__filename), '..', 'dist', 'index.js'); | ||
const __filename = fileURLToPath(import.meta.url) | ||
const CLI = path.resolve(path.dirname(__filename), '..', 'dist', 'index.js') | ||
describe('crypto-cli', () => { | ||
// TODO: Skipping tests, we're getting rate limited: https://github.com/Zidious/crypto-cli/issues/17 | ||
describe.skip('crypto-cli', () => { | ||
describe('no flags provided', () => { | ||
it('returns error', async () => { | ||
let err: ExecaError | null = null; | ||
let err: ExecaError | null = null | ||
try { | ||
await execa(CLI, []); | ||
await execa(CLI, []) | ||
} catch (error) { | ||
err = error as ExecaError; | ||
err = error as ExecaError | ||
} | ||
assert.isNotNull(err); | ||
assert.isNotNull(err) | ||
assert.equal( | ||
err?.stderr, | ||
'No coin name provided. Check `crypto --help` for help' | ||
); | ||
assert.equal(err?.exitCode, 1); | ||
}); | ||
}); | ||
) | ||
assert.equal(err?.exitCode, 1) | ||
}) | ||
}) | ||
@@ -34,26 +35,26 @@ describe('flags', () => { | ||
it('returns the price', async () => { | ||
const results = await execa(CLI, ['--price', 'bitcoin']); | ||
const results = await execa(CLI, ['--price', 'bitcoin']) | ||
/* price will vary this output is always returned if successful */ | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
describe('with a invalid coin name', () => { | ||
it('returns the price', async () => { | ||
let err: ExecaError | null = null; | ||
let err: ExecaError | null = null | ||
try { | ||
await execa(CLI, ['--price', 'abcd']); | ||
await execa(CLI, ['--price', 'abcd']) | ||
} catch (error) { | ||
err = error as ExecaError; | ||
err = error as ExecaError | ||
} | ||
assert.isNotNull(err); | ||
assert.equal(err?.stderr, 'Unknown coin: abcd'); | ||
assert.equal(err?.exitCode, 1); | ||
}); | ||
}); | ||
}); | ||
assert.isNotNull(err) | ||
assert.equal(err?.stderr, 'Unknown coin: abcd') | ||
assert.equal(err?.exitCode, 1) | ||
}) | ||
}) | ||
}) | ||
@@ -68,14 +69,14 @@ describe('with multiple `--price` flags', () => { | ||
'ethereum' | ||
]); | ||
]) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'Ethereum: $'); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'Ethereum: $') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
describe('with valid and invalid coin names', () => { | ||
it('returns the price', async () => { | ||
let err: ExecaError | null = null; | ||
let results: ExecaReturnValue<string> | null = null; | ||
let err: ExecaError | null = null | ||
let results: ExecaReturnValue<string> | null = null | ||
try { | ||
@@ -88,16 +89,16 @@ /* we only return valid coin names so it should ignore invalid coin names */ | ||
'bitcoin' | ||
]); | ||
]) | ||
} catch (error) { | ||
err = error as ExecaError; | ||
err = error as ExecaError | ||
} | ||
assert.isNull(err); | ||
assert.include(results?.stdout, 'Bitcoin: $'); | ||
assert.notInclude(results?.stdout, 'abcd: $'); | ||
assert.equal(results?.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
assert.isNull(err) | ||
assert.include(results?.stdout, 'Bitcoin: $') | ||
assert.notInclude(results?.stdout, 'abcd: $') | ||
assert.equal(results?.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
describe('with `--priceChange, --pc`', () => { | ||
describe('with `--price-change, --pc`', () => { | ||
describe('with a valid coin name', () => { | ||
@@ -109,10 +110,10 @@ it('returns the price change percentage', async () => { | ||
'--priceChange' | ||
]); | ||
]) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'change (24H): '); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'change (24H): ') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
@@ -122,10 +123,10 @@ describe('with `--high`', () => { | ||
it('returns the highest price sold in the previous 24H', async () => { | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--high']); | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--high']) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'high (24H): '); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'high (24H): ') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
@@ -135,10 +136,10 @@ describe('with `--low`', () => { | ||
it('returns the lowest price sold in the previous 24H', async () => { | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--low']); | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--low']) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'low (24H): '); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'low (24H): ') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
@@ -148,10 +149,10 @@ describe('with `--volume`', () => { | ||
it('returns the volume for a given coin', async () => { | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--volume']); | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--volume']) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'volume (24H): '); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'volume (24H): ') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
@@ -161,12 +162,12 @@ describe('with `--ath`', () => { | ||
it('returns the all time high price for a given coin', async () => { | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--ath']); | ||
const results = await execa(CLI, ['--price', 'bitcoin', '--ath']) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'ATH: $'); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'ATH: $') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
describe('with `--athChange`', () => { | ||
describe('with `--ath-change`', () => { | ||
describe('with a valid coin name', () => { | ||
@@ -178,10 +179,10 @@ it('returns the percentage from the all time high price', async () => { | ||
'--athChange' | ||
]); | ||
]) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'ATH (%):'); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'ATH (%):') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
@@ -196,10 +197,10 @@ describe('with `--save`', () => { | ||
'csv' | ||
]); | ||
]) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'Exporting coin data...'); | ||
assert.include(results.stdout, 'Export complete.'); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'Exporting coin data...') | ||
assert.include(results.stdout, 'Export complete.') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
@@ -213,12 +214,12 @@ describe('give format json', () => { | ||
'json' | ||
]); | ||
]) | ||
assert.include(results.stdout, 'Bitcoin: $'); | ||
assert.include(results.stdout, 'Exporting coin data...'); | ||
assert.include(results.stdout, 'Export complete.'); | ||
assert.equal(results.exitCode, 0); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
assert.include(results.stdout, 'Bitcoin: $') | ||
assert.include(results.stdout, 'Exporting coin data...') | ||
assert.include(results.stdout, 'Export complete.') | ||
assert.equal(results.exitCode, 0) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
#!/usr/bin/env node | ||
import meow from 'meow'; | ||
import { app } from './app.js'; | ||
import meow from 'meow' | ||
import CoinGeckoAPI from '@crypto-coffee/coingecko-api' | ||
import { logError } from './utils.js' | ||
import { saveCoinData } from './actions/saveCoinData.js' | ||
import type { Flags } from './types.js' | ||
import { priceStats } from './actions/priceStats.js' | ||
@@ -9,22 +13,25 @@ const cli = meow( | ||
Usage: | ||
$ crypto --price <coin name> <additional flags> | ||
$ crypto <coin ticker(s)> <additional flags> | ||
Options: | ||
--price, --p - coin name | ||
--priceChange, --pc - coin price change (%) in the past 24 hours | ||
--volume, --v - coin volume in the past 24 hours | ||
--high - highest price sold in the past 24 hours | ||
--low - lowest price sold in the past 24 hours | ||
--ath - coin all time high price | ||
--athChange, --athc - percent price change from ATH | ||
--version - current version of the crypto-cli tool | ||
--save - export all coin data to CSV and/or JSON | ||
--price-change, --pc Coin price change (%) in the past 24 hours | ||
--volume, --v Coin volume in the past 24 hours | ||
--ath-change, -athc Percent price change from the all time high | ||
--high, --h Highest price sold in the past 24 hours | ||
--low, --l Lowest price sold in the past 24 hours | ||
--ath Coin all time high price | ||
--save json,csv Save coin data via JSON and/or CSV | ||
--version Current version | ||
Examples: | ||
$crypto --price bitcoin --pc | ||
$crypto bitcoin --pc | ||
>> bitoin: $1337 - change (24H): 13.37% | ||
$crypto bitcoin,ethereum | ||
>> bitcoin: $1337 | ||
>> ethereum: $1337 | ||
Save coin data: | ||
$crypto --save json | ||
$crypto --save json,csv | ||
$crypto bitcoin --save json | ||
$crypto bitcoin --save json,csv | ||
`, | ||
@@ -34,7 +41,2 @@ { | ||
flags: { | ||
price: { | ||
type: 'string', | ||
isMultiple: true, | ||
alias: 'p' | ||
}, | ||
priceChange: { | ||
@@ -49,6 +51,8 @@ type: 'boolean', | ||
high: { | ||
type: 'boolean' | ||
type: 'boolean', | ||
alias: 'h' | ||
}, | ||
low: { | ||
type: 'boolean' | ||
type: 'boolean', | ||
alias: 'l' | ||
}, | ||
@@ -67,4 +71,42 @@ ath: { | ||
} | ||
); | ||
) | ||
app(cli.input[0], cli.flags); | ||
const app = async () => { | ||
const { save } = cli.flags as Flags | ||
const coinTickers = cli.input[0] | ||
if (!coinTickers) { | ||
logError('No coin name provided. Check `crypto --help` for help') | ||
} | ||
const gecko = new CoinGeckoAPI.default() | ||
const results = await gecko.coinMarkets({ | ||
vs_currency: 'usd', | ||
ids: coinTickers | ||
}) | ||
if (!results.length) { | ||
logError(`Unknown coin: ${coinTickers}`) | ||
} | ||
priceStats({ | ||
results, | ||
flags: cli.flags as Flags | ||
}) | ||
await saveCoinData({ | ||
options: save, | ||
results | ||
}) | ||
process.exit(0) | ||
} | ||
app().catch(error => { | ||
logError( | ||
`An error occured: ${ | ||
(error as Error).message | ||
}\n Please report the issue here: https://github.com/Zidious/crypto-cli` | ||
) | ||
}) |
@@ -1,5 +0,5 @@ | ||
import chalk from 'chalk'; | ||
import chalk from 'chalk' | ||
const ERROR = chalk.bold.red; | ||
const SUCCESS = chalk.bold.green; | ||
const ERROR = chalk.bold.red | ||
const SUCCESS = chalk.bold.green | ||
@@ -9,15 +9,22 @@ const formatter = new Intl.NumberFormat('en-US', { | ||
currency: 'USD' | ||
}); | ||
}) | ||
export const logError = (message: string) => { | ||
console.error(ERROR(message)); | ||
process.exit(1); | ||
}; | ||
console.error(ERROR(message)) | ||
process.exit(1) | ||
} | ||
export const logSuccess = (message: string) => { | ||
console.log(SUCCESS(message)); | ||
}; | ||
console.log(SUCCESS(message)) | ||
} | ||
export const format = (price: number) => { | ||
return formatter.format(price); | ||
}; | ||
return formatter.format(price) | ||
} | ||
export const formatFileName = (coinName: string, fileExt: string): string => { | ||
/* use unix timestamp, resolves conflict of same filenames */ | ||
const timestamp = new Date().valueOf() | ||
return `${coinName.toLowerCase()}-${timestamp}.${fileExt}` | ||
} |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
30148
730
70
1
+ Added@streamparser/json@0.0.6(transitive)
+ Addedchalk@5.3.0(transitive)
+ Addedjson2csv@6.0.0-alpha.2(transitive)
- Removedchalk@5.2.0(transitive)
- Removedjson2csv@5.0.7(transitive)
- Removedjsonparse@1.3.1(transitive)
Updatedchalk@^5.3.0
Updatedjson2csv@^6.0.0-alpha.2