Skip to content

Commit

Permalink
Add ability to format bytes as metric or IEC; affects [bundlejs bundl…
Browse files Browse the repository at this point in the history
…ephobia ChromeWebStoreSize CratesSize DockerSize GithubRepoSize GithubCodeSize GithubSize NpmUnpackedSize SpigetDownloadSize steam VisualStudioAppCenterReleasesSize whatpulse] (badges#10547)

* add renderSizeBadge helper, use it everywhere

- switch from pretty-bytes to byte-size
- add renderSizeBadge() helper function
- match upstream conventions for metric/IEC units
- add new test helpers and use them in service tests

* unrelated: fix npm unpacked size query param schema

not strictly related to this PR
but I noticed it was broken

* chromewebstore: reformat size string, test against isIecFileSize
  • Loading branch information
chris48s authored Dec 1, 2024
1 parent b7d7f45 commit 151c70d
Show file tree
Hide file tree
Showing 28 changed files with 142 additions and 144 deletions.
21 changes: 9 additions & 12 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@shields_io/camp": "^18.1.2",
"@xmldom/xmldom": "0.9.5",
"badge-maker": "file:badge-maker",
"byte-size": "^9.0.0",
"bytes": "^3.1.2",
"camelcase": "^8.0.0",
"chalk": "^5.3.0",
Expand Down Expand Up @@ -57,7 +58,6 @@
"parse-link-header": "^2.0.0",
"path-to-regexp": "^6.3.0",
"pg": "^8.13.1",
"pretty-bytes": "^6.1.1",
"priorityqueuejs": "^2.0.0",
"prom-client": "^15.1.3",
"qs": "^6.13.1",
Expand Down
15 changes: 5 additions & 10 deletions services/bundlejs/bundlejs-package.service.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import Joi from 'joi'
import { BaseJsonService, pathParam, queryParam } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'

const schema = Joi.object({
size: Joi.object({
compressedSize: Joi.string().required(),
rawCompressedSize: nonNegativeInteger,
}).required(),
}).required()

Expand Down Expand Up @@ -76,13 +78,6 @@ export default class BundlejsPackage extends BaseJsonService {

static defaultBadgeData = { label: 'bundlejs', color: 'informational' }

static render({ size }) {
return {
label: 'minified size (gzip)',
message: size,
}
}

async fetch({ scope, packageName, exports }) {
const searchParams = {
q: `${scope ? `${scope}/` : ''}${packageName}`,
Expand Down Expand Up @@ -110,7 +105,7 @@ export default class BundlejsPackage extends BaseJsonService {

async handle({ scope, packageName }, { exports }) {
const json = await this.fetch({ scope, packageName, exports })
const size = json.size.compressedSize
return this.constructor.render({ size })
const size = json.size.rawCompressedSize
return renderSizeBadge(size, 'metric', 'minified size (gzip)')
}
}
12 changes: 6 additions & 6 deletions services/bundlejs/bundlejs-package.tester.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { isFileSize } from '../test-validators.js'
import { isMetricFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()

t.create('bundlejs/package (packageName)')
.get('/jquery.json')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })

t.create('bundlejs/package (version)')
.get('/[email protected]')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })

t.create('bundlejs/package (scoped)')
.get('/@cycle/rx-run.json')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })

t.create('bundlejs/package (select exports)')
.get('/value-enhancer.json?exports=isVal,val')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })

t.create('bundlejs/package (scoped version select exports)')
.get('/@ngneat/[email protected]?exports=randEmail,randFullName')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })

t.create('bundlejs/package (not found)')
.get('/[email protected]')
Expand Down
7 changes: 2 additions & 5 deletions services/bundlephobia/bundlephobia.service.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService, pathParams } from '../index.js'

Expand Down Expand Up @@ -112,10 +112,7 @@ export default class Bundlephobia extends BaseJsonService {

static render({ format, size }) {
const label = format === 'min' ? 'minified size' : 'minzipped size'
return {
label,
message: prettyBytes(size),
}
return renderSizeBadge(size, 'iec', label)
}

async fetch({ scope, packageName, version }) {
Expand Down
18 changes: 9 additions & 9 deletions services/bundlephobia/bundlephobia.tester.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()

Expand All @@ -13,42 +13,42 @@ const data = [
{
format: formats.A,
get: '/min/preact.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.B,
get: '/min/preact/8.0.0.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.C,
get: '/min/@cycle/core.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.D,
get: '/min/@cycle/core/7.0.0.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.A,
get: '/minzip/preact.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.B,
get: '/minzip/preact/8.0.0.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.C,
get: '/minzip/@cycle/core.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.D,
get: '/minzip/@cycle/core/7.0.0.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.A,
Expand Down
15 changes: 13 additions & 2 deletions services/chrome-web-store/chrome-web-store-size.service.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NotFound, pathParams } from '../index.js'
import { InvalidResponse, NotFound, pathParams } from '../index.js'
import BaseChromeWebStoreService from './chrome-web-store-base.js'

export default class ChromeWebStoreSize extends BaseChromeWebStoreService {
Expand All @@ -22,6 +22,17 @@ export default class ChromeWebStoreSize extends BaseChromeWebStoreService {
color: 'blue',
}

transform(sizeStr) {
const match = sizeStr.match(/^(\d+)([a-zA-Z]+)$/)
if (!match) {
throw new InvalidResponse({
prettyMessage: 'size does not match expected format',
})
}
const [, size, units] = match
return `${size} ${units}`
}

async handle({ storeId }) {
const chromeWebStore = await this.fetch({ storeId })
const size = chromeWebStore.size()
Expand All @@ -30,6 +41,6 @@ export default class ChromeWebStoreSize extends BaseChromeWebStoreService {
throw new NotFound({ prettyMessage: 'not found' })
}

return { message: size }
return { message: this.transform(size) }
}
}
4 changes: 2 additions & 2 deletions services/chrome-web-store/chrome-web-store-size.tester.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createServiceTester } from '../tester.js'
import { isIecFileSize } from '../test-validators.js'

export const t = await createServiceTester()
const isFileSize = /^\d+(\.\d+)?(MiB|KiB)$/

t.create('Size').get('/nccfelhkfpbnefflolffkclhenplhiab.json').expectBadge({
label: 'extension size',
message: isFileSize,
message: isIecFileSize,
})

t.create('Size (not found)')
Expand Down
12 changes: 2 additions & 10 deletions services/crates/crates-size.service.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import prettyBytes from 'pretty-bytes'
import { pathParams } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { BaseCratesService, description } from './crates-base.js'

export default class CratesSize extends BaseCratesService {
Expand Down Expand Up @@ -38,17 +38,9 @@ export default class CratesSize extends BaseCratesService {
},
}

render({ size }) {
return {
label: 'size',
message: prettyBytes(size),
color: 'blue',
}
}

async handle({ crate, version }) {
const json = await this.fetch({ crate, version })
const size = this.constructor.getVersionObj(json).crate_size
return this.render({ size })
return renderSizeBadge(size, 'iec')
}
}
6 changes: 3 additions & 3 deletions services/crates/crates-size.tester.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { createServiceTester } from '../tester.js'
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
export const t = await createServiceTester()

t.create('size')
.get('/tokio.json')
.expectBadge({ label: 'size', message: isFileSize })
.expectBadge({ label: 'size', message: isIecFileSize })

t.create('size (with version)')
.get('/tokio/1.32.0.json')
.expectBadge({ label: 'size', message: '725 kB' })
.expectBadge({ label: 'size', message: '708 KiB' })

t.create('size (not found)')
.get('/not-a-crate.json')
Expand Down
8 changes: 2 additions & 6 deletions services/docker/docker-size.service.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'
import { latest } from '../version.js'
import { BaseJsonService, NotFound, pathParams, queryParams } from '../index.js'
Expand Down Expand Up @@ -124,10 +124,6 @@ export default class DockerSize extends BaseJsonService {

static defaultBadgeData = { label: 'image size', color: 'blue' }

static render({ size }) {
return { message: prettyBytes(size) }
}

async fetch({ user, repo, tag, page }) {
page = page ? `&page=${page}` : ''
return await fetch(this, {
Expand Down Expand Up @@ -233,6 +229,6 @@ export default class DockerSize extends BaseJsonService {
}

const { size } = await this.transform({ tag, sort, data, arch })
return this.constructor.render({ size })
return renderSizeBadge(size, 'iec', 'image size')
}
}
12 changes: 6 additions & 6 deletions services/docker/docker-size.tester.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()

t.create('docker image size (valid, library)')
.get('/_/alpine.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})

t.create('docker image size (valid, library, arch parameter )')
.get('/_/mysql.json?arch=amd64')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})

t.create('docker image size (valid, library with tag)')
.get('/_/alpine/latest.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})

t.create('docker image size (valid, user)')
.get('/jrottenberg/ffmpeg.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})

t.create('docker image size (valid, user with tag)')
.get('/jrottenberg/ffmpeg/3.2-alpine.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})

t.create('docker image size (invalid, incorrect tag)')
Expand Down
11 changes: 2 additions & 9 deletions services/github/github-code-size.service.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import prettyBytes from 'pretty-bytes'
import { pathParams } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { BaseGithubLanguage } from './github-languages-base.js'
import { documentation } from './github-helpers.js'

Expand Down Expand Up @@ -31,15 +31,8 @@ export default class GithubCodeSize extends BaseGithubLanguage {

static defaultBadgeData = { label: 'code size' }

static render({ size }) {
return {
message: prettyBytes(size),
color: 'blue',
}
}

async handle({ user, repo }) {
const data = await this.fetch({ user, repo })
return this.constructor.render({ size: this.getTotalSize(data) })
return renderSizeBadge(this.getTotalSize(data), 'iec', 'code size')
}
}
Loading

0 comments on commit 151c70d

Please sign in to comment.