Skip to content

Commit eac4ef2

Browse files
committed
feat: replace lodash with stdlib
1 parent a4b4bcf commit eac4ef2

File tree

8 files changed

+243
-24
lines changed

8 files changed

+243
-24
lines changed

package-lock.json

Lines changed: 12 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@
3232
"find-up": "^5.0.0",
3333
"js-yaml": "^4.1.0",
3434
"keytar": "^7.9.0",
35-
"lodash-es": "^4.17.21",
3635
"node-fetch": "^2.6.7",
3736
"p-limit": "^3.0.0",
3837
"p-map": "^4.0.0",
38+
"process": "0.11.10",
3939
"read": "^1.0.7",
4040
"rimraf": "^4.0.0",
4141
"semver": "^7.6.2",
@@ -74,7 +74,6 @@
7474
"eslint-plugin-prettier": "5.2.1",
7575
"husky": "9.1.7",
7676
"jest": "^29.6.1",
77-
"lodash": "4.17.21",
7877
"prettier": "3.4.2",
7978
"rollup": "2.79.2",
8079
"rollup-plugin-typescript2": "0.36.0",

src/cli/commands/definition/dump-setup.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import fs from "fs"
22
import yaml from "js-yaml"
3-
import { keyBy } from "lodash-es"
43
import pMap from "p-map"
54
import { CommandModule } from "yargs"
65
import { Config } from "../../../config"
@@ -196,7 +195,9 @@ async function getProjects(
196195
const repos = await getReposFromGitHub(github, orgs)
197196
const snykRepos = await snykReposPromise
198197

199-
const definitionRepos = keyBy(getRepos(definition), (it) => it.id)
198+
const definitionRepos = Object.fromEntries(
199+
getRepos(definition).map((repo: GetReposResponse) => [repo.id, repo]),
200+
)
200201

201202
const projectGroups = Object.values(
202203
repos.reduce<{

src/cli/commands/snyk/report.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { groupBy, repeat, sortBy, sumBy } from "lodash-es"
1+
import { sumBy } from "../../../collections/collections"
22
import { sprintf } from "sprintf-js"
33
import { CommandModule } from "yargs"
44
import { DefinitionFile, getRepos } from "../../../definition"
@@ -14,6 +14,7 @@ import {
1414
definitionFileOptionValue,
1515
getDefinitionFile,
1616
} from "../../util"
17+
import { groupBy, sortBy } from "../../../collections/collections"
1718

1819
function totalSeverityCount(project: SnykProject) {
1920
return (
@@ -26,7 +27,7 @@ function totalSeverityCount(project: SnykProject) {
2627

2728
function buildStatsLine(stats: SnykProject["issueCountsBySeverity"]) {
2829
function item(num: number, str: string) {
29-
return num === 0 ? repeat(" ", str.length + 4) : sprintf("%3d %s", num, str)
30+
return num === 0 ? " ".repeat(str.length + 4) : sprintf("%3d %s", num, str)
3031
}
3132

3233
return sprintf(
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import { groupBy, uniq, sortBy, sumBy } from "./collections"
2+
3+
describe("collections", () => {
4+
describe("groupBy", () => {
5+
type TestCase<T> = {
6+
description: string
7+
got: T[]
8+
iteratee: (item: T) => string
9+
want: Record<string, T[]>
10+
}
11+
const tests: TestCase<{ id: number }>[] = [
12+
{
13+
description: "groups items by id",
14+
got: [{ id: 1 }, { id: 2 }, { id: 1 }],
15+
iteratee: (item) => item.id.toString(),
16+
want: { "1": [{ id: 1 }, { id: 1 }], "2": [{ id: 2 }] },
17+
},
18+
{
19+
description: "groups items by id with duplicates",
20+
got: [{ id: 1 }, { id: 2 }, { id: 1 }],
21+
iteratee: (item) => item.id.toString(),
22+
want: { "1": [{ id: 1 }, { id: 1 }], "2": [{ id: 2 }] },
23+
},
24+
{
25+
description: "handles empty array",
26+
got: [],
27+
iteratee: (item) => item.id.toString(),
28+
want: {},
29+
},
30+
{
31+
description: "handles single item",
32+
got: [{ id: 1 }],
33+
iteratee: (item) => item.id.toString(),
34+
want: { "1": [{ id: 1 }] },
35+
},
36+
{
37+
description: "handles multiple unique items",
38+
got: [{ id: 1 }, { id: 2 }, { id: 3 }],
39+
iteratee: (item) => item.id.toString(),
40+
want: { "1": [{ id: 1 }], "2": [{ id: 2 }], "3": [{ id: 3 }] },
41+
},
42+
]
43+
44+
test.each(tests)(
45+
"$description",
46+
({ got, iteratee, want }: TestCase<{ id: number }>) => {
47+
expect(groupBy(got, iteratee)).toEqual(want)
48+
},
49+
)
50+
})
51+
52+
describe("uniq", () => {
53+
type TestCase<T> = {
54+
description: string
55+
got: T[]
56+
want: T[]
57+
}
58+
const tests: TestCase<number>[] = [
59+
{
60+
description: "removes duplicates from array",
61+
got: [1, 2, 2, 3, 4, 4, 5],
62+
want: [1, 2, 3, 4, 5],
63+
},
64+
{
65+
description: "removes duplicates from array with duplicates",
66+
got: [1, 2, 2, 3, 4, 4, 5],
67+
want: [1, 2, 3, 4, 5],
68+
},
69+
{
70+
description: "handles empty array",
71+
got: [],
72+
want: [],
73+
},
74+
{
75+
description: "handles array with all same elements",
76+
got: [1, 1, 1, 1],
77+
want: [1],
78+
},
79+
{
80+
description: "handles array with all unique elements",
81+
got: [1, 2, 3, 4, 5],
82+
want: [1, 2, 3, 4, 5],
83+
},
84+
]
85+
86+
test.each(tests)("$description", ({ got, want }: TestCase<number>) => {
87+
expect(uniq(got)).toEqual(want)
88+
})
89+
})
90+
91+
describe("sortBy", () => {
92+
type TestCase<T> = {
93+
description: string
94+
got: T[]
95+
getKey: (item: T) => string
96+
want: T[]
97+
}
98+
const tests: TestCase<{ name: string }>[] = [
99+
{
100+
description: "sorts items by name",
101+
got: [{ name: "b" }, { name: "a" }, { name: "c" }],
102+
getKey: (item) => item.name,
103+
want: [{ name: "a" }, { name: "b" }, { name: "c" }],
104+
},
105+
{
106+
description: "sorts items by numeric string",
107+
got: [{ name: "30" }, { name: "20" }, { name: "40" }],
108+
getKey: (item) => item.name,
109+
want: [{ name: "20" }, { name: "30" }, { name: "40" }],
110+
},
111+
{
112+
description: "handles empty array",
113+
got: [],
114+
getKey: (item) => item.name,
115+
want: [],
116+
},
117+
{
118+
description: "sorts items by numeric string with two elements",
119+
got: [{ name: "2" }, { name: "1" }],
120+
getKey: (item) => item.name,
121+
want: [{ name: "1" }, { name: "2" }],
122+
},
123+
{
124+
description: "sorts items by name with multiple elements",
125+
got: [{ name: "z" }, { name: "x" }, { name: "y" }],
126+
getKey: (item) => item.name,
127+
want: [{ name: "x" }, { name: "y" }, { name: "z" }],
128+
},
129+
]
130+
131+
test.each(tests)(
132+
"$description",
133+
({ got, getKey, want }: TestCase<{ name: string }>) => {
134+
expect(sortBy(got, getKey)).toEqual(want)
135+
},
136+
)
137+
})
138+
139+
describe("sumBy", () => {
140+
type TestCase<T> = {
141+
description: string
142+
got: T[]
143+
iteratee: (item: T) => number
144+
want: number
145+
}
146+
const cases: TestCase<{ value: number }>[] = [
147+
{
148+
description: "sums values in array",
149+
got: [{ value: 1 }, { value: 2 }, { value: 3 }],
150+
iteratee: (item) => item.value,
151+
want: 6,
152+
},
153+
{
154+
description: "sums values in array with two elements",
155+
got: [{ value: 10 }, { value: 20 }],
156+
iteratee: (item) => item.value,
157+
want: 30,
158+
},
159+
{
160+
description: "handles empty array",
161+
got: [],
162+
iteratee: (item) => item.value,
163+
want: 0,
164+
},
165+
{
166+
description: "sums values in array with negative and positive elements",
167+
got: [{ value: -1 }, { value: 1 }],
168+
iteratee: (item) => item.value,
169+
want: 0,
170+
},
171+
{
172+
description: "sums values in array with same elements",
173+
got: [{ value: 5 }, { value: 5 }, { value: 5 }],
174+
iteratee: (item) => item.value,
175+
want: 15,
176+
},
177+
]
178+
179+
test.each(cases)(
180+
"$description",
181+
({ got, iteratee, want }: TestCase<{ value: number }>) => {
182+
expect(sumBy(got, iteratee)).toEqual(want)
183+
},
184+
)
185+
})
186+
})

src/collections/collections.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export function groupBy<T>(
2+
array: T[],
3+
iteratee: (item: T) => string,
4+
): Record<string, T[]> {
5+
return array.reduce(
6+
(result, item) => {
7+
const key = iteratee(item)
8+
if (!result[key]) {
9+
result[key] = []
10+
}
11+
result[key].push(item)
12+
return result
13+
},
14+
{} as Record<string, T[]>,
15+
)
16+
}
17+
18+
export function uniq<T>(array: T[]): T[] {
19+
return Array.from(new Set(array))
20+
}
21+
22+
export function sortBy<T>(arr: T[], getKey: (item: T) => string): T[] {
23+
return [...arr].sort((a, b) => getKey(a).localeCompare(getKey(b)))
24+
}
25+
26+
export function sumBy<T>(array: T[], iteratee: (item: T) => number): number {
27+
return array.reduce((sum, item) => sum + iteratee(item), 0)
28+
}

src/definition/definition.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import AJV from "ajv"
22
import fs from "fs"
33
import yaml from "js-yaml"
4-
import { uniq } from "lodash-es"
54
import schema from "../definition-schema.json"
65
import { Definition, GetReposResponse } from "./types"
6+
import { uniq } from "../collections/collections"
77

88
export { schema }
99

@@ -162,9 +162,8 @@ export function getRepos(definition: Definition): GetReposResponse[] {
162162
}
163163

164164
export function getGitHubOrgs(definition: Definition): string[] {
165-
return uniq(
166-
definition.projects.flatMap((project) =>
167-
project.github.map((it) => it.organization),
168-
),
165+
const githubOrganizations = definition.projects.flatMap((project) =>
166+
project.github.map((it) => it.organization),
169167
)
168+
return uniq(githubOrganizations)
170169
}

src/github/changeset/changeset.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { sortBy } from "lodash-es"
21
import pMap from "p-map"
32
import { getGitHubOrgs, getRepos } from "../../definition"
43
import {
@@ -11,6 +10,7 @@ import {
1110
import { GitHubService } from "../service"
1211
import { OrgsGetResponse, Permission, ReposGetResponse } from "../types"
1312
import { ChangeSetItem, RepoAttribUpdateItem } from "./types"
13+
import { sortBy } from "../../collections/collections"
1414

1515
function getChangedRepoAttribs(
1616
definitionRepo: DefinitionRepo,
@@ -59,14 +59,17 @@ async function getUnknownRepos(
5959
(orgName) => limitToOrg === undefined || limitToOrg === orgName,
6060
)
6161

62+
const orgRepoList = await pMap(orgs, (orgName) =>
63+
github.getOrgRepoList({ org: orgName }),
64+
)
65+
6266
return sortBy(
63-
(await pMap(orgs, (orgName) => github.getOrgRepoList({ org: orgName })))
67+
orgRepoList
6468
.flat()
6569
.filter((it) => !knownRepos.includes(`${it.owner.login}/${it.name}`)),
6670
(it) => `${it.owner.login}/${it.name}`,
6771
)
6872
}
69-
7073
/**
7174
* Get teams from both the project level and the repository level
7275
* and ensure that repository level override project level.

0 commit comments

Comments
 (0)