* chore: fix lint

* fix: constants check:lint command

* chore(lint): permit unused vars which begin w/ _

* chore: rm dead code

* fix(lint): more lint fixes to constants pkg

* fix(lint): lint the live server

- fix lint issues

* chore: improve clean script

* fix(lint): more lint

* chore: set live server process title

* chore(deps): update to turbo@2.5.5

* chore(live): target node22

* fix(dev): add missing ui pkg dependency

* fix(dev): lint decorators

* fix(dev): lint space app

* fix(dev): address lint issues in types pkg

* fix(dev): lint editor pkg

* chore(dev): moar lint

* fix(dev): live server exit code

* chore: address PR feedback

* fix(lint): better TPageExtended type

* chore: refactor

* chore: revert most live server changes

* fix: few more lint issues

* chore: enable ci checks

Ensure we can build + confirm that lint is not getting worse.

* chore: address PR feedback

* fix: web lint warning added to package.json

* fix: ci:lint command

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
Aaron Heckmann 2025-07-24 13:14:51 -07:00 committed by GitHub
parent 514686d9d5
commit 57479f4554
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
95 changed files with 348 additions and 460 deletions

View file

@ -66,9 +66,11 @@ const InstanceGitlabAuthenticationPage = observer(() => {
<ToggleSwitch
value={Boolean(parseInt(enableGitlabConfig))}
onChange={() => {
Boolean(parseInt(enableGitlabConfig)) === true
? updateConfig("IS_GITLAB_ENABLED", "0")
: updateConfig("IS_GITLAB_ENABLED", "1");
if (Boolean(parseInt(enableGitlabConfig)) === true) {
updateConfig("IS_GITLAB_ENABLED", "0");
} else {
updateConfig("IS_GITLAB_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}

View file

@ -67,9 +67,11 @@ const InstanceGoogleAuthenticationPage = observer(() => {
<ToggleSwitch
value={Boolean(parseInt(enableGoogleConfig))}
onChange={() => {
Boolean(parseInt(enableGoogleConfig)) === true
? updateConfig("IS_GOOGLE_ENABLED", "0")
: updateConfig("IS_GOOGLE_ENABLED", "1");
if (Boolean(parseInt(enableGoogleConfig)) === true) {
updateConfig("IS_GOOGLE_ENABLED", "0");
} else {
updateConfig("IS_GOOGLE_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}

View file

@ -10,7 +10,7 @@
"preview": "next build && next start",
"start": "next start",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist",
"check:lint": "eslint . --max-warnings 0",
"check:lint": "eslint . --max-warnings 19",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",

View file

@ -10,7 +10,7 @@
"dev": "tsup --watch --onSuccess 'node --env-file=.env dist/server.js'",
"build": "tsc --noEmit && tsup",
"start": "node --env-file=.env dist/server.js",
"check:lint": "eslint . --max-warnings 0",
"check:lint": "eslint . --max-warnings 10",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
@ -51,14 +51,15 @@
"@types/compression": "1.8.1",
"@types/cors": "^2.8.17",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.21",
"@types/express-ws": "^3.0.4",
"@types/express": "^4.17.23",
"@types/express-ws": "^3.0.5",
"@types/node": "^20.14.9",
"@types/pino-http": "^5.8.4",
"concurrently": "^9.0.1",
"nodemon": "^3.1.7",
"ts-node": "^10.9.2",
"tsup": "8.4.0",
"typescript": "5.8.3"
"typescript": "5.8.3",
"ws": "^8.18.3"
}
}

View file

@ -1 +1 @@
export type TAdditionalDocumentTypes = {};
export type TAdditionalDocumentTypes = never;

View file

@ -9,6 +9,7 @@ const transport = {
};
const hooks = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
logMethod(inputArgs: any, method: any): any {
if (inputArgs.length >= 2) {
const arg1 = inputArgs.shift();

View file

@ -52,7 +52,7 @@ export const getHocusPocusServer = async () => {
cookie,
userId,
});
} catch (error) {
} catch (_error) {
throw Error("Authentication unsuccessful!");
}
},

View file

@ -36,7 +36,7 @@ export abstract class APIService {
return this.axiosInstance.patch(url, data, config);
}
delete(url: string, data?: any, config = {}) {
delete(url: string, data?: Record<string, unknown> | null | string, config = {}) {
return this.axiosInstance.delete(url, { data, ...config });
}

View file

@ -10,7 +10,7 @@ type Props = {
workspaceSlug: string;
projectId: string;
};
searchParams: any;
searchParams: Record<"board" | "peekId", string | string[] | undefined>;
};
export default async function IssuesPage(props: Props) {
@ -23,7 +23,7 @@ export default async function IssuesPage(props: Props) {
try {
response = await publishService.retrieveSettingsByProjectId(workspaceSlug, projectId);
} catch (error) {
// redirect to 404 page on error
console.error("Error fetching project publish settings:", error);
notFound();
}
@ -31,8 +31,8 @@ export default async function IssuesPage(props: Props) {
if (response?.entity_name === "project") {
url = `/issues/${response?.anchor}`;
const params = new URLSearchParams();
if (board) params.append("board", board);
if (peekId) params.append("peekId", peekId);
if (board) params.append("board", String(board));
if (peekId) params.append("peekId", String(peekId));
if (params.toString()) url += `?${params.toString()}`;
redirect(url);
} else {

View file

@ -109,7 +109,9 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
onSubmit={async (event) => {
event.preventDefault();
await handleCSRFToken();
formRef.current && formRef.current.submit();
if (formRef.current) {
formRef.current.submit();
}
setIsSubmitting(true);
}}
onError={() => setIsSubmitting(false)}

View file

@ -1,6 +1,6 @@
import { FC } from "react";
import { useSearchParams } from "next/navigation";
import Image from "next/image";
import { useSearchParams } from "next/navigation";
import { useTheme } from "next-themes";
import { API_BASE_URL } from "@plane/constants";
// images

View file

@ -1,6 +1,6 @@
import { FC } from "react";
import { useSearchParams } from "next/navigation";
import Image from "next/image";
import { useSearchParams } from "next/navigation";
import { useTheme } from "next-themes";
import { API_BASE_URL } from "@plane/constants";
// images

View file

@ -1,6 +1,6 @@
import { FC } from "react";
import { useSearchParams } from "next/navigation";
import Image from "next/image";
import { useSearchParams } from "next/navigation";
import { useTheme } from "next-themes";
import { API_BASE_URL } from "@plane/constants";
// images

View file

@ -39,18 +39,10 @@ export const AppliedFiltersList: React.FC<Props> = observer((props) => {
{filterKey === "priority" && (
<AppliedPriorityFilters
handleRemove={(val) => handleRemoveFilter("priority", val)}
values={filterValue ?? []}
values={(filterValue ?? []) as TFilters["priority"]}
/>
)}
{/* {filterKey === "labels" && labels && (
<AppliedLabelsFilters
handleRemove={(val) => handleRemoveFilter("labels", val)}
labels={labels}
values={value}
/>
)} */}
{filterKey === "state" && (
<AppliedStateFilters
handleRemove={(val) => handleRemoveFilter("state", val)}

View file

@ -1,11 +1,11 @@
"use client";
import { X } from "lucide-react";
import { PriorityIcon } from "@plane/ui";
import { PriorityIcon, type TIssuePriorities } from "@plane/ui";
type Props = {
handleRemove: (val: string) => void;
values: string[];
values: TIssuePriorities[];
};
export const AppliedPriorityFilters: React.FC<Props> = (props) => {
@ -17,7 +17,7 @@ export const AppliedPriorityFilters: React.FC<Props> = (props) => {
values.length > 0 &&
values.map((priority) => (
<div key={priority} className="flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs">
<PriorityIcon priority={priority as any} className={`h-3 w-3`} />
<PriorityIcon priority={priority} className={`h-3 w-3`} />
{priority}
<button
type="button"

View file

@ -7,7 +7,7 @@ import { useRouter } from "next/navigation";
// hooks
import { useIssueFilter } from "@/hooks/store";
// store
import { TIssueQueryFilters } from "@/types/issue";
import type { TIssueLayout, TIssueQueryFilters } from "@/types/issue";
// components
import { AppliedFiltersList } from "./filters-list";
@ -39,13 +39,21 @@ export const IssueAppliedFilters: FC<TIssueAppliedFilters> = observer((props) =>
const priority = key === "priority" ? value : (issueFilters?.filters?.priority ?? []);
const labels = key === "labels" ? value : (issueFilters?.filters?.labels ?? []);
let params: any = { board: activeLayout || "list" };
if (priority.length > 0) params = { ...params, priority: priority.join(",") };
if (state.length > 0) params = { ...params, states: state.join(",") };
if (labels.length > 0) params = { ...params, labels: labels.join(",") };
params = new URLSearchParams(params).toString();
const params: {
board: TIssueLayout | string;
priority?: string;
states?: string;
labels?: string;
} = {
board: activeLayout || "list",
};
router.push(`/issues/${anchor}?${params}`);
if (priority.length > 0) params.priority = priority.join(",");
if (state.length > 0) params.states = state.join(",");
if (labels.length > 0) params.labels = labels.join(",");
const qs = new URLSearchParams(params).toString();
router.push(`/issues/${anchor}?${qs}`);
},
[activeLayout, anchor, issueFilters, router]
);

View file

@ -37,7 +37,7 @@ export const IssueKanbanLayoutRoot: React.FC<Props> = observer((props: Props) =>
fetchNextPublicIssues(anchor, groupId, subgroupId);
}
},
[fetchNextPublicIssues]
[anchor, getIssueLoader, fetchNextPublicIssues]
);
const debouncedFetchMoreIssues = debounce(

View file

@ -41,7 +41,7 @@ export const IssuesListLayoutRoot = observer((props: Props) => {
(groupId?: string) => {
fetchNextPublicIssues(anchor, groupId);
},
[fetchNextPublicIssues]
[anchor, fetchNextPublicIssues]
);
return (

View file

@ -18,7 +18,9 @@ export const useIntersectionObserver = (
const observer = new IntersectionObserver(
(entries) => {
if (entries[entries.length - 1].isIntersecting) {
callback && callback();
if (callback) {
callback();
}
}
},
{

View file

@ -64,7 +64,7 @@ export class InstanceStore implements IInstanceStore {
this.instance = instanceInfo.instance;
this.config = instanceInfo.config;
});
} catch (error) {
} catch (_error) {
runInAction(() => {
this.isLoading = false;
this.error = {

View file

@ -194,7 +194,7 @@ export class IssueDetailStore implements IIssueDetailStore {
});
await this.issueService.updateComment(anchor, issueID, commentID, data);
} catch (error) {
} catch (_error) {
const issueComments = await this.issueService.listComments(anchor, issueID);
runInAction(() => {
@ -222,7 +222,7 @@ export class IssueDetailStore implements IIssueDetailStore {
},
};
});
} catch (error) {
} catch (_error) {
console.log("Failed to add issue vote");
}
};
@ -288,7 +288,7 @@ export class IssueDetailStore implements IIssueDetailStore {
await this.issueService.addCommentReaction(anchor, commentID, {
reaction: reactionHex,
});
} catch (error) {
} catch (_error) {
const issueComments = await this.issueService.listComments(anchor, issueID);
runInAction(() => {
@ -322,7 +322,7 @@ export class IssueDetailStore implements IIssueDetailStore {
});
await this.issueService.removeCommentReaction(anchor, commentID, reactionHex);
} catch (error) {
} catch (_error) {
const issueComments = await this.issueService.listComments(anchor, issueID);
runInAction(() => {
@ -356,7 +356,7 @@ export class IssueDetailStore implements IIssueDetailStore {
await this.issueService.addReaction(anchor, issueID, {
reaction: reactionHex,
});
} catch (error) {
} catch (_error) {
console.log("Failed to add issue vote");
const issueReactions = await this.issueService.listReactions(anchor, issueID);
runInAction(() => {
@ -376,7 +376,7 @@ export class IssueDetailStore implements IIssueDetailStore {
});
await this.issueService.removeReaction(anchor, issueID, reactionHex);
} catch (error) {
} catch (_error) {
console.log("Failed to remove issue reaction");
const reactions = await this.issueService.listReactions(anchor, issueID);
runInAction(() => {
@ -408,7 +408,7 @@ export class IssueDetailStore implements IIssueDetailStore {
});
await this.issueService.addVote(anchor, issueID, data);
} catch (error) {
} catch (_error) {
console.log("Failed to add issue vote");
const issueVotes = await this.issueService.listVotes(anchor, issueID);
@ -429,7 +429,7 @@ export class IssueDetailStore implements IIssueDetailStore {
});
await this.issueService.removeVote(anchor, issueID);
} catch (error) {
} catch (_error) {
console.log("Failed to remove issue vote");
const issueVotes = await this.issueService.listVotes(anchor, issueID);

View file

@ -91,7 +91,7 @@ export class ProfileStore implements IProfileStore {
this.data = userProfile;
});
return userProfile;
} catch (error) {
} catch (_error) {
runInAction(() => {
this.isLoading = false;
this.error = {
@ -118,7 +118,7 @@ export class ProfileStore implements IProfileStore {
}
const userProfile = await this.userService.updateProfile(data);
return userProfile;
} catch (error) {
} catch (_error) {
if (currentUserProfileData) {
Object.keys(currentUserProfileData).forEach((key: string) => {
const userKey: keyof TUserProfile = key as keyof TUserProfile;

View file

@ -35,7 +35,7 @@ export const getDate = (date: string | Date | undefined | null): Date | undefine
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;
return new Date(year, month - 1, day);
} catch (e) {
} catch (_err) {
return undefined;
}
};

View file

@ -19,7 +19,7 @@ const fallbackCopyTextToClipboard = (text: string) => {
// FIXME: Even though we are using this as a fallback, execCommand is deprecated 👎. We should find a better way to do this.
// https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
document.execCommand("copy");
} catch (err) {}
} catch (_err) {}
document.body.removeChild(textArea);
};

View file

@ -8,7 +8,7 @@
"build": "next build",
"start": "next start",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist",
"check:lint": "eslint . --max-warnings 0",
"check:lint": "eslint . --max-warnings 27",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
@ -64,7 +64,7 @@
"@types/react-dom": "^18.2.18",
"@types/uuid": "^9.0.1",
"@types/zxcvbn": "^4.4.4",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/eslint-plugin": "^8.36.0",
"typescript": "5.8.3"
}
}

View file

@ -1,3 +1,4 @@
.next/*
out/*
public/*
public/*
core/local-db/worker/wa-sqlite/src/*

View file

@ -2,7 +2,7 @@
import { observer } from "mobx-react";
// ui
import { GanttChartSquare, LayoutGrid, List } from "lucide-react";
import { GanttChartSquare, LayoutGrid, List, type LucideIcon } from "lucide-react";
// plane package imports
import { TCycleLayoutOptions } from "@plane/types";
import { CustomMenu } from "@plane/ui";
@ -11,7 +11,7 @@ import { useCycleFilter, useProject } from "@/hooks/store";
const CYCLE_VIEW_LAYOUTS: {
key: TCycleLayoutOptions;
icon: any;
icon: LucideIcon;
title: string;
}[] = [
{

View file

@ -14,7 +14,7 @@ import { PasswordStrengthMeter } from "@/components/account";
import { PageHead } from "@/components/core";
import { ProfileSettingContentHeader } from "@/components/profile";
// helpers
import { authErrorHandler } from "@/helpers/authentication.helper";
import { authErrorHandler, type EAuthenticationErrorCodes } from "@/helpers/authentication.helper";
// hooks
import { useUser } from "@/hooks/store";
// services
@ -87,8 +87,14 @@ const SecurityPage = observer(() => {
title: t("auth.common.password.toast.change_password.success.title"),
message: t("auth.common.password.toast.change_password.success.message"),
});
} catch (err: any) {
const errorInfo = authErrorHandler(err.error_code?.toString());
} catch (error: unknown) {
let errorInfo = undefined;
if (error instanceof Error) {
const err = error as Error & { error_code?: string };
const code = err.error_code?.toString();
errorInfo = code ? authErrorHandler(code as EAuthenticationErrorCodes) : undefined;
}
setToast({
type: TOAST_TYPE.ERROR,
title: errorInfo?.title ?? t("auth.common.password.toast.error.title"),

View file

@ -95,11 +95,17 @@ const SetPasswordPage = observer(() => {
if (!csrfToken) throw new Error("csrf token not found");
await handleSetPassword(csrfToken, { password: passwordFormData.password });
router.push("/");
} catch (err: any) {
} catch (error: unknown) {
let message = undefined;
if (error instanceof Error) {
const err = error as Error & { error?: string };
message = err.error;
}
setToast({
type: TOAST_TYPE.ERROR,
title: t("common.errors.default.title"),
message: err?.error ?? t("common.errors.default.message"),
message: message ?? t("common.errors.default.message"),
});
}
};

View file

@ -30,7 +30,7 @@ const CreateWorkspacePage = observer(() => {
const { data: currentUser } = useUser();
const { updateUserProfile } = useUserProfile();
// states
const [defaultValues, setDefaultValues] = useState({
const [defaultValues, setDefaultValues] = useState<Pick<IWorkspace, "name" | "slug" | "organization_size">>({
name: "",
slug: "",
organization_size: "",
@ -101,7 +101,7 @@ const CreateWorkspacePage = observer(() => {
<CreateWorkspaceForm
onSubmit={onSubmit}
defaultValues={defaultValues}
setDefaultValues={setDefaultValues as any}
setDefaultValues={setDefaultValues}
/>
</div>
</div>

View file

@ -42,10 +42,14 @@ const OnboardingPage = observer(() => {
// computed values
const workspacesList = Object.values(workspaces ?? {});
// fetching workspaces list
const { isLoading: workspaceListLoader } = useSWR(USER_WORKSPACES_LIST, () => {
user?.id && fetchWorkspaces();
if (user?.id) {
fetchWorkspaces();
}
});
// fetching user workspace invitations
const { isLoading: invitationsLoader, data: invitations } = useSWR(
`USER_WORKSPACE_INVITATIONS_LIST_${user?.id}`,

View file

@ -14,7 +14,7 @@ import { PasswordStrengthMeter } from "@/components/account/password-strength-me
import { PageHead } from "@/components/core/page-title";
import { ProfileSettingContentHeader, ProfileSettingContentWrapper } from "@/components/profile";
// helpers
import { authErrorHandler } from "@/helpers/authentication.helper";
import { authErrorHandler, type EAuthenticationErrorCodes } from "@/helpers/authentication.helper";
// hooks
import { useUser } from "@/hooks/store";
// services
@ -87,8 +87,10 @@ const SecurityPage = observer(() => {
title: t("auth.common.password.toast.change_password.success.title"),
message: t("auth.common.password.toast.change_password.success.message"),
});
} catch (err: any) {
const errorInfo = authErrorHandler(err.error_code?.toString());
} catch (error: unknown) {
const err = error as Error & { error_code?: string };
const code = err.error_code?.toString();
const errorInfo = code ? authErrorHandler(code as EAuthenticationErrorCodes) : undefined;
setToast({
type: TOAST_TYPE.ERROR,
title: errorInfo?.title ?? t("auth.common.password.toast.error.title"),

View file

@ -6,7 +6,7 @@ import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element
import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element";
import { observer } from "mobx-react";
// plane constants
import { DRAG_ALLOWED_GROUPS } from "@plane/constants";
import { DRAG_ALLOWED_GROUPS } from "@plane/constants";
// i18n
import { useTranslation } from "@plane/i18n";
//types

View file

@ -5,7 +5,7 @@ import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
import { observer } from "mobx-react";
// plane constants
import { DRAG_ALLOWED_GROUPS } from "@plane/constants";
import { DRAG_ALLOWED_GROUPS } from "@plane/constants";
// plane i18n
import { useTranslation } from "@plane/i18n";
// plane ui

View file

@ -6,7 +6,7 @@ import { useParams } from "next/navigation";
import { useForm, UseFormRegister } from "react-hook-form";
import { PlusIcon } from "lucide-react";
// plane constants
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
// i18n
import { useTranslation } from "@plane/i18n";
import { IProject, TIssue, EIssueLayoutTypes } from "@plane/types";

View file

@ -29,7 +29,7 @@ type Props = {
slug: string;
organization_size: string;
};
setDefaultValues: Dispatch<SetStateAction<IWorkspace>>;
setDefaultValues: Dispatch<SetStateAction<Pick<IWorkspace, "name" | "slug" | "organization_size">>>;
secondaryButton?: React.ReactNode;
primaryButtonText?: {
loading: string;

View file

@ -8,7 +8,7 @@
"build": "next build",
"start": "next start",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist",
"check:lint": "eslint . --max-warnings 0",
"check:lint": "eslint . --max-warnings 821",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",