Skip to content

Commit

Permalink
feat: upgrade linting capabilities (#90)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamalston authored Dec 8, 2024
1 parent f90b92d commit 9cfa232
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 35 deletions.
58 changes: 53 additions & 5 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,20 +1,68 @@
// @ts-check

import { readdirSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';

import eslint from '@eslint/js';
import perfectionist from 'eslint-plugin-perfectionist';
import tseslint from 'typescript-eslint';

const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const baseDir = join(_dirname, 'src');
const internalDirectories = readdirSync(baseDir, {
withFileTypes: true,
}).flatMap((dirent) => (dirent.isDirectory() ? dirent.name : []));

export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.strict,
tseslint.configs.stylistic,
{ ignores: ['build', 'deprecated'] },
tseslint.configs.strictTypeChecked,
tseslint.configs.stylisticTypeChecked,
perfectionist.configs['recommended-natural'],
{ ignores: ['build', 'coverage', 'deprecated'] },
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: _dirname,
},
},
rules: {
eqeqeq: 'error',
'@typescript-eslint/no-empty-function': [
'@typescript-eslint/no-unnecessary-condition': 'off',
'perfectionist/sort-imports': [
'error',
{ allow: ['arrowFunctions'] },
{
customGroups: {
value: {
react: ['^react$', '^react-.+'],
},
},
groups: [
'react',
'type',
'builtin',
'external',
'internal-type',
'internal',
['parent-type', 'sibling-type', 'index-type'],
['parent', 'sibling', 'index'],
'object',
'unknown',
],
internalPattern: [`^(${internalDirectories.join('|')})(/|$)`],
sortSideEffects: true,
},
],
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-jsx-props': 'off',
'perfectionist/sort-modules': 'off',
'perfectionist/sort-objects': 'off',
},
},
{
files: ['**/*.mjs'],
extends: [tseslint.configs.disableTypeChecked],
},
);
51 changes: 43 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"eslint": "^9.16.0",
"eslint-plugin-perfectionist": "^4.2.0",
"husky": "^9.1.7",
"jest-canvas-mock": "^2.5.2",
"lint-staged": "^15.2.10",
Expand Down
7 changes: 4 additions & 3 deletions src/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useEffect, useState } from 'react';

import { Buttons, Content, Footer, Particles, Toggle } from 'components';

import './App.scss';
import { AppProvider } from './AppContext';
import { Buttons, Content, Footer, Particles, Toggle } from 'components';
import { config } from './config';

export const App = () => {
Expand All @@ -11,9 +12,9 @@ export const App = () => {

const init = () => {
if (
window.matchMedia(
matchMedia(
'(max-device-width: 820px) and (-webkit-min-device-pixel-ratio: 2)',
)?.matches
).matches
) {
setIsMobile(true);
}
Expand Down
8 changes: 5 additions & 3 deletions src/App/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface AppContextInterface extends AppProviderInterface {
const actions = { SET_THEME: 'SET_THEME' } as const;

interface AppAction {
type: 'SET_THEME';
type: typeof actions.SET_THEME;
value: string;
}

Expand All @@ -26,7 +26,7 @@ const initialState: AppState = {
config: {} as Config,
isMobile: false,
theme: themes.dark,
setTheme: () => {},
setTheme: () => undefined,
};

export const reducer = (state: AppState, action: AppAction): AppState => {
Expand Down Expand Up @@ -58,7 +58,9 @@ export const AppProvider = ({
config: state.config,
isMobile: state.isMobile,
theme: state.theme,
setTheme: (value) => dispatch({ type: actions.SET_THEME, value }),
setTheme: (value) => {
dispatch({ type: actions.SET_THEME, value });
},
};

return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
Expand Down
2 changes: 1 addition & 1 deletion src/App/config.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Config } from 'types';
import { Email, GitHub, LinkedIn, Resume } from 'icons';
import { Config } from 'types';

export const config: Config = {
name: {
Expand Down
23 changes: 13 additions & 10 deletions src/Index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { configure, fireEvent, render, screen } from '@testing-library/react';
import { act } from 'react';

import { configure, fireEvent, render, screen } from '@testing-library/react';

import '__mocks__/matchMedia';
import { App } from 'App/App';
import { AppProvider, reducer } from 'App/AppContext';
import { Footer } from 'components';
import { themes } from 'appearance';
import { Footer } from 'components';

configure({ testIdAttribute: 'data-v2' });

Expand All @@ -25,12 +26,12 @@ const mockState = {
},
isMobile: false,
theme: themes.dark,
setTheme: () => {},
setTheme: () => undefined,
};

describe('application tests', () => {
beforeEach(async () => {
await act(async () => render(<App />));
await act(() => render(<App />));
});

/**
Expand Down Expand Up @@ -166,7 +167,7 @@ describe('application tests', () => {

describe('app context tests', () => {
it('should render partial footer on mobile', async () => {
await act(async () =>
await act(() =>
render(
<AppProvider
config={mockState.config}
Expand Down Expand Up @@ -206,7 +207,7 @@ describe('local storage tests', () => {
it("should show the dark theme when 'theme' is set to 'true' in local storage", async () => {
// set local storage item and render the app
localStorage.setItem('theme', 'true');
await act(async () => render(<App />));
await act(() => render(<App />));

// check that the local storage item has been updated correctly
expect(localStorage.getItem('theme')).toEqual('dark');
Expand All @@ -217,7 +218,7 @@ describe('local storage tests', () => {
it("should show the light theme when 'theme' is set to 'false' in local storage", async () => {
// set local storage item and render the app
localStorage.setItem('theme', 'false');
await act(async () => render(<App />));
await act(() => render(<App />));

// check that the local storage item has been updated correctly
expect(localStorage.getItem('theme')).toEqual('light');
Expand All @@ -228,13 +229,15 @@ describe('local storage tests', () => {

// https://testing-library.com/docs/react-testing-library/api/#rerender
it('should persist the light theme through an app re-render', async () => {
const { rerender } = render(<App />);
const { rerender } = await act(() => render(<App />));

expect(localStorage.getItem('theme')).toBeNull();
localStorage.setItem('theme', 'light');

// re-render the app and check the theme
await act(async () => rerender(<App />));
act(() => {
rerender(<App />);
});
const particles = screen.getByTestId('particles');

expect(localStorage.getItem('theme')).toEqual('light');
Expand All @@ -244,7 +247,7 @@ describe('local storage tests', () => {
it('should change local storage value when toggle is clicked', async () => {
// set local storage item and render the app
localStorage.setItem('theme', 'light');
await act(async () => render(<App />));
await act(() => render(<App />));

// click the toggle
const toggle = screen.getByTestId('toggle');
Expand Down
4 changes: 2 additions & 2 deletions src/__mocks__/matchMedia.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
value: (query: string) => ({
matches: false,
media: query,
onchange: null,
Expand All @@ -9,5 +9,5 @@ Object.defineProperty(window, 'matchMedia', {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
}),
});
1 change: 1 addition & 0 deletions src/components/Buttons.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useContext } from 'react';

import styled from 'styled-components';

import { AppContext } from 'App/AppContext';
Expand Down
1 change: 1 addition & 0 deletions src/components/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useContext } from 'react';

import styled, { css } from 'styled-components';

import { AppContext } from 'App/AppContext';
Expand Down
1 change: 1 addition & 0 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useContext } from 'react';

import styled from 'styled-components';

import { AppContext } from 'App/AppContext';
Expand Down
3 changes: 2 additions & 1 deletion src/components/Particles.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useContext } from 'react';
import styled from 'styled-components';
import ReactParticles from 'react-tsparticles';

import styled from 'styled-components';

import { AppContext } from 'App/AppContext';
import { options } from 'appearance';
import { Theme } from 'types';
Expand Down
1 change: 1 addition & 0 deletions src/components/Toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useContext } from 'react';

import styled from 'styled-components';

import { AppContext } from 'App/AppContext';
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createRoot } from 'react-dom/client';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

import { App } from 'App/App';

Expand Down
2 changes: 1 addition & 1 deletion src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
import '@testing-library/jest-dom';
import 'jest-canvas-mock';

console.error = (message) => {
console.error = (message: string) => {
throw new Error(`Console error: ${message}`);
};
2 changes: 2 additions & 0 deletions src/types/config.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { JSX } from 'react';

export interface Content {
display: string;
}
Expand Down

0 comments on commit 9cfa232

Please sign in to comment.