[Tokei] Implement a lines of code (LOC) badge (#5547)

* Implement LOC badge

* Apply suggestions from code review

Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>

* Fix tokei tests after API change

* Format the code

* Rename tokei service file

* Add comment about Tokei API behavior

* Document tokei badge behavior

Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>
This commit is contained in:
Jonathan
2020-09-17 23:34:15 -04:00
committed by GitHub
parent ab65b42f0b
commit c8eb99fc4c
2 changed files with 113 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
'use strict'
const Joi = require('@hapi/joi')
const { metric } = require('../text-formatters')
const { nonNegativeInteger } = require('../validators')
const { BaseJsonService } = require('..')
const schema = Joi.object({
lines: nonNegativeInteger,
}).required()
const documentation = `
<p>
The <code>provider</code> is the domain name of git host.
If no TLD is provided, <code>.com</code> will be added.
For example, setting <code>gitlab</code> or <code>bitbucket.org</code> as the
provider also works.
<br><br>
Tokei will automatically count all files with a recognized extension. It will
automatically ignore files and folders in <code>.ignore</code> files. If you
want to ignore files or folders specifically for tokei, add them to the
<code>.tokeignore</code> in the root of your repository.
See
<a href="https://github.com/XAMPPRocky/tokei#excluding-folders">https://github.com/XAMPPRocky/tokei#excluding-folders</a>
for more info.
</p>
`
module.exports = class Tokei extends BaseJsonService {
static category = 'size'
static route = { base: 'tokei/lines', pattern: ':provider/:user/:repo' }
static get examples() {
return [
{
title: 'Lines of code',
namedParams: {
provider: 'github',
user: 'badges',
repo: 'shields',
},
staticPreview: this.render({ lines: 119500 }),
keywords: ['loc', 'tokei'],
documentation,
},
]
}
static defaultBadgeData = {
label: 'total lines',
color: 'blue',
}
static render({ lines }) {
return { message: metric(lines) }
}
async fetch({ provider, user, repo }) {
// This request uses the tokei-rs (https://github.com/XAMPPRocky/tokei_rs) API.
//
// By default, the API returns an svg, but when the Accept HTTP header is set to
// `application/json`, it sends json data. The `_requestJson` method
// automatically sets the Accept Header to what we need, so we don't need to
// specify it here.
//
// This behaviour of the API is "documented" here:
// https://github.com/XAMPPRocky/tokei_rs/issues/8#issuecomment-475071147
return this._requestJson({
schema,
url: `https://tokei.rs/b1/${provider}/${user}/${repo}`,
errorMessages: {
400: 'repo not found',
},
})
}
async handle({ provider, user, repo }) {
const { lines } = await this.fetch({ provider, user, repo })
return this.constructor.render({ lines })
}
}

View File

@@ -0,0 +1,31 @@
'use strict'
const { ServiceTester } = require('../tester')
const { isMetric } = require('../test-validators')
const t = new ServiceTester({ id: 'tokei', title: 'Tokei LOC Tests' })
module.exports = t
t.create('GitHub LOC')
.get('/lines/github/badges/shields.json')
.expectBadge({ label: 'total lines', message: isMetric })
t.create('GitLab LOC')
.get('/lines/gitlab/tezos/tezos.json')
.expectBadge({ label: 'total lines', message: isMetric })
t.create('GitHub LOC (with .com)')
.get('/lines/github.com/badges/shields.json')
.expectBadge({ label: 'total lines', message: isMetric })
t.create('GitLab LOC (with .com)')
.get('/lines/gitlab.com/tezos/tezos.json')
.expectBadge({ label: 'total lines', message: isMetric })
t.create('BitBucket LOC')
.get('/lines/bitbucket.org/MonliH/tokei-shields-test.json')
.expectBadge({ label: 'total lines', message: isMetric })
t.create('Invalid Provider')
.get('/lines/example/tezos/tezos.json')
.expectBadge({ label: 'total lines', message: 'repo not found' })