Skip to content

Feature/bundlephobia support #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Nov 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,28 @@ USAGE
<!-- usagestop -->
# Commands
<!-- commands -->
* [`cdt bundlephobia [FILE]`](#cdt-bundlephobia-file)
* [`cdt crypto [STRING]`](#cdt-crypto-string)
* [`cdt hash [STRING]`](#cdt-hash-string)
* [`cdt help [COMMAND]`](#cdt-help-command)
* [`cdt minify [FILE]`](#cdt-minify-file)

## `cdt bundlephobia [FILE]`

describe the command here

```
USAGE
$ cdt bundlephobia [FILE]

OPTIONS
-f, --force
-h, --help show CLI help
-n, --name=name name to print
```

_See code: [src/commands/bundlephobia.ts](https://github.com/codingtools/cdt/blob/v0.1.2/src/commands/bundlephobia.ts)_

## `cdt crypto [STRING]`

Encryption and Decryption functionality for File/String
Expand Down
17 changes: 17 additions & 0 deletions error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
data: {
assets: [ [Object] ],
dependencyCount: 3,
dependencySizes: [ [Object] ],
description: 'React is a JavaScript library for building user interfaces.',
gzip: 2630,
hasJSModule: false,
hasJSNext: false,
hasSideEffects: true,
name: 'react',
repository: 'https://github.com/facebook/react.git',
scoped: false,
size: 6499,
version: '16.11.0'
}
}
43 changes: 37 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"@oclif/plugin-help": "^2.2.1",
"@types/crypto-js": "^3.1.43",
"@types/signale": "^1.2.1",
"axios": "^0.19.0",
"chalk": "^2.4.2",
"crypto-js": "^3.1.9-1",
"jshashes": "^1.0.7",
"minify": "^4.1.3",
Expand Down
123 changes: 123 additions & 0 deletions src/commands/bundlephobia.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {Command, flags} from '@oclif/command'
import axios from 'axios'
import chalk from 'chalk'

import Logger from '../utilities/logger'

// TODO:
// ADD package.json support
// ADD VALID tests ( for now they just ignoring )
export default class Bundlephobia extends Command {
static description = 'Find cost of adding a npm/yarn package'

static flags = {
help: flags.help({char: 'h'}),
packages: flags.string({
char: 'p',
description: 'packages for which cost is required, pass more than one separated by space',
multiple: true // can get multiple package names
}),
}

static args = [{name: 'package'}] // only one can be passed club which one passed through flag and arg

private static getPackages(flags: any, args: any) {
let packages = []

if (args.package)
packages.push(args.package)
if (flags.packages)
packages = packages.concat(flags.packages) // not inplace operation
return packages
}

private static getErrorMessage(pkg: string, message: string) {
// replacing will be useful when we do not have specific version
// output will be like below
/*
⚠ @codingtools/[email protected] This package has not been published with this particular version.
Valid versions - `<code>latest</code>`, `<code>0.1.1</code>` and `<code>0.1.2</code>`
*/
if (message.includes('This package has not been published with this particular version.'))
message = message.replace(/`<code>|<\/code>`/g, '')

return `${chalk.red(pkg)} ${message}`
}

private static getSize(byteSize: number) {
if (byteSize >= 1024 * 1024)
return `${chalk.red((byteSize / (1024 * 1024)).toFixed(1) + 'MB')}`
else if (byteSize >= 1024)
return `${chalk.blue((byteSize / (1024)).toFixed(1) + 'KB')}`
else //if (byteSize < 1024)
return `${chalk.green(byteSize.toFixed(1) + 'B')}`
}

// values needed package
async run() {
const {args, flags} = this.parse(Bundlephobia)

args.packages = Bundlephobia.getPackages(flags, args) // get a list

this.checkParameters(flags, args)
this.bundlePhobia(flags, args)
}

// tslint:disable-next-line:no-unused
private checkParameters(flags: unknown, args: any) {
if (args.packages.length === 0)
Logger.error(this, 'At least one package must be passed')
}

// tslint:disable-next-line:no-unused
private bundlePhobia(flags: any, args: any) {
Logger.progressStart(this, 'finding size...')

let size = 0
let gzip = 0
let dependencyCount = 0
let packagesResolved = 0

let packagesInfo: any[] = args.packages.map(
(pkg: string) => {
return {
url: `https://bundlephobia.com/api/size?package=${pkg}`,
pkg
}
}
)

// tslint:disable-next-line:no-unsafe-any no-unused
let x = axios.all(packagesInfo.map((packageInfo: any) => { // have to use x for removing TSLintError: promises must be handled appropriately
return axios.get(packageInfo.url).then(successResponse => {
packagesResolved ++
size += successResponse.data.size
gzip += successResponse.data.gzip
dependencyCount += successResponse.data.dependencyCount
Logger.progressStop(this, this.getSuccessMessage(successResponse.data))
}).catch(errorResponse => {
Logger.progressStopError(this, Bundlephobia.getErrorMessage(packageInfo.pkg, errorResponse.response.data.error.message))
})
// tslint:disable-next-line:no-unused
}))
.then(() => {}).catch(() => {})
. finally(() => {
Logger.success(this, this.getFinalMessage({
count: packagesResolved,
dependencyCount,
size,
gzip
}))
})

}

private getFinalMessage(data: any) {
return `${chalk.magenta('Total')} [${chalk.cyan(data.count + ' packages resolved')}] has ${data.dependencyCount} dependencies with size of ${Bundlephobia.getSize(data.size)}(${Bundlephobia.getSize(data.gzip)} gzipped)`
}

private getSuccessMessage(data: any) {
return `${chalk.magenta(data.name)}@${chalk.cyan(data.version)} has ${data.dependencyCount} dependencies with size of ${Bundlephobia.getSize(data.size)}(${Bundlephobia.getSize(data.gzip)} gzipped)`
}

}
2 changes: 1 addition & 1 deletion src/commands/minify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class Minify extends Command {

static flags = {
help: flags.help({char: 'h'}),
type: flags.string({char: 't' , description: 'type of file to be minified, it will try to find type with extension'}),
type: flags.string({char: 't' , description: 'type of file to be minified, it will try to find type with extension supported: JS, HTML/HTM, CSS'}),
file: flags.string({char: 'f' , description: 'file to be minified'}),
}

Expand Down
12 changes: 11 additions & 1 deletion src/utilities/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ export default class Logger {
signale.error(`${message}`)
thisRef.exit(0) //added to exit command
}

// tslint:disable-next-line:no-unused
public static warn(thisRef: any, message: any) {
signale.warn(`${message}`)
}

// tslint:disable-next-line:no-unused
public static progressStart(thisRef: any, message: string) {
// signale.watch(`${message}`)
Expand All @@ -31,6 +37,11 @@ export default class Logger {
Logger.spinner.succeed(message)
}

// tslint:disable-next-line:no-unused
public static progressStopError(thisRef: any, message: string) {
Logger.spinner.warn(message)
}

// public static logSuccess(thisRef: any, message: string) {
// thisRef.log(` › Success: ${message}`)
// }
Expand All @@ -40,5 +51,4 @@ export default class Logger {
// public static logError(thisRef: any, message: string) {
// thisRef.error(`${message}`)
// }

}
31 changes: 31 additions & 0 deletions test/commands/bundlephobia.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {expect, test} from '@oclif/test'

// TODO: add test for invalid package
// test for valid with matching
describe('bundlephobia', () => {
test
.stdout()
.command(['bundlephobia'])
.exit(0)
.it('if no package passed', ctx => {
expect(ctx.stdout).to.contain('At least one package must be passed')
})

test
.stdout()
.command(['bundlephobia', '[email protected]'])
.it('if package passed with argument', ctx => {
setTimeout(() => // TODO: can we remove it and check if we can resolve promise here
expect(ctx.stdout).to.contain(' [[email protected]] minified:6.5 kB gzip:2.6 kB')
, 5000) // proving 5 seconds just to be safe
})

test
.stdout()
.command(['bundlephobia', '-p', '[email protected]', '[email protected]'])
.it('if package passed with flag', ctx => {
setTimeout(() => // TODO: can we remove it and check if we can resolve promise here
expect(ctx.stdout).to.contain(' [[email protected]] minified:6.5 kB gzip:2.6 kB')
, 5000)
})
})
Loading