Fixing eslint.

This commit is contained in:
Dessalines 2024-10-14 19:03:42 -04:00
parent 5f98a6e9e3
commit e8dfff4da7
19 changed files with 1212 additions and 126 deletions

View file

@ -1,4 +0,0 @@
generate_translations.js
webpack.config.js
tailwind.config.js
src/api_tests

View file

@ -1,41 +0,0 @@
{
"root": true,
"env": {
"browser": true
},
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"warnOnUnsupportedTypeScriptVersion": false
},
"rules": {
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"arrow-body-style": 0,
"curly": 0,
"eol-last": 0,
"eqeqeq": 0,
"func-style": 0,
"import/no-duplicates": 0,
"max-statements": 0,
"max-params": 0,
"new-cap": 0,
"no-console": 0,
"no-duplicate-imports": 0,
"no-extra-parens": 0,
"no-return-assign": 0,
"no-throw-literal": 0,
"no-trailing-spaces": 0,
"no-unused-expressions": 0,
"no-useless-constructor": 0,
"no-useless-escape": 0,
"no-var": 0,
"prefer-const": 0,
"prefer-rest-params": 0,
"quote-props": 0,
"unicorn/filename-case": 0
}
}

View file

@ -1,5 +1,5 @@
# Build the git includes for the docs
FROM alpine:3 as docs_include
FROM alpine:3 AS docs_include
RUN apk add --no-cache git bash curl
WORKDIR /app
COPY lemmy-docs ./lemmy-docs
@ -7,7 +7,7 @@ WORKDIR /app/lemmy-docs
RUN ./update-includes.sh
# Build the docs
FROM alpine:3 as docs
FROM alpine:3 AS docs
WORKDIR /app
RUN wget -O mdbook.tar.gz https://github.com/rust-lang/mdBook/releases/download/v0.4.30/mdbook-v0.4.30-x86_64-unknown-linux-musl.tar.gz
RUN tar -xzf mdbook.tar.gz
@ -15,7 +15,7 @@ COPY lemmy-docs ./lemmy-docs
RUN ./mdbook build lemmy-docs -d ../docs
# Build the typedoc API docs
FROM node:alpine as api
FROM node:alpine AS api
WORKDIR /app
COPY lemmy-js-client lemmy-js-client
WORKDIR /app/lemmy-js-client
@ -24,7 +24,7 @@ RUN pnpm i
RUN pnpm run docs
# Build the isomorphic app
FROM node:alpine as builder
FROM node:alpine AS builder
RUN apk update && apk add python3 build-base gcc wget git curl --no-cache
RUN curl -sf https://gobinaries.com/tj/node-prune | sh
RUN corepack enable pnpm
@ -63,7 +63,7 @@ RUN rm -rf ./node_modules/import-sort-parser-typescript
RUN rm -rf ./node_modules/typescript
RUN rm -rf ./node_modules/npm
FROM node:alpine as runner
FROM node:alpine AS runner
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/node_modules /app/node_modules

98
eslint.config.mjs Normal file
View file

@ -0,0 +1,98 @@
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import prettier from "eslint-plugin-prettier/recommended";
import jsxa11y from "eslint-plugin-jsx-a11y";
import inferno from "eslint-plugin-inferno";
export default [
pluginJs.configs.recommended,
...tseslint.configs.recommended,
prettier,
{
plugins: {
inferno: inferno,
rules: inferno.configs.recommended,
},
},
{
plugins: {
"jsx-a11y": jsxa11y,
},
rules: jsxa11y.configs.recommended.rules,
},
{
languageOptions: {
parser: tseslint.parser,
},
},
// For some reason this has to be in its own block
{
ignores: [
"crawl.mjs",
"generate_rss_feed.mjs",
"generate_translations.mjs",
"lemmy-js-client",
"webpack.config.js",
"tailwind.config.js",
"src/shared/build-config.js",
"src/api_tests",
"**/*.png",
"**/*.css",
"**/*.scss",
"**/*.svg",
"dist/*",
".yalc/*",
],
},
{
files: ["src/**/*.js", "src/**/*.mjs", "src/**/*.ts", "src/**/*.tsx"],
rules: {
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_" },
],
"explicit-module-boundary-types": 0,
"no-empty-function": 0,
"no-non-null-assertion": 0,
"arrow-body-style": 0,
curly: 0,
"eol-last": 0,
eqeqeq: "error",
"func-style": 0,
"import/no-duplicates": 0,
"max-statements": 0,
"max-params": 0,
"new-cap": 0,
"no-console": 0,
"no-duplicate-imports": 0,
"no-extra-parens": 0,
"no-return-assign": 0,
"no-throw-literal": 0,
"no-trailing-spaces": 0,
"no-unused-expressions": 0,
"no-useless-constructor": 0,
"no-useless-escape": 0,
"no-var": 0,
"prefer-const": "error",
"prefer-rest-params": 0,
"prettier/prettier": "error",
"quote-props": 0,
"unicorn/filename-case": 0,
"jsx-a11y/media-has-caption": 0,
"jsx-a11y/label-has-associated-control": 0,
"no-restricted-imports": [
"error",
{
patterns: [
{
group: ["assets/*", "client/*", "server/*", "shared/*"],
message: "Use relative import instead.",
},
],
},
],
},
},
];

View file

@ -11,10 +11,13 @@
"clean": "rimraf dist",
"crawl": "node crawl.mjs",
"update-donations": "node update_donations.mjs",
"lint": "node generate_translations.mjs && tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check \"src/**/*.{ts,tsx,js,css,scss}\"",
"prebuild:dev": "pnpm clean && node generate_translations.mjs && pnpm tailwind && pnpm generate_rss_feed",
"prebuild:prod": "pnpm clean && node generate_translations.mjs && pnpm tailwind && pnpm generate_rss_feed",
"lint": "pnpm translations:generate && tsc --noEmit && pnpm eslint --report-unused-disable-directives && pnpm prettier --check \"src/**/*.{ts,tsx,js,mjs,css,scss}\"",
"prebuild:dev": "pnpm clean && pnpm translations:generate && pnpm tailwind && pnpm generate_rss_feed",
"prebuild:prod": "pnpm clean && pnpm translations:generate && pnpm tailwind && pnpm generate_rss_feed",
"generate_rss_feed": "node generate_rss_feed.mjs",
"translations:generate": "node generate_translations.mjs",
"translations:init": "git submodule init && pnpm translations:update",
"translations:update": "git submodule update --remote --recursive",
"tailwind": "tailwindcss -i ./src/style.css -o ./dist/styles/styles.css --minify",
"prepare": "husky",
"start": "pnpm build:dev --watch & tailwind --watch"
@ -27,7 +30,7 @@
"@babel/preset-env": "7.25.8",
"@babel/preset-typescript": "^7.23.2",
"@babel/runtime": "^7.23.2",
"@glidejs/glide": "3.6.2",
"@glidejs/glide": "3.5.2",
"@tailwindcss/typography": "^0.5.10",
"babel-loader": "^9.1.3",
"babel-plugin-inferno": "^6.7.0",
@ -73,6 +76,9 @@
"@typescript-eslint/parser": "^8.0.0",
"css-loader": "^7.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-inferno": "^7.34.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-prettier": "^5.0.1",
"husky": "^9.0.6",
"lint-staged": "^15.0.2",
@ -84,6 +90,7 @@
"style-loader": "^4.0.0",
"terser": "^5.24.0",
"typescript": "^5.2.2",
"typescript-eslint": "^8.9.0",
"webpack-dev-server": "5.1.0"
},
"engines": {

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ import { getLanguageFromCookie, i18n } from "../shared/i18next";
// If query param is set, server updates cookie automatically,
// so no need to check the query here
const languageCookie = getLanguageFromCookie(document.cookie);
if (languageCookie != null) {
if (languageCookie !== undefined) {
i18n.changeLanguage(languageCookie);
} else {
i18n.changeLanguage(navigator.language);

View file

@ -35,7 +35,7 @@ server.use(
server.use("/feed.xml", express.static(path.resolve("./dist/feed.xml")));
function erudaInit(): string {
if (process.env["NODE_ENV"] == "development") {
if (process.env["NODE_ENV"] === "development") {
return `
<script src="//cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
@ -49,12 +49,12 @@ function setLanguage(req: Request, res: Response): string {
// Setting the language for non-js browsers
const cookieLang = getLanguageFromCookie(req.headers.cookie);
let language: string;
if (req.query["lang"] != null) {
if (req.query["lang"] !== undefined) {
language = req.query["lang"].toString();
res.cookie("lang", language, {
expires: new Date(Date.now() + 60 * 60 * 24 * 7),
});
} else if (cookieLang != null) {
} else if (cookieLang !== undefined) {
language = cookieLang;
} else {
language = req.headers["accept-language"]

View file

@ -37,6 +37,7 @@ const AppDetailsTitle = ({ app }: AppDetailsCardProps) => (
<img
src={app.icon || "/static/assets/images/lemmy.svg"}
className="rounded-xl w-7 h-7"
alt=""
/>
<a href={app.link} className={`card-title text-2xl ${TEXT_GRADIENT}`}>
{app.name}
@ -68,9 +69,10 @@ const AppDetailsCard = ({ app }: AppDetailsCardProps) => (
<img
src={app.banner || "/static/assets/images/lemmy.svg"}
className="rounded-xl max-h-96 mb-2"
alt=""
/>
<p className="text-sm text-gray-300 mb-2">{app.description}</p>
{app.sourceType == SourceType.Closed && (
{app.sourceType === SourceType.Closed && (
<div className="alert alert-warning">
<Icon icon="alert-octagon" />
<span>{i18n.t("closed_source_warning")}</span>
@ -209,7 +211,7 @@ export class Apps extends Component<any, State> {
}
function handlePlatformChange(i: Apps, event: any) {
let platform: Platform = (event.target.value as Platform) ?? Platform.All;
const platform: Platform = (event.target.value as Platform) ?? Platform.All;
i.setState({
platform,
});

View file

@ -39,7 +39,7 @@ const QrModal = ({ name, imgData }) => (
</button>
</form>
<div className="container mx-auto">
<img className="w-auto" src={imgData} />
<img className="w-auto" src={imgData} alt="" />
</div>
</div>
</dialog>
@ -56,7 +56,7 @@ export class Crypto extends Component<any, State> {
}
async componentDidMount() {
let cryptoQr = new Map<string, string>();
const cryptoQr = new Map<string, string>();
for (const c of CRYPTOS) {
cryptoQr.set(c.name, await QRCode.toDataURL(c.address));
}

View file

@ -149,7 +149,7 @@ const GoldSponsorCards = ({ title, sponsors, color }: GoldSponsorCardsProps) =>
<div className="flex flex-wrap flex-row justify-center">
{s.avatar && (
<div className="avatar w-auto h-8">
<img src={s.avatar} className="rounded" />
<img src={s.avatar} className="rounded" alt="" />
</div>
)}
<span className="text-xs">{s.name}</span>
@ -268,13 +268,13 @@ export class Donate extends Component<any, any> {
}
function convertTranslators(): Translation[] {
let trans: Translation[] = [];
const trans: Translation[] = [];
for (const [key, value] of Object.entries(translators)) {
let split = key.split("_");
let lang = split[0];
let country = split[1] !== undefined ? split[1].toUpperCase() : undefined;
const split = key.split("_");
const lang = split[0];
const country = split[1] !== undefined ? split[1].toUpperCase() : undefined;
let t: Translation = {
const t: Translation = {
lang,
country,
translators: value,

View file

@ -48,7 +48,7 @@ export class InstancePicker extends Component<Props, State> {
</button>
</form>
<div className="container mx-auto">
{this.state.activeStep == Step.Interest && (
{this.state.activeStep === Step.Interest && (
<>
<p className="text-2xl font-bold text-center pb-4">
{i18n.t("what_topic")}
@ -67,7 +67,7 @@ export class InstancePicker extends Component<Props, State> {
</div>
</>
)}
{this.state.activeStep == Step.Language && (
{this.state.activeStep === Step.Language && (
<>
<p className="text-2xl font-bold text-center pb-4">
{i18n.t("what_language")}
@ -94,21 +94,25 @@ export class InstancePicker extends Component<Props, State> {
)}
<ul className="steps steps-vertical lg:steps-horizontal w-full">
<li
<li>
<button
onClick={linkEvent(this, handleResetInterests)}
className={classNames("step text-gray-300", {
"step-primary": this.state.activeStep == Step.Interest,
"step-primary": this.state.activeStep === Step.Interest,
})}
>
{i18n.t("interests")}
</button>
</li>
<li
<li>
<button
onClick={linkEvent(this, handleResetInterests)}
className={classNames("step text-gray-300", {
"step-primary": this.state.activeStep == Step.Language,
"step-primary": this.state.activeStep === Step.Language,
})}
>
{i18n.t("languages")}
</button>
</li>
</ul>
</div>
@ -120,7 +124,7 @@ export class InstancePicker extends Component<Props, State> {
function handleTopicChange(i: InstancePicker, event: any) {
i.setState({
topic: TOPICS.find(c => c.name == event.target.value) ?? ALL_TOPIC,
topic: TOPICS.find(c => c.name === event.target.value) ?? ALL_TOPIC,
activeStep: Step.Language,
});
}

View file

@ -194,7 +194,7 @@ const imgError =
const InstanceIcon = ({ domain, icon }) => (
<a className="rounded-xl bg-neutral-800 p-4" href={buildUrl(domain)}>
<img className="w-24 h-24" src={icon} onError={imgError} />
<img className="w-24 h-24" src={icon} onError={imgError} alt="" />
</a>
);
@ -257,11 +257,11 @@ export const StatsBadges = ({ users, comments, monthlyUsers }) => (
);
function registrationModeToString(registrationMode: string): string {
if (registrationMode == "Open") {
if (registrationMode === "Open") {
return i18n.t("registrations_open");
} else if (registrationMode == "Closed") {
} else if (registrationMode === "Closed") {
return i18n.t("registrations_closed");
} else if (registrationMode == "RequireApplication") {
} else if (registrationMode === "RequireApplication") {
return i18n.t("requires_application");
} else {
return i18n.t("registrations_open");
@ -293,10 +293,13 @@ export const DetailsModal = ({
<img
src={banner}
className="object-cover w-full h-48 rounded-xl mb-3"
alt=""
/>
)}
<div className="flex flex-row flex-wrap gap-4 mb-3 items-center">
{icon && <img className="w-8 h-8" src={icon} onError={imgError} />}
{icon && (
<img className="w-8 h-8" src={icon} onError={imgError} alt="" />
)}
<StatsBadges
users={users}
comments={comments}
@ -375,11 +378,11 @@ interface Props {
}
function getSortFromQuery(sort?: string): Sort {
return SORTS.find(s => s.name == sort) ?? RANDOM_SORT;
return SORTS.find(s => s.name === sort) ?? RANDOM_SORT;
}
function getTopicFromQuery(topic?: string): Topic {
return TOPICS.find(c => c.name == topic) ?? ALL_TOPIC;
return TOPICS.find(c => c.name === topic) ?? ALL_TOPIC;
}
function getInstancesQueryParams() {
@ -459,9 +462,9 @@ export class Instances extends Component<Props, State> {
instances = instances.filter(i => !this.isOpenInstance(i));
// Sort
if (this.state.sort == RANDOM_SORT) {
if (this.state.sort === RANDOM_SORT) {
instances = sortRandom(instances);
} else if (this.state.sort == MOST_ACTIVE_SORT) {
} else if (this.state.sort === MOST_ACTIVE_SORT) {
instances = sortActive(instances);
} else {
instances = sortActive(instances).reverse();
@ -570,14 +573,14 @@ export class Instances extends Component<Props, State> {
function handleSortChange(i: Instances, event: any) {
i.setState({
sort: SORTS.find(s => s.name == event.target.value) ?? RANDOM_SORT,
sort: SORTS.find(s => s.name === event.target.value) ?? RANDOM_SORT,
});
i.buildInstanceList();
}
function handleTopicChange(i: Instances, event: any) {
i.setState({
topic: TOPICS.find(c => c.name == event.target.value) ?? ALL_TOPIC,
topic: TOPICS.find(c => c.name === event.target.value) ?? ALL_TOPIC,
});
i.buildInstanceList();
}

View file

@ -50,6 +50,7 @@ const CarouselBlock = () => (
"border-primary/[.15]": i & 1,
"border-secondary/[.15]": !(i & 1),
})}
alt=""
/>
))}
</ul>
@ -116,7 +117,11 @@ const FollowCommunitiesBlock = ({ i }: MainProps) => (
const FeatureCard = ({ pic, title, subtitle, classes }) => (
<div className={`card ${CARD_GRADIENT} shadow-xl ${classes}`}>
<div className="p-4">
<img src={pic} className="rounded-xl w-full object-fill min-h-[300px]" />
<img
src={pic}
className="rounded-xl w-full object-fill min-h-[300px]"
alt=""
/>
</div>
<div className="card-body pt-0">
<h2 className="card-title text-secondary">{title}</h2>
@ -500,6 +505,7 @@ export class Main extends Component<Props, State> {
<img
src="/static/assets/images/world_background.svg"
className="bg-top bg-no-repeat bg-contain opacity-20 absolute"
alt=""
/>
<div className="container mx-auto px-4">
<TitleBlock i={this} />

View file

@ -72,7 +72,11 @@ export const Navbar = ({ footer = false }) => (
>
<div className="navbar-start">
<Link className="btn btn-ghost normal-case text-xl" to="/">
<img src="/static/assets/images/lemmy.svg" className="h-12 w-12" />
<img
src="/static/assets/images/lemmy.svg"
className="h-12 w-12"
alt=""
/>
</Link>
</div>
<div className="navbar-center hidden lg:flex">
@ -111,13 +115,10 @@ export const Navbar = ({ footer = false }) => (
"dropdown-top": footer,
})}
>
<label tabIndex={0} className="btn btn-ghost lg:hidden">
<label className="btn btn-ghost lg:hidden">
<Icon icon="align-right" size={IconSize.Large} />
</label>
<ul
tabIndex={0}
className="menu menu-sm dropdown-content z-[1] p-2 shadow bg-neutral-800 rounded-box w-52 items-center mt-3 "
>
<ul className="menu menu-sm dropdown-content z-[1] p-2 shadow bg-neutral-800 rounded-box w-52 items-center mt-3 ">
<NavLinks />
</ul>
</div>

View file

@ -22,7 +22,7 @@ export class NewsItem extends Component<any, any> {
}
get markdown(): string {
return news_md.find(v => v.title == this.title)?.markdown ?? "";
return news_md.find(v => v.title === this.title)?.markdown ?? "";
}
render() {

View file

@ -18,7 +18,7 @@ interface NewsInfo {
function buildNewsInfoArray(): Array<NewsInfo> {
return news_reversed.map(n => {
let split = n.title.split(" - ");
const split = n.title.split(" - ");
return {
dateStr: split[0],
@ -48,7 +48,11 @@ const TitleBlock = () => (
<div className="pt-16 text-center text-4xl font-bold mb-8">
{title}
<a href="/feed.xml" className="ml-4 inline-block">
<img src="/static/assets/images/rss.svg" width={24} />
<img
src="/static/assets/images/rss.svg"
width={24}
alt={i18n.t("rss_feeds")}
/>
</a>
</div>
);

View file

@ -72,17 +72,11 @@ export const i18n = i18next as i18nTyped;
export { resources };
// https://gist.github.com/hunan-rostomyan/28e8702c1cecff41f7fe64345b76f2ca
export function getLanguageFromCookie(cookies?: string): string | null {
if (cookies == null) {
return null;
}
export function getLanguageFromCookie(cookies?: string): string | undefined {
const key = "lang=";
return (
cookies
.split(";")
return cookies
?.split(";")
.map(c => c.trim())
.filter(cookie => cookie.substring(0, key.length) === key)
.map(cookie => cookie.substring(key.length))[0] || null
);
.map(cookie => cookie.substring(key.length))[0];
}

View file

@ -6,7 +6,7 @@ import markdown_it_ruby from "markdown-it-ruby";
import markdown_it_sub from "markdown-it-sub";
import markdown_it_sup from "markdown-it-sup";
let SHORTNUM_SI_FORMAT = new Intl.NumberFormat("en-US", {
const SHORTNUM_SI_FORMAT = new Intl.NumberFormat("en-US", {
maximumFractionDigits: 1,
//@ts-ignore
notation: "compact",