Skip to content

Commit 4672849

Browse files
authored
chore: clean up usage of Object.keys (coder#14484)
1 parent d2a22c5 commit 4672849

File tree

10 files changed

+83
-111
lines changed

10 files changed

+83
-111
lines changed

site/src/components/Timeline/Timeline.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,12 @@ export const Timeline = <TData,>({
3737

3838
return (
3939
<>
40-
{Object.keys(itemsByDate).map((dateStr) => {
41-
const items = itemsByDate[dateStr];
42-
43-
return (
44-
<Fragment key={dateStr}>
45-
<TimelineDateRow date={new Date(dateStr)} />
46-
{items.map(row)}
47-
</Fragment>
48-
);
49-
})}
40+
{Object.entries(itemsByDate).map(([dateStr, items]) => (
41+
<Fragment key={dateStr}>
42+
<TimelineDateRow date={new Date(dateStr)} />
43+
{items.map(row)}
44+
</Fragment>
45+
))}
5046
</>
5147
);
5248
};

site/src/contexts/ProxyContext.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,9 @@ const selectByLatency = (
233233
return undefined;
234234
}
235235

236-
const proxyMap = proxies.reduce(
237-
(acc, proxy) => {
238-
acc[proxy.id] = proxy;
239-
return acc;
240-
},
241-
{} as Record<string, Region>,
242-
);
243-
244-
const best = Object.keys(latencies)
245-
.map((proxyId) => {
246-
return {
247-
id: proxyId,
248-
...latencies[proxyId],
249-
};
250-
})
236+
const proxyMap = Object.fromEntries(proxies.map((it) => [it.id, it]));
237+
const best = Object.entries(latencies)
238+
.map(([proxyId, latency]) => ({ id: proxyId, ...latency }))
251239
// If the proxy is not in our list, or it is unhealthy, ignore it.
252240
.filter((latency) => proxyMap[latency.id]?.healthy)
253241
.sort((a, b) => a.latencyMS - b.latencyMS)

site/src/modules/dashboard/entitlements.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ export const getFeatureVisibility = (
1313
return {};
1414
}
1515

16-
const permissionPairs = Object.keys(features).map((feature) => {
17-
const { entitlement, limit, actual, enabled } = features[feature];
18-
const entitled = ["entitled", "grace_period"].includes(entitlement);
19-
const limitCompliant = limit && actual ? limit >= actual : true;
20-
return [feature, entitled && limitCompliant && enabled];
21-
});
16+
const permissionPairs = Object.entries(features).map(
17+
([feature, { entitlement, limit, actual, enabled }]) => {
18+
const entitled = ["entitled", "grace_period"].includes(entitlement);
19+
const limitCompliant = limit && actual ? limit >= actual : true;
20+
return [feature, entitled && limitCompliant && enabled];
21+
},
22+
);
2223
return Object.fromEntries(permissionPairs);
2324
};
2425

site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,23 @@ import { DockerIcon } from "components/Icons/DockerIcon";
99
import { type CSSProperties, type ElementType, type FC, useState } from "react";
1010
import type { FileTree } from "utils/filetree";
1111

12-
const sortFileTree = (fileTree: FileTree) => (a: string, b: string) => {
13-
const contentA = fileTree[a];
14-
const contentB = fileTree[b];
15-
if (typeof contentA === "object") {
16-
return -1;
17-
}
18-
if (typeof contentB === "object") {
19-
return 1;
12+
const isFolder = (content?: FileTree | string): content is FileTree =>
13+
typeof content === "object";
14+
15+
type FileTreeEntry = [key: string, content: FileTree | string];
16+
function compareFileTreeEntries(
17+
[keyA, contentA]: FileTreeEntry,
18+
[keyB, contentB]: FileTreeEntry,
19+
) {
20+
// A and B are either both files or both folders, so they should be sorted
21+
// lexically.
22+
if (isFolder(contentA) === isFolder(contentB)) {
23+
return keyA.localeCompare(keyB);
2024
}
21-
return a.localeCompare(b);
22-
};
25+
// Either A or B is a folder, and the other is a file. Put whichever one is a
26+
// folder first.
27+
return isFolder(contentA) ? -1 : 1;
28+
}
2329

2430
type ContextMenu = {
2531
path: string;
@@ -51,9 +57,6 @@ export const TemplateFileTree: FC<TemplateFilesTreeProps> = ({
5157
}) => {
5258
const [contextMenu, setContextMenu] = useState<ContextMenu | undefined>();
5359

54-
const isFolder = (content?: FileTree | string): content is FileTree =>
55-
typeof content === "object";
56-
5760
const buildTreeItems = (
5861
label: string,
5962
filename: string,
@@ -181,12 +184,11 @@ export const TemplateFileTree: FC<TemplateFilesTreeProps> = ({
181184
}
182185
>
183186
{isFolder(content) &&
184-
Object.keys(content)
185-
.sort(sortFileTree(content))
186-
.map((filename) => {
187-
const child = content[filename];
188-
return buildTreeItems(filename, filename, child, currentPath);
189-
})}
187+
Object.entries(content)
188+
.sort(compareFileTreeEntries)
189+
.map(([filename, child]) =>
190+
buildTreeItems(filename, filename, child, currentPath),
191+
)}
190192
</TreeItem>
191193
);
192194
};
@@ -198,12 +200,9 @@ export const TemplateFileTree: FC<TemplateFilesTreeProps> = ({
198200
defaultExpandedItems={activePath ? expandablePaths(activePath) : []}
199201
defaultSelectedItems={activePath}
200202
>
201-
{Object.keys(fileTree)
202-
.sort(sortFileTree(fileTree))
203-
.map((filename) => {
204-
const child = fileTree[filename];
205-
return buildTreeItems(filename, filename, child);
206-
})}
203+
{Object.entries(fileTree)
204+
.sort(compareFileTreeEntries)
205+
.map(([filename, child]) => buildTreeItems(filename, filename, child))}
207206

208207
<Menu
209208
onClose={() => setContextMenu(undefined)}

site/src/modules/templates/TemplateFiles/TemplateFiles.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type Interpolation, type Theme, useTheme } from "@emotion/react";
22
import EditOutlined from "@mui/icons-material/EditOutlined";
33
import RadioButtonCheckedOutlined from "@mui/icons-material/RadioButtonCheckedOutlined";
44
import { SyntaxHighlighter } from "components/SyntaxHighlighter/SyntaxHighlighter";
5-
import set from "lodash/fp/set";
5+
import set from "lodash/set";
66
import { linkToTemplate, useLinks } from "modules/navigation";
77
import { type FC, useCallback, useMemo } from "react";
88
import { Link } from "react-router-dom";
@@ -31,8 +31,6 @@ export const TemplateFiles: FC<TemplateFilesProps> = ({
3131
const getLink = useLinks();
3232
const theme = useTheme();
3333

34-
const filenames = Object.keys(currentFiles);
35-
3634
const fileInfo = useCallback(
3735
(filename: string) => {
3836
const value = currentFiles[filename].trim();
@@ -49,13 +47,13 @@ export const TemplateFiles: FC<TemplateFilesProps> = ({
4947
);
5048

5149
const fileTree: FileTree = useMemo(() => {
52-
let tree: FileTree = {};
53-
for (const filename of filenames) {
50+
const tree: FileTree = {};
51+
for (const filename of Object.keys(currentFiles)) {
5452
const info = fileInfo(filename);
55-
tree = set(filename.split("/"), info.value, tree);
53+
set(tree, filename.split("/"), info.value);
5654
}
5755
return tree;
58-
}, [fileInfo, filenames]);
56+
}, [fileInfo, currentFiles]);
5957

6058
const versionLink = `${getLink(
6159
linkToTemplate(organizationName, templateName),
@@ -88,7 +86,7 @@ export const TemplateFiles: FC<TemplateFilesProps> = ({
8886
</div>
8987

9088
<div css={styles.files} data-testid="template-files-content">
91-
{[...filenames]
89+
{Object.keys(currentFiles)
9290
.sort((a, b) => a.localeCompare(b))
9391
.map((filename) => {
9492
const info = fileInfo(filename);

site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({
5151
}) => {
5252
const theme = useTheme();
5353
const groupedLogsByStage = groupLogsByStage(logs);
54-
const stages = Object.keys(groupedLogsByStage);
5554

5655
return (
5756
<div
@@ -62,8 +61,7 @@ export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({
6261
}}
6362
{...attrs}
6463
>
65-
{stages.map((stage) => {
66-
const logs = groupedLogsByStage[stage];
64+
{Object.entries(groupedLogsByStage).map(([stage, logs]) => {
6765
const isEmpty = logs.every((log) => log.output === "");
6866
const lines = logs.map((log) => ({
6967
time: log.created_at,

site/src/pages/HealthPage/HealthLayout.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,9 @@ export const HealthLayout: FC = () => {
152152
</div>
153153

154154
<nav css={{ display: "flex", flexDirection: "column", gap: 1 }}>
155-
{Object.keys(visibleSections)
155+
{Object.entries(visibleSections)
156156
.sort()
157-
.map((key) => {
158-
const label =
159-
visibleSections[key as keyof typeof visibleSections];
157+
.map(([key, label]) => {
160158
const healthSection =
161159
healthStatus[key as keyof typeof visibleSections];
162160

site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,10 @@ export const ProvisionerDaemonsPage: FC = () => {
6060
const daemonScope = daemon.tags.scope || "organization";
6161
const iconScope =
6262
daemonScope === "organization" ? <Business /> : <Person />;
63-
const extraTags = Object.keys(daemon.tags)
64-
.filter((key) => key !== "scope" && key !== "owner")
65-
.reduce(
66-
(acc, key) => {
67-
acc[key] = daemon.tags[key];
68-
return acc;
69-
},
70-
{} as Record<string, string>,
71-
);
63+
64+
const extraTags = Object.entries(daemon.tags).filter(
65+
([key]) => key !== "scope" && key !== "owner",
66+
);
7267
const isWarning = warnings.length > 0;
7368
return (
7469
<div
@@ -131,8 +126,8 @@ export const ProvisionerDaemonsPage: FC = () => {
131126
</span>
132127
</Pill>
133128
</Tooltip>
134-
{Object.keys(extraTags).map((k) => (
135-
<ProvisionerTag key={k} k={k} v={extraTags[k]} />
129+
{extraTags.map(([key, value]) => (
130+
<ProvisionerTag key={key} tagName={key} tagValue={value} />
136131
))}
137132
</div>
138133
</header>
@@ -150,8 +145,8 @@ export const ProvisionerDaemonsPage: FC = () => {
150145
>
151146
{warnings.length > 0 ? (
152147
<div css={{ display: "flex", flexDirection: "column" }}>
153-
{warnings.map((warning, i) => (
154-
<span key={i}>{warning.message}</span>
148+
{warnings.map((warning) => (
149+
<span key={warning.code}>{warning.message}</span>
155150
))}
156151
</div>
157152
) : (
@@ -191,32 +186,30 @@ const parseBool = (s: string): { valid: boolean; value: boolean } => {
191186
};
192187

193188
interface ProvisionerTagProps {
194-
k: string;
195-
v: string;
196-
onDelete?: (key: string) => void;
189+
tagName: string;
190+
tagValue: string;
191+
onDelete?: (tagName: string) => void;
197192
}
198193

199-
export const ProvisionerTag: FC<ProvisionerTagProps> = ({ k, v, onDelete }) => {
200-
const { valid, value: boolValue } = parseBool(v);
201-
const kv = `${k}: ${v}`;
194+
export const ProvisionerTag: FC<ProvisionerTagProps> = ({
195+
tagName,
196+
tagValue,
197+
onDelete,
198+
}) => {
199+
const { valid, value: boolValue } = parseBool(tagValue);
200+
const kv = `${tagName}: ${tagValue}`;
202201
const content = onDelete ? (
203202
<>
204203
{kv}
205204
<IconButton
206-
aria-label={`delete-${k}`}
205+
aria-label={`delete-${tagName}`}
207206
size="small"
208207
color="secondary"
209208
onClick={() => {
210-
onDelete(k);
209+
onDelete(tagName);
211210
}}
212211
>
213-
<CloseIcon
214-
fontSize="inherit"
215-
css={{
216-
width: 14,
217-
height: 14,
218-
}}
219-
/>
212+
<CloseIcon fontSize="inherit" css={{ width: 14, height: 14 }} />
220213
</IconButton>
221214
</>
222215
) : (

site/src/pages/TemplateVersionEditorPage/ProvisionerTagsPopover.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,19 @@ export const ProvisionerTagsPopover: FC<ProvisionerTagsPopoverProps> = ({
104104
}
105105
/>
106106
<Stack direction="row" spacing={1} wrap="wrap">
107-
{Object.keys(tags)
108-
.filter((key) => {
109-
// filter out owner since you cannot override it
110-
return key !== "owner";
111-
})
112-
.map((k) => (
113-
<Fragment key={k}>
114-
{k === "scope" ? (
115-
<ProvisionerTag k={k} v={tags[k]} />
107+
{Object.entries(tags)
108+
// filter out owner since you cannot override it
109+
.filter(([key]) => key !== "owner")
110+
.map(([key, value]) => (
111+
<Fragment key={key}>
112+
{key === "scope" ? (
113+
<ProvisionerTag tagName={key} tagValue={value} />
116114
) : (
117-
<ProvisionerTag k={k} v={tags[k]} onDelete={onDelete} />
115+
<ProvisionerTag
116+
tagName={key}
117+
tagValue={value}
118+
onDelete={onDelete}
119+
/>
118120
)}
119121
</Fragment>
120122
))}

site/src/utils/filetree.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,8 @@ export const traverse = (
9696
) => void,
9797
parent?: string,
9898
) => {
99-
for (const filename of Object.keys(fileTree)) {
99+
for (const [filename, content] of Object.entries(fileTree)) {
100100
const fullPath = parent ? `${parent}/${filename}` : filename;
101-
const content = fileTree[filename];
102101
callback(content, filename, fullPath);
103102
if (typeof content === "object") {
104103
traverse(content, callback, fullPath);

0 commit comments

Comments
 (0)