Skip to content

Commit

Permalink
more rigid linters
Browse files Browse the repository at this point in the history
  • Loading branch information
Mazuh committed Mar 9, 2024
1 parent a40d685 commit 9a36271
Show file tree
Hide file tree
Showing 26 changed files with 2,547 additions and 941 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/*.test.ts
**/*.test.tsx
**/jest.config.ts
30 changes: 21 additions & 9 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@ module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
"eslint:recommended",
"plugin:@typescript-eslint/strict-type-checked",
"plugin:@typescript-eslint/stylistic-type-checked",
"plugin:react-hooks/recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: ["./tsconfig.json", "./tsconfig.node.json"],
tsconfigRootDir: __dirname,
},
plugins: ["react-refresh"],
rules: {
'react-refresh/only-export-components': [
'warn',
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
"@typescript-eslint/no-confusing-void-expression": "off",
"@typescript-eslint/no-misused-promises": "warn",
"@typescript-eslint/no-unnecessary-condition": "off",
},
}
};
3,298 changes: 2,435 additions & 863 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 2",
"test": "jest --ci --coverage --verbose --silent=true",
"test:watch": "jest --silent=true --watch",
"test:debug": "DEBUG_PRINT_LIMIT=9999999 jest --silent=false",
Expand Down Expand Up @@ -57,6 +57,7 @@
"autoprefixer": "^10.4.16",
"babel-plugin-module-resolver": "^5.0.0",
"eslint": "^8.53.0",
"eslint-plugin-react": "^7.34.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
"identity-obj-proxy": "^3.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/components/template/AppPageTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function AppPageTemplate({
target="_blank"
className="flex transition-colors hover:text-foreground/80 text-foreground/60"
aria-label="Official Postmaiden website"
title="Official Postmaiden website"
title="Official Postmaiden website" rel="noreferrer"
>
<FontAwesomeIcon
icon={faGlobe}
Expand Down
6 changes: 3 additions & 3 deletions src/components/theme/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Theme, ThemeProviderContext } from "@/components/theme/useTheme";
import { useEffect, useState } from "react";

export type ThemeProviderProps = {
export interface ThemeProviderProps {
children: React.ReactNode;
defaultTheme?: Theme;
storageKey?: string;
};
}

export function ThemeProvider({
children,
Expand All @@ -14,7 +14,7 @@ export function ThemeProvider({
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
() => (localStorage.getItem(storageKey) as Theme | null) ?? defaultTheme
);

useEffect(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/theme/useTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { createContext, useContext } from "react";

export type Theme = "dark" | "light" | "system";

export type ThemeProviderState = {
export interface ThemeProviderState {
theme: Theme;
setTheme: (theme: Theme) => void;
};
}

const initialState: ThemeProviderState = {
theme: "system",
Expand Down
10 changes: 5 additions & 5 deletions src/components/ui/form-stuff.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { createContext, useContext } from "react";
import { FieldPath, FieldValues, useFormContext } from "react-hook-form";

export type FormFieldContextValue<
export interface FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
> {
name: TName;
};
}

export const FormFieldContext = createContext<FormFieldContextValue>(
{} as FormFieldContextValue
);

export type FormItemContextValue = {
export interface FormItemContextValue {
id: string;
};
}

export const FormItemContext = createContext<FormItemContextValue>(
{} as FormItemContextValue
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const FormControl = forwardRef<
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
? formDescriptionId
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
Expand Down Expand Up @@ -105,7 +105,7 @@ export const FormMessage = forwardRef<
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
const body = error ? String(error.message) : children;

if (!body) {
return null;
Expand Down
3 changes: 1 addition & 2 deletions src/components/ui/input.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { forwardRef } from "react";
import { cn } from "@/lib/utils";

export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>

export const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Progress = forwardRef<
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
style={{ transform: `translateX(-${100 - (value ?? 0)}%)` }}
/>
</ProgressPrimitive.Root>
));
Expand Down
2 changes: 1 addition & 1 deletion src/entities/http-for-dummies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function isRequestingToLocalhost(request: RequestSnapshot): boolean {
}

/** HTTP methods but as constants. */
export const HTTP_METHODS: Array<ProjectRequestSpec["method"]> = [
export const HTTP_METHODS: ProjectRequestSpec["method"][] = [
"GET",
"POST",
"PUT",
Expand Down
4 changes: 2 additions & 2 deletions src/entities/runtime-entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface RequestSnapshot {
readonly url: string;
readonly method: string;
readonly body: string;
readonly headers: Array<{ key: string; value: string }>;
readonly headers: { key: string; value: string }[];
}

/**
Expand All @@ -42,5 +42,5 @@ export interface RequestSnapshot {
export interface ResponseSnapshot {
readonly status: number;
readonly body: string;
readonly headers: Array<{ key: string; value: string }>;
readonly headers: { key: string; value: string }[];
}
8 changes: 5 additions & 3 deletions src/features/project-workspace/ProjectWorkspacePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { Runtime } from "./Runtime";

export function ProjectWorkspacePage() {
const params = useParams();
const projectUuid = validateUuid(params.uuid || "") ? params.uuid : "";
const projectUuid = validateUuid(params.uuid ?? "") ? params.uuid : "";

if (!projectUuid) {
return (
Expand Down Expand Up @@ -166,7 +166,9 @@ function RequestsSpecsList(props: {
function CreateRequestSpecButton() {
const { create } = useRequestsSpecs();

const handleClick = () => create();
const handleClick = () => {
create().catch(console.error);
};

return (
<Button variant="secondary" onClick={handleClick}>
Expand All @@ -185,7 +187,7 @@ function RequestSpecRemovalButton(props: { spec: ProjectRequestSpec }) {

const handleSubmit = (event: SyntheticEvent) => {
event.preventDefault();
remove(props.spec).then(close);
remove(props.spec).then(close).catch(console.error);
};

return (
Expand Down
10 changes: 5 additions & 5 deletions src/features/project-workspace/RequestsSpecsContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ export function RequestsSpecsContextProvider({
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isError, setError] = useState<Error | null>(null);

const loadProject = useCallback(async () => {
const loadProject = useCallback(() => {
setError(null);
retrieveProject(projectUuid)
.then((project) => tap(project, (p) => setProjectName(p.name)))
.then((project) => tap(project, (p) => setSpecs(p.specs)))
.catch((error) => setError(error))
.catch((error: Error) => setError(error))
.finally(() => setIsLoading(false));
}, [projectUuid]);

Expand All @@ -46,15 +46,15 @@ export function RequestsSpecsContextProvider({
setError(null);
return createRequestSpec({ projectUuid })
.then((created) => tap(created, () => loadProject()))
.catch((error) => tap(null, () => setError(error)));
.catch((error: Error) => tap(null, () => setError(error)));
}, [projectUuid, loadProject]);

const patch: RequestsSpecsContextValue["patch"] = useCallback(
async (patching) => {
setError(null);
return patchRequestSpec({ projectUuid, patching })
.then(() => tap(null, () => loadProject()))
.catch((error) => tap(null, () => setError(error)));
.catch((error: Error) => tap(null, () => setError(error)));
},
[projectUuid, loadProject]
);
Expand All @@ -65,7 +65,7 @@ export function RequestsSpecsContextProvider({
setIsLoading(true);
return removeRequestSpec({ projectUuid, removing: removing.uuid })
.then(() => tap(null, () => loadProject()))
.catch((error) => tap(null, () => setError(error)));
.catch((error: Error) => tap(null, () => setError(error)));
},
[projectUuid, loadProject]
);
Expand Down
42 changes: 28 additions & 14 deletions src/features/project-workspace/Runtime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,33 @@ export interface RuntimeProps {

export function Runtime(props: RuntimeProps) {
const { specs, patch } = useRequestsSpecs();
const spec = specs.find((s) => s.uuid === props.specUuid) || null;
const spec = specs.find((s) => s.uuid === props.specUuid) ?? null;

const patchUrlUnsafelly = (patching: { uuid: string; url: string }) => {
patch(patching);
patch(patching).catch(console.error);
};

const patchUrlRef = useRef<typeof patchUrlUnsafelly>(
debounce(patchUrlUnsafelly, 500)
);
const patchUrl = (url: string) =>
patchUrlRef.current({ uuid: spec!.uuid, url });
spec ? patchUrlRef.current({ uuid: spec.uuid, url }) : null;

const handleUrlChange = (event: React.FormEvent) => {
const url = (event.target as HTMLInputElement).value;
patchUrl(url);
};

const patchMethod = (method: string) =>
patch({ uuid: spec!.uuid, method: method as ProjectRequestSpec["method"] });
const patchMethod = (method: string) => {
if (!spec) {
throw new Error("Unexpected missing spec.");
}

patch({
uuid: spec.uuid,
method: method as ProjectRequestSpec["method"],
}).catch(console.error);
};

const [runtime, setRuntime] = useState<RuntimeState>({
step: "idle",
Expand Down Expand Up @@ -128,12 +136,16 @@ export function Runtime(props: RuntimeProps) {
}));

const runSpec = async (running: { method: string; url: string }) => {
if (!spec) {
throw new Error("Unexpected missing spec.");
}

const requestInfo: RequestSnapshot = {
url: running.url,
method: running.method,
body: "",
headers: spec!.headers.length
? spec!.headers.filter((h) => h.isEnabled)
headers: spec.headers.length
? spec.headers.filter((h) => h.isEnabled)
: [],
};

Expand Down Expand Up @@ -183,7 +195,7 @@ export function Runtime(props: RuntimeProps) {
const url = (
event.currentTarget.elements.namedItem("url") as HTMLInputElement
).value;
runSpec({ method, url });
runSpec({ method, url }).catch(console.error);
};

return (
Expand Down Expand Up @@ -252,7 +264,8 @@ export function Runtime(props: RuntimeProps) {
<span role="img" aria-label="Idea">
💡
</span>
You're requesting <code>localhost</code>, it can be a classic{" "}
You&apos;re requesting <code>localhost</code>, it can be a
classic{" "}
<Anchor
href="https://stackoverflow.com/a/46505542"
target="_blank"
Expand Down Expand Up @@ -376,7 +389,7 @@ function ResponseContent(props: {

function CollapsibleHeadersList(props: {
heading: string;
headers: Array<{ key: string; value: string }>;
headers: { key: string; value: string }[];
}) {
const thereAreItems = props.headers.length > 0;

Expand Down Expand Up @@ -436,10 +449,11 @@ function CollapsibleHeadersList(props: {
</DialogHeader>
<p>
Yes, a few headers might be missing. 🤔 Because{" "}
<strong>we don't have servers intercepting</strong> your requests
and responses, so your <strong>browser has rigid control</strong>{" "}
over what the client is running. It implements arbitrary policies
to <strong>inject/override</strong> headers into requests and{" "}
<strong>we don&apos;t have servers intercepting</strong> your
requests and responses, so your{" "}
<strong>browser has rigid control</strong> over what the client is
running. It implements arbitrary policies to{" "}
<strong>inject/override</strong> headers into requests and{" "}
<strong>omit</strong> headers from responses.
</p>
<p>
Expand Down
Loading

0 comments on commit 9a36271

Please sign in to comment.