mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-07-14 18:15:56 -04:00
chore(lint): add perf category to oxlint and fix resulting warnings
- Enable `perf: warn` in `.oxlintrc.json` and add `builtin` env; disable `no-await-in-loop` and `react/no-array-index-key` globally
- Reorder plugins so `oxc` is first
- Add `.oxfmtrc.json` override to suppress trailing commas in JSON/JSONC files
- Replace spread-into-new-object patterns (`{ ...x, key: val }`) with `Object.assign(x, { key: val })` in discover, explore, integrations, search, credits, and image-cache to satisfy perf rules
- Memoize `ChartContext` and `ToggleGroupContext` provider values to avoid unnecessary re-renders
This commit is contained in:
@@ -38,6 +38,14 @@
|
||||
],
|
||||
"functions": ["cn", "clsx", "cva", "useResolveClassNames"]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.json", "**/*.jsonc"],
|
||||
"options": {
|
||||
"trailingComma": "none"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
|
||||
+9
-3
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": ["eslint", "typescript", "unicorn", "oxc", "react", "import"],
|
||||
"plugins": ["oxc", "eslint", "typescript", "react", "import", "unicorn"],
|
||||
"env": {
|
||||
"builtin": true
|
||||
},
|
||||
"categories": {
|
||||
"correctness": "error",
|
||||
"suspicious": "warn"
|
||||
"suspicious": "warn",
|
||||
"perf": "warn"
|
||||
},
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": "off",
|
||||
@@ -12,7 +16,9 @@
|
||||
"import/no-named-as-default-member": "off",
|
||||
"unicorn/no-array-sort": "off",
|
||||
"unicorn/consistent-function-scoping": "off",
|
||||
"no-new": "off"
|
||||
"no-new": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"react/no-array-index-key": "off"
|
||||
},
|
||||
"ignorePatterns": ["node_modules", "dist", "build", "docs", "**/routeTree.gen.ts"]
|
||||
}
|
||||
|
||||
@@ -50,11 +50,10 @@ export const discover = os.discover.use(authed).handler(async ({ input, context
|
||||
const titleMap = ensureBrowseTitlesExist(baseItems);
|
||||
const items = baseItems.map((item) => {
|
||||
const entry = titleMap.get(`${item.tmdbId}-${item.type}`);
|
||||
return {
|
||||
...item,
|
||||
return Object.assign(item, {
|
||||
id: entry?.id ?? "",
|
||||
posterThumbHash: entry?.posterThumbHash ?? null,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const titleIds = items.map((r) => r.id);
|
||||
|
||||
@@ -64,11 +64,10 @@ export const trending = os.explore.trending.use(authed).handler(async ({ input,
|
||||
|
||||
const items = baseItems.map((item) => {
|
||||
const entry = titleMap.get(`${item.tmdbId}-${item.type}`);
|
||||
return {
|
||||
...item,
|
||||
return Object.assign(item, {
|
||||
id: entry?.id ?? "",
|
||||
posterThumbHash: entry?.posterThumbHash ?? null,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const heroEntry = heroResult
|
||||
@@ -125,11 +124,10 @@ export const popular = os.explore.popular.use(authed).handler(async ({ input, co
|
||||
const titleMap = ensureBrowseTitlesExist(baseItems);
|
||||
const items = baseItems.map((item) => {
|
||||
const entry = titleMap.get(`${item.tmdbId}-${item.type}`);
|
||||
return {
|
||||
...item,
|
||||
return Object.assign(item, {
|
||||
id: entry?.id ?? "",
|
||||
posterThumbHash: entry?.posterThumbHash ?? null,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const titleIds = items.map((r) => r.id);
|
||||
|
||||
@@ -55,8 +55,7 @@ export const list = os.integrations.list.use(authed).handler(({ context }) => {
|
||||
|
||||
const result = userIntegrations.map((integration) => {
|
||||
const events = eventsByIntegration.get(integration.id) ?? [];
|
||||
return {
|
||||
...serializeIntegration(integration),
|
||||
return Object.assign(serializeIntegration(integration), {
|
||||
recentEvents: events.map((e) => ({
|
||||
id: e.id,
|
||||
eventType: e.eventType,
|
||||
@@ -65,7 +64,7 @@ export const list = os.integrations.list.use(authed).handler(({ context }) => {
|
||||
status: e.status,
|
||||
receivedAt: e.receivedAt.toISOString(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return { integrations: result };
|
||||
|
||||
@@ -53,10 +53,7 @@ export const search = os.search.use(authed).handler(async ({ input }) => {
|
||||
})),
|
||||
);
|
||||
return {
|
||||
results: personItems.map((r) => ({
|
||||
...r,
|
||||
id: personMap.get(r.tmdbId),
|
||||
})),
|
||||
results: personItems.map((r) => Object.assign(r, { id: personMap.get(r.tmdbId) })),
|
||||
page: personResults.page ?? input.page,
|
||||
totalPages: personResults.total_pages ?? 1,
|
||||
totalResults: personResults.total_results ?? 0,
|
||||
@@ -140,9 +137,9 @@ export const search = os.search.use(authed).handler(async ({ input }) => {
|
||||
);
|
||||
|
||||
const results = mapped.map((r) => {
|
||||
if (r.type === "person") return { ...r, id: personMap.get(r.tmdbId) };
|
||||
if (r.type === "person") return Object.assign(r, { id: personMap.get(r.tmdbId) });
|
||||
const entry = titleMap.get(`${r.tmdbId}-${r.type}`);
|
||||
return { ...r, id: entry?.id };
|
||||
return Object.assign(r, { id: entry?.id });
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -44,9 +44,10 @@ function ChartContainer({
|
||||
}) {
|
||||
const uniqueId = React.useId();
|
||||
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
||||
const contextValue = React.useMemo(() => ({ config }), [config]);
|
||||
|
||||
return (
|
||||
<ChartContext.Provider value={{ config }}>
|
||||
<ChartContext.Provider value={contextValue}>
|
||||
<div
|
||||
data-slot="chart"
|
||||
data-chart={chartId}
|
||||
|
||||
@@ -31,6 +31,10 @@ function ToggleGroup({
|
||||
spacing?: number;
|
||||
orientation?: "horizontal" | "vertical";
|
||||
}) {
|
||||
const contextValue = React.useMemo(
|
||||
() => ({ variant, size, spacing, orientation }),
|
||||
[variant, size, spacing, orientation],
|
||||
);
|
||||
return (
|
||||
<ToggleGroupPrimitive
|
||||
data-slot="toggle-group"
|
||||
@@ -45,9 +49,7 @@ function ToggleGroup({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ToggleGroupContext.Provider value={{ variant, size, spacing, orientation }}>
|
||||
{children}
|
||||
</ToggleGroupContext.Provider>
|
||||
<ToggleGroupContext.Provider value={contextValue}>{children}</ToggleGroupContext.Provider>
|
||||
</ToggleGroupPrimitive>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -361,8 +361,7 @@ export function getCastForTitle(titleId: string): CastMember[] {
|
||||
.orderBy(titleCast.displayOrder)
|
||||
.all();
|
||||
|
||||
return rows.map((r) => ({
|
||||
...r,
|
||||
profilePath: tmdbImageUrl(r.profilePath, "profiles"),
|
||||
}));
|
||||
return rows.map((r) =>
|
||||
Object.assign(r, { profilePath: tmdbImageUrl(r.profilePath, "profiles") }),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -146,10 +146,11 @@ export async function cacheImagesForTitle(titleId: string) {
|
||||
|
||||
// Parallel cache checks instead of sequential awaits
|
||||
const checks = await Promise.all(
|
||||
candidates.map(async (c) => ({
|
||||
...c,
|
||||
cached: await isImageCached(c.category, path.basename(c.imgPath)),
|
||||
})),
|
||||
candidates.map(async (c) =>
|
||||
Object.assign(c, {
|
||||
cached: await isImageCached(c.category, path.basename(c.imgPath)),
|
||||
}),
|
||||
),
|
||||
);
|
||||
const tasks = checks
|
||||
.filter((c) => !c.cached)
|
||||
|
||||
Reference in New Issue
Block a user