Skip to content

Commit

Permalink
feat: migrate to typescript (#95)
Browse files Browse the repository at this point in the history
* migrate to ts

* type more

* type more

* type more

* type more

* update cherry diff metric to prevent adding js code

* we can purely rely on loc metrics
  • Loading branch information
fwuensche authored Sep 10, 2024
1 parent a6ad13c commit 3ce73eb
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 94 deletions.
10 changes: 0 additions & 10 deletions .cherry.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,5 @@ module.exports = {
name: 'TODO',
pattern: /TODO/,
},
{
name: '[TS Migration] TS lines of code',
include: TS_FILES,
groupByFile: true,
},
{
name: '[TS Migration] JS lines of code',
include: JS_FILES,
groupByFile: true,
},
],
}
2 changes: 1 addition & 1 deletion .github/workflows/cherry_diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ jobs:
- name: Raise if new JavaScript code is added
# This command will fail if the number of lines of code in JavaScript files has increased
# in the current branch compared to the base branch, encouraging developers to contribute to migrating to TS.
run: npm run cherry -- diff --metric='TODO' --error-if-increase --quiet
run: npm run cherry -- diff --metric='[loc] JavaScript' --error-if-increase --quiet
7 changes: 5 additions & 2 deletions bin/commands/backfill.js → bin/commands/backfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getConfiguration } from '../../src/configuration.js'
import { getFiles } from '../../src/files.js'
import { panic } from '../../src/error.js'

// @ts-expect-error TODO: properly type this
export default function (program) {
program
.command('backfill')
Expand All @@ -17,11 +18,14 @@ export default function (program) {
.option('--until <until>', 'the date at which the backfill will stop as yyyy-mm-dd (defaults to today)')
.option('--interval <interval>', 'the number of days between backfills (defaults to 30)')
.option('--quiet', 'reduce output to a minimum')
// @ts-expect-error TODO: properly type this
.action(async (options) => {
const since = options.since ? new Date(options.since) : substractDays(new Date(), 90)
const until = options.until ? new Date(options.until) : new Date()
const interval = options.interval ? parseInt(options.interval) : 30
// @ts-expect-error TODO: properly type this
if (isNaN(since)) panic('Invalid since date')
// @ts-expect-error TODO: properly type this
if (isNaN(until)) panic('Invalid until date')
if (since > until) panic('The since date must be before the until date')
const initialBranch = await git.branchName()
Expand All @@ -42,11 +46,10 @@ export default function (program) {

await git.checkout(sha)

const files = await getFiles()
const codeOwners = new Codeowners()
const occurrences = await findOccurrences({
configuration,
files,
filePaths: await getFiles(),
codeOwners,
quiet: options.quiet,
})
Expand Down
4 changes: 2 additions & 2 deletions bin/commands/diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function (program) {
// Start by calculating the occurrences for the current branch
const currentOccurrences = await findOccurrences({
configuration,
files: await getFiles(),
filePaths: await getFiles(),
metricNames,
codeOwners: new Codeowners(),
quiet: options.quiet,
Expand All @@ -49,7 +49,7 @@ export default function (program) {
await git.checkout(baseBranchCommit)
previousOccurrences = await findOccurrences({
configuration,
files: await getFiles(),
filePaths: await getFiles(),
metricNames,
codeOwners: new Codeowners(),
quiet: options.quiet,
Expand Down
2 changes: 1 addition & 1 deletion bin/commands/push.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default function (program) {
await git.checkout(`${sha}~`)
const previousOccurrences = await findOccurrences({
configuration,
files: await getFiles(),
filePaths: await getFiles(),
codeOwners: new Codeowners(),
quiet: options.quiet,
})
Expand Down
4 changes: 2 additions & 2 deletions bin/commands/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ export default function (program) {
const owners = options.owner
const quiet = options.quiet

const files = owners ? await getFiles(owners, codeOwners) : await getFiles()
const filePaths = owners ? await getFiles(owners, codeOwners) : await getFiles()

const occurrences = await findOccurrences({
configuration,
files,
filePaths,
metricNames: options.metric,
codeOwners,
quiet,
Expand Down
60 changes: 48 additions & 12 deletions bin/helpers.js → bin/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Contribution, EvalMetric, Metric, Occurrence } from '../src/types.js'

import Spinnies from 'spinnies'
import _ from 'lodash'
import axios from 'axios'
Expand All @@ -11,18 +13,20 @@ export const API_BASE_URL = process.env.API_URL ?? 'https://www.cherrypush.com/a

export const UPLOAD_BATCH_SIZE = 1000

export const countByMetric = (occurrences) =>
export const countByMetric = (occurrences: Occurrence[]) =>
_(occurrences)
.groupBy('metricName')
.mapValues((occurrences) =>
_.sumBy(occurrences, (occurrence) => (_.isNumber(occurrence.value) ? occurrence.value : 1))
)
.value()

const handleApiError = async (callback) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleApiError = async (callback: any) => {
try {
return await callback()
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error.response)
throw new Error(
`❌ Error while calling cherrypush.com API ${error.response.status}: ${
Expand All @@ -33,7 +37,7 @@ const handleApiError = async (callback) => {
}
}

export const buildMetricsPayload = (occurrences) =>
export const buildMetricsPayload = (occurrences: Occurrence[]) =>
_(occurrences)
.groupBy('metricName')
.mapValues((occurrences, metricName) => ({
Expand All @@ -44,7 +48,15 @@ export const buildMetricsPayload = (occurrences) =>
.flatten()
.value()

export const uploadContributions = async (apiKey, projectName, authorName, authorEmail, sha, date, contributions) =>
export const uploadContributions = async (
apiKey: string,
projectName: string,
authorName: string,
authorEmail: string,
sha: string,
date: Date,
contributions: Contribution[]
) =>
handleApiError(() =>
axios
.post(
Expand All @@ -55,7 +67,14 @@ export const uploadContributions = async (apiKey, projectName, authorName, autho
.then(({ data }) => data)
)

const buildContributionsPayload = (projectName, authorName, authorEmail, sha, date, contributions) => ({
const buildContributionsPayload = (
projectName: string,
authorName: string,
authorEmail: string,
sha: string,
date: Date,
contributions: Contribution[]
) => ({
project_name: projectName,
author_name: authorName,
author_email: authorEmail,
Expand All @@ -67,7 +86,7 @@ const buildContributionsPayload = (projectName, authorName, authorEmail, sha, da
})),
})

export const upload = async (apiKey, projectName, date, occurrences) => {
export const upload = async (apiKey: string, projectName: string, date: Date, occurrences: Occurrence[]) => {
if (!projectName) panic('specify a project_name in your cherry.js configuration file before pushing metrics')

const uuid = await v4()
Expand Down Expand Up @@ -101,23 +120,36 @@ export const upload = async (apiKey, projectName, date, occurrences) => {
})
)
)
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
spinnies.fail('batches', {
text: `Batch ${index + 1} out of ${occurrencesBatches.length}: ${error.message}`,
})
}
}
}

const buildPushPayload = ({ apiKey, projectName, uuid, date, occurrences }) => ({
const buildPushPayload = ({
apiKey,
projectName,
uuid,
date,
occurrences,
}: {
apiKey: string
projectName: string
uuid: string
date: Date
occurrences: Occurrence[]
}) => ({
api_key: apiKey,
project_name: projectName,
date: date.toISOString(),
uuid,
metrics: buildMetricsPayload(occurrences),
})

export const buildSarifPayload = (projectName, branch, sha, occurrences) => {
export const buildSarifPayload = (projectName: string, branch: string, sha: string, occurrences: Occurrence[]) => {
const rules = _(occurrences)
.groupBy('metricName')
.map((occurrences) => ({
Expand Down Expand Up @@ -168,7 +200,7 @@ export const buildSarifPayload = (projectName, branch, sha, occurrences) => {
}
}

export const buildSonarGenericImportPayload = (occurrences) => ({
export const buildSonarGenericImportPayload = (occurrences: Occurrence[]) => ({
issues: occurrences.map((occurrence) => ({
engineId: 'cherry',
ruleId: occurrence.metricName,
Expand All @@ -184,4 +216,8 @@ export const buildSonarGenericImportPayload = (occurrences) => ({
})),
})

export const sortObject = (object) => _(object).toPairs().sortBy(0).fromPairs().value()
export const sortObject = (object: object) => _(object).toPairs().sortBy(0).fromPairs().value()

export function isEvalMetric(metric: Metric): metric is EvalMetric {
return 'eval' in metric
}
21 changes: 21 additions & 0 deletions package-lock.json

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

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"test": "sh test/fixtures/setup.sh && vitest run",
"test:setup": "sh test/fixtures/setup.sh",
"test:cleanup": "sh test/fixtures/cleanup.sh",
"type-check": "tsc --noEmit",
"format": "prettier --write ."
},
"repository": {
Expand Down Expand Up @@ -53,16 +54,19 @@
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/lodash": "^4.17.7",
"@types/spinnies": "^0.5.3",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "8.x.x",
"@typescript-eslint/parser": "8.x.x",
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.1.0",
"husky": "^9.0.11",
"lint-staged": "^15.2.10",
"prettier": "^3.3.3",
"tsx": "^4.19.0",
"typescript": "5.5.4",
"vitest": "^2.0.5",
"tsx": "^4.19.0"
"vitest": "^2.0.5"
},
"lint-staged": {
"*": "prettier --ignore-unknown --write",
Expand Down
8 changes: 5 additions & 3 deletions src/contributions.js → src/contributions.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { Contribution, Occurrence } from './types.js'

import _ from 'lodash'

const toCountByMetricName = (occurrences) =>
const toCountByMetricName = (occurrences: Occurrence[]) =>
_.mapValues(_.groupBy(occurrences, 'metricName'), (occurrences) =>
_.sum(occurrences.map((occurrence) => occurrence.value || 1))
)

export const computeContributions = (occurrences, previousOccurrences) => {
export const computeContributions = (occurrences: Occurrence[], previousOccurrences: Occurrence[]) => {
const counts = toCountByMetricName(occurrences)
const previousCounts = toCountByMetricName(previousOccurrences)

const metrics = _.uniq(Object.keys(counts).concat(Object.keys(previousCounts)))
const contributions = []
const contributions: Contribution[] = []
metrics.forEach((metric) => {
const diff = (counts[metric] || 0) - (previousCounts[metric] || 0)
if (diff !== 0) contributions.push({ metricName: metric, diff })
Expand Down
14 changes: 9 additions & 5 deletions src/date.js → src/date.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
export const toISODate = (date) => date.toISOString().split('T')[0]
export const substractDays = (date, count) => {
export const toISODate = (date: Date) => date.toISOString().split('T')[0]

export const substractDays = (date: Date, count: number) => {
date.setDate(date.getDate() - count)
return date
}
export const addDays = (date, count) => {

export const addDays = (date: Date, count: number) => {
date.setDate(date.getDate() + count)
return date
}
export const firstDayOfMonth = (date) => new Date(Date.UTC(date.getFullYear(), date.getMonth(), 1))
export const nextMonth = (originalDate) => {

export const firstDayOfMonth = (date: Date) => new Date(Date.UTC(date.getFullYear(), date.getMonth(), 1))

export const nextMonth = (originalDate: Date) => {
const date = firstDayOfMonth(originalDate) // Avoid returning 1 for getMonth() when day is 31
const [year, month] = date.getMonth() < 11 ? [date.getFullYear(), date.getMonth() + 1] : [date.getFullYear() + 1, 0]

Expand Down
Loading

0 comments on commit 3ce73eb

Please sign in to comment.