### Install eslint-plugin-nextfriday Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Install the plugin as a development dependency using npm, yarn, or pnpm. ```bash npm install --save-dev eslint-plugin-nextfriday ``` ```bash yarn add --dev eslint-plugin-nextfriday ``` ```bash pnpm add -D eslint-plugin-nextfriday ``` -------------------------------- ### Install ESLint Plugin Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CONSTANT_CASE.md Commands to install the `eslint-plugin-nextfriday` package using pnpm, npm, or yarn. ```bash pnpm add -D eslint-plugin-nextfriday eslint # npm install --save-dev eslint-plugin-nextfriday eslint # yarn add --dev eslint-plugin-nextfriday eslint ``` -------------------------------- ### Clone Repository and Install Dependencies Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Clone the repository locally and install project dependencies using pnpm. ```bash git clone https://github.com/YOUR_USERNAME/eslint-plugin-nextfriday.git cd eslint-plugin-nextfriday pnpm install ``` -------------------------------- ### Install eslint-plugin-nextfriday Source: https://context7.com/next-friday/eslint-plugin-nextfriday/llms.txt Install the plugin as a development dependency using npm, yarn, or pnpm. ```bash # npm npm install --save-dev eslint-plugin-nextfriday ``` ```bash # yarn yarn add --dev eslint-plugin-nextfriday ``` ```bash # pnpm pnpm add -D eslint-plugin-nextfriday ``` -------------------------------- ### Install Agent Skill for AI Coding Assistants Source: https://context7.com/next-friday/eslint-plugin-nextfriday/llms.txt Install the Agent Skill to teach AI coding assistants all 56 rules, ensuring generated code is compliant before ESLint runs. The skill lives in `skills/eslint-plugin-nextfriday/`. ```bash npx skills add next-friday/eslint-plugin-nextfriday --skill eslint-plugin-nextfriday ``` -------------------------------- ### Verify Development Setup Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Run tests and build the project to ensure the development environment is set up correctly. ```bash pnpm test pnpm build ``` -------------------------------- ### Install and Run ESLint Plugin Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Install the plugin and run ESLint in read-only mode to survey violations before making changes. Use `--no-fix` to prevent automatic modifications. ```bash pnpm add -D eslint-plugin-nextfriday eslint pnpm eslint . --no-fix ``` -------------------------------- ### Hook and Service File Naming Conventions Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/skills/eslint-plugin-nextfriday/naming-conventions.md Functions in *.hook.ts files must start with 'use'. Async functions in *.service.ts files must start with 'fetch'. ```typescript // user.hook.ts function useUserProfile() { /* ... */ } // user.service.ts async function fetchUserProfile() { /* ... */ } ``` -------------------------------- ### Incorrect Hook Naming Examples Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_HOOK_NAMING.md These examples show functions in hook files that do not start with the required `use` prefix. ```typescript // search-params.hook.ts export function searchParamsHandler() {} export default handleSearch; // auth.hook.ts export const authManager = () => {}; export default function customHook() {} ``` -------------------------------- ### Correct Hook Naming Examples Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_HOOK_NAMING.md These examples demonstrate the correct usage of the `use` prefix for functions in custom hook files, ensuring compliance with React's conventions. ```typescript // search-params.hook.ts export function useSearchParamsHandler() {} export default useSearchParamsHandler; // auth.hook.ts export const useAuthManager = () => {}; export default function useCustomHook() {} ``` -------------------------------- ### Conventional Commits Examples Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Illustrative examples of commit messages adhering to the Conventional Commits specification. ```text feat(rule): add no-emoji rule fix(config): correct base configuration exports docs(readme): update installation instructions test(rule): improve jsx-pascal-case test coverage ``` -------------------------------- ### Manual ESLint Rule Configuration Source: https://context7.com/next-friday/eslint-plugin-nextfriday/llms.txt Configure ESLint rules manually by importing the plugin and specifying rules in the 'rules' object. This example shows a comprehensive setup for various categories. ```javascript // eslint.config.mjs import nextfriday from "eslint-plugin-nextfriday"; export default [ { plugins: { nextfriday }, rules: { // Variable Naming "nextfriday/no-single-char-variables": "error", "nextfriday/no-lazy-identifiers": "error", "nextfriday/boolean-naming-prefix": "error", "nextfriday/enforce-camel-case": "error", "nextfriday/enforce-constant-case": "error", "nextfriday/enforce-property-case": "error", "nextfriday/no-misleading-constant-case": "error", // File Naming "nextfriday/file-kebab-case": "error", "nextfriday/jsx-pascal-case": "error", // Code Style "nextfriday/no-emoji": "error", "nextfriday/prefer-destructuring-params": "error", "nextfriday/prefer-function-declaration": "error", "nextfriday/require-explicit-return-type": "error", "nextfriday/no-complex-inline-return": "error", "nextfriday/no-logic-in-params": "error", "nextfriday/enforce-hook-naming": "error", "nextfriday/enforce-service-naming": "error", "nextfriday/enforce-sorted-destructuring": "error", "nextfriday/no-env-fallback": "error", "nextfriday/no-inline-default-export": "error", "nextfriday/no-direct-date": "error", "nextfriday/newline-after-multiline-block": "error", "nextfriday/newline-before-return": "error", "nextfriday/no-inline-nested-object": "error", "nextfriday/no-inline-return-properties": "error", "nextfriday/prefer-async-await": "error", "nextfriday/enforce-curly-newline": "error", "nextfriday/no-nested-ternary": "error", "nextfriday/prefer-guard-clause": "error", // Import Optimization "nextfriday/no-relative-imports": "error", "nextfriday/prefer-import-type": "error", "nextfriday/prefer-react-import-types": "error", "nextfriday/sort-exports": "error", "nextfriday/sort-imports": "error", // Type Patterns "nextfriday/enforce-type-declaration-order": "error", "nextfriday/no-nested-interface-declaration": "error", "nextfriday/prefer-named-param-types": "error", "nextfriday/prefer-inline-literal-union": "error", "nextfriday/prefer-inline-type-export": "error", "nextfriday/prefer-interface-over-inline-types": "error", "nextfriday/sort-type-alphabetically": "error", "nextfriday/sort-type-required-first": "error", // React/JSX "nextfriday/jsx-newline-between-elements": "error", "nextfriday/jsx-no-inline-object-prop": "error", "nextfriday/jsx-no-newline-single-line-elements": "error", "nextfriday/jsx-no-non-component-function": "error", "nextfriday/jsx-no-ternary-null": "error", "nextfriday/jsx-no-variable-in-callback": "error", "nextfriday/jsx-require-suspense": "error", "nextfriday/jsx-simple-props": "error", "nextfriday/jsx-sort-props": "error", "nextfriday/jsx-spread-props-last": "error", "nextfriday/prefer-jsx-template-literals": "error", "nextfriday/react-props-destructure": "error", "nextfriday/enforce-props-suffix": "error", "nextfriday/enforce-readonly-component-props": "error", // Next.js "nextfriday/nextjs-require-public-env": "error", }, }, ]; ``` -------------------------------- ### Essential Development Commands Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Common commands for development, including installing dependencies, building, testing, linting, formatting, and type checking. ```bash # Development pnpm install # Install dependencies pnpm build # Build the plugin using tsup pnpm test # Run Jest tests pnpm test:coverage # Run tests with coverage report pnpm test:watch # Run tests in watch mode # Code Quality pnpm eslint # Lint and fix code in src/ pnpm eslint:check # Lint without fixing pnpm prettier # Format all files pnpm prettier:check # Check formatting without fixing pnpm typecheck # TypeScript type checking # Release Management pnpm changeset # Create a changeset for versioning ``` -------------------------------- ### ESLint Rule Tester Setup Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Boilerplate code for setting up RuleTester from @typescript-eslint/rule-tester to write ESLint rule tests. ```typescript import { RuleTester } from "@typescript-eslint/rule-tester"; import { afterAll, describe, it } from "@jest/globals"; import yourRuleName from "../your-rule-name"; RuleTester.afterAll = afterAll; RuleTester.it = it; RuleTester.itOnly = it.only; RuleTester.describe = describe; const ruleTester = new RuleTester({ languageOptions: { parser: require("@typescript-eslint/parser"), parserOptions: { ecmaVersion: 2020, sourceType: "module", }, }, }); ruleTester.run("your-rule-name", yourRuleName, { valid: [ // Valid test cases ], invalid: [ // Invalid test cases ], }); ``` -------------------------------- ### Correct JSX Prop Ordering Example Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/JSX_SORT_PROPS.md This example shows the correct ordering of JSX props according to the rule, grouping them by value type for better readability. This includes string literals, hyphenated strings, numbers, expressions, inline objects, functions, JSX elements, and shorthand booleans. ```tsx handleClick()} icon={} disabled /> ``` -------------------------------- ### Severity Configuration Example Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CONSTANT_CASE.md Illustrates the correct and incorrect ways to configure the rule's severity in ESLint flat config, highlighting that no options are supported. ```javascript // Correct "nextfriday/enforce-constant-case": "error" // Won't apply — there are no options to override "nextfriday/enforce-constant-case": ["error", { allowCamelCase: true }] ``` -------------------------------- ### Correct: Blank line before and after multiline statement Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NEWLINE_AFTER_MULTILINE_BLOCK.md This example demonstrates the correct usage with blank lines before and after multi-line statements. ```typescript const apiClient = axios.create({ baseURL: process.env.API_URL, timeout: 5000, }); const fetchUsers = () => apiClient.get("/users"); ``` ```typescript const userSchema = z.object({ name: z.string().min(1), email: z.string().email(), age: z.number().positive(), }); const validateUser = (data: unknown) => userSchema.parse(data); ``` ```typescript function calculateTotal(items: CartItem[]): number { return items.reduce((sum, item) => sum + item.price * item.quantity, 0); } function formatCurrency(amount: number): string { return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(amount); } ``` ```typescript const pathname = "/categories/electronics"; const nestedCategory = CATEGORIES.flatMap((category) => category.children ?? []).find( (child) => child.href === pathname, ); ``` -------------------------------- ### Correct Chain of Dependencies Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_TYPE_DECLARATION_ORDER.md This example demonstrates a correct chain of dependencies where each type is declared after the type that references it, ensuring a top-down reading flow. ```typescript interface Parent { child: Child; } interface Child { grandchild: Grandchild; } interface Grandchild { value: string; } ``` -------------------------------- ### Correct: Newline before return Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NEWLINE_BEFORE_RETURN.md This example demonstrates the correct usage with a blank line before the return statement, enhancing readability. ```typescript function findUserById(users: User[], id: string): User | null { const user = users.find((user) => user.id === id); return user ?? null; } ``` ```typescript function processData(data: RawData): ProcessedData { const validated = validateData(data); const transformed = transformData(validated); return transformed; } ``` ```typescript function getNodeValue(node: ASTNode): ASTNode { if (node.type === "Expression") { return getNodeValue(node.expression); } return node; } ``` -------------------------------- ### Correct Multi-line If Statement Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CURLY_NEWLINE.md This example shows a correctly formatted multi-line if statement that requires braces. ```ts if (veryLongCondition && anotherCondition) { return []; } ``` -------------------------------- ### Correct Import Order: Type Imports Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/SORT_IMPORTS.md Demonstrates that type-only imports can be placed anywhere without affecting the import order. This example highlights the flexibility with type imports. ```ts // Good: Type imports can appear anywhere import type { FC } from "react"; import { foo } from "../foo"; import React from "react"; ``` -------------------------------- ### Correct Single-line If Statements Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CURLY_NEWLINE.md These examples show correctly formatted single-line if statements without braces. ```ts if (!data) return []; if (x > 0) doSomething(); ``` -------------------------------- ### React Component Example (`UserProfile.tsx`) Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/skills/eslint-plugin-nextfriday/complete-examples.md A React component demonstrating type imports, absolute paths, props interfaces, readonly wrappers, and structured export. Use this as a template for creating new React components. ```tsx import type { ReactElement } from "react"; import type { User } from "@/types/user"; interface UserProfileProps { email: string; name: string; onSave: (user: User) => void; avatar?: string; bio?: string; } interface UserAvatarProps { alt: string; src: string; } function UserAvatar(props: Readonly): ReactElement { const { alt, src } = props; return {alt}; } function UserProfile(props: Readonly): ReactElement { const { avatar, bio, email, name, onSave } = props; const isComplete = Boolean(avatar && bio); const displayName = `${name} (${email})`; const handleSave = (): void => { const user = { avatar, bio, email, name }; onSave(user); }; return (

{displayName}

{bio}

{isComplete ? ( ) : (

{`Please complete your profile, ${name}`}

)}
); } export default UserProfile; ``` -------------------------------- ### Require 'use' prefix in *.hook.ts files Source: https://context7.com/next-friday/eslint-plugin-nextfriday/llms.txt Ensures all exported functions in files named `.hook.ts` or `.hooks.ts` start with `use`. ```typescript // useUser.hook.ts // ❌ Error export function getUser(): User {} export const fetchData = (): Data => {}; // ✅ OK export function useUser(): User {} export const useFetchData = (): Data => {}; export default function useAuthState(): AuthState {} ``` -------------------------------- ### Correct Interface Declaration Order Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_TYPE_DECLARATION_ORDER.md This example illustrates the correct declaration order for interfaces, with the consumer (Foo) declared before its dependency (Baz). ```typescript interface Foo { bar: Baz; } interface Baz { baz: string; } ``` -------------------------------- ### Correct Export Order: All Groups Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/SORT_EXPORTS.md This example shows the correct ordering of all three export groups: external/alias re-export, relative re-export, and local export. ```typescript // Good: All 3 groups in correct order export { foo } from "@/lib/foo"; export { bar } from "../bar"; export { baz }; ``` -------------------------------- ### Install Agent Skill for ESLint Plugin NextFriday Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Use this command to add the Agent Skill for ESLint Plugin NextFriday, enabling AI coding assistants to automatically adhere to the plugin's rules. ```bash npx skills add next-friday/eslint-plugin-nextfriday --skill eslint-plugin-nextfriday ``` -------------------------------- ### Correct Boolean Variable Declarations Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/BOOLEAN_NAMING_PREFIX.md Examples of boolean variables and parameters that adhere to the naming convention with appropriate prefixes. ```typescript const isValid = true; const hasUser = false; const isOpen = true; const isClosed = false; const isActive: boolean = true; const isEnabled: boolean = checkEnabled(); const isEqual = a === b; const isDifferent = a !== b; const isBigger = a > b; const isNegated = !value; const areCombined = a && b; const hasEither = a || b; function process(isActive: boolean) {} const fn = (hasAccess: boolean) => {}; function toggle(isActive = true) {} ``` -------------------------------- ### Correct: Interface Props With Readonly<> Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_READONLY_COMPONENT_PROPS.md This example shows the correct way to define interface-based props using the Readonly<> wrapper. ```tsx interface Props { children: ReactNode; } const Component = (props: Readonly) =>
{props.children}
; ``` -------------------------------- ### Correct Service Function Naming Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_SERVICE_NAMING.md These examples show functions that adhere to the naming conventions. Non-async or non-exported functions are not subject to these restrictions. ```typescript // profile.service.ts export async function getArticles() {} export async function fetchArticles() {} export async function searchArticles(query: string) {} export async function createOrder(data: OrderRequest) {} export async function updateProfile(id: string, data: ProfileRequest) {} export async function removeComment(id: string) {} export async function verifyEmail(token: string) {} ``` ```typescript // Non-async functions can use any prefix export function setLocalState(value: string) {} ``` ```typescript // Non-exported functions are not checked async function handleInternal() {} ``` -------------------------------- ### Combine Presets with Community Plugins Source: https://context7.com/next-friday/eslint-plugin-nextfriday/llms.txt Integrate rules from bundled community plugins like sonarjs and unicorn by spreading their configuration arrays into your ESLint config. This allows for a comprehensive linting setup. ```javascript // eslint.config.mjs import nextfriday from "eslint-plugin-nextfriday"; export default [ nextfriday.configs["react/recommended"], ...nextfriday.configs.sonarjs, // eslint-plugin-sonarjs recommended ...nextfriday.configs.unicorn, // eslint-plugin-unicorn recommended (filename-case, prevent-abbreviations and no-null in JSX/TSX off) ]; ``` -------------------------------- ### Correct: Using interface for complex props Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/PREFER_INTERFACE_OVER_INLINE_TYPES.md This example shows the correct way to define complex props using an interface, improving code organization and reusability. ```tsx interface ComponentProps { children: ReactNode; title: string; onClick: () => void; } const Component = (props: ComponentProps) => (

{props.title}

{props.children}
); ``` -------------------------------- ### Utility File Example (`user-service.ts`) Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/skills/eslint-plugin-nextfriday/complete-examples.md A utility file demonstrating kebab-case naming, type imports, SCREAMING_SNAKE_CASE constants, function declarations, explicit return types, destructured parameters, and async/await patterns. Use this as a template for creating new utility functions. ```ts import type { User } from "@/types/user"; const API_BASE_URL = "https://api.example.com"; const MAX_RETRIES = 3; async function fetchUser({ userId }: FetchUserParams): Promise { const url = `${API_BASE_URL}/users/${userId}`; const response = await fetch(url); const data = await response.json(); return data; } async function fetchUsers(): Promise { const url = `${API_BASE_URL}/users`; const response = await fetch(url); const data = await response.json(); return data; } interface FetchUserParams { userId: string; } export { fetchUser, fetchUsers }; ``` -------------------------------- ### Allow compact self-closing components Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/JSX_NO_NEWLINE_SINGLE_LINE_ELEMENTS.md This example demonstrates correct usage with self-closing JSX components that are grouped together without empty lines. ```tsx
``` -------------------------------- ### Correct Multi-Line Nested Object Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NO_INLINE_NESTED_OBJECT.md This example shows the correct way to format nested objects with multiple properties by spanning them across multiple lines. ```typescript const config = { database: { host: "localhost", port: 5432, name: "myapp", }, }; ``` ```typescript const routes = { api: { users: "/api/users", posts: "/api/posts", }, auth: { login: "/auth/login", logout: "/auth/logout", }, }; ``` -------------------------------- ### Correct: Inline Export of Types Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/PREFER_INLINE_TYPE_EXPORT.md This example demonstrates the correct way to export types and interfaces using inline exports directly at the declaration site. This improves readability by associating the export with the declaration. ```typescript export interface ButtonProps { label: string; disabled: boolean; } export type Theme = "light" | "dark"; ``` ```typescript export interface Config { port: number; } ``` -------------------------------- ### Ban misleading async function prefixes in *.service.ts files Source: https://context7.com/next-friday/eslint-plugin-nextfriday/llms.txt Async functions exported from `.service.ts` files must not start with `delete`, `do`, `handle`, or `set`. Use semantically accurate alternatives. ```typescript // user.service.ts // ❌ Error (banned prefix → suggested alternative) export async function deleteUser(): Promise {} // → removeUser / archiveUser export async function doProcess(): Promise {} // → submitProcess / processData export async function handleRequest(): Promise {} // → createRequest / verifyRequest export async function setData(): Promise {} // → updateData / saveData / patchData // ✅ OK export async function removeUser(): Promise {} export async function fetchUsers(): Promise {} export async function updateProfile(): Promise {} ``` -------------------------------- ### Configure Recommended React Preset with Specific Files Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Apply the `react/recommended` preset to specific files or directories while using a different configuration for the rest of the project. This allows for gradual adoption of stricter rules. ```javascript import nextfriday from "eslint-plugin-nextfriday"; export default [ nextfriday.configs.react, { files: ["src/components/v2/**/*.{ts,tsx}", "src/lib/**/*.ts"], ...nextfriday.configs["react/recommended"], }, ]; ``` -------------------------------- ### Incorrect JSX Prop Ordering Examples Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/JSX_SORT_PROPS.md These examples demonstrate common violations of the JSX prop ordering rule, where props are not sorted by their value type. ```tsx ``` ```tsx {}} count={42} /> ``` ```tsx ``` -------------------------------- ### Correct: Simple inline types allowed Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/PREFER_INTERFACE_OVER_INLINE_TYPES.md This example demonstrates that simple inline types with two or fewer properties are permitted, adhering to the rule's flexibility. ```tsx const Component = (props: { children: ReactNode }) =>
{props.children}
; ``` ```tsx const Component = (props: { title: string; onClick: () => void }) =>
{props.title}
; ``` -------------------------------- ### Layering ESLint Presets by Directory Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Apply different rule severities to specific directories by stacking ESLint configuration objects. This example demonstrates ignoring certain files, applying a base React preset, promoting component/hook directories to a recommended tier, enforcing specific rules in utility directories, and disabling rules for test files. ```javascript import nextfriday from "eslint-plugin-nextfriday"; export default [ { ignores: ["src/legacy/**", "dist/**", "build/**", "**/*.generated.ts"], }, nextfriday.configs.react, { files: ["src/components/**/*.{ts,tsx}", "src/hooks/**/*.{ts,tsx}"], ...nextfriday.configs["react/recommended"], }, { files: ["src/utils/**/*.ts", "src/lib/**/*.ts"], rules: { "nextfriday/require-explicit-return-type": "error", "nextfriday/no-relative-imports": "error", }, }, { files: ["**/*.{test,spec}.{ts,tsx}"], rules: { "nextfriday/require-explicit-return-type": "off", "nextfriday/no-single-char-variables": "off", "nextfriday/no-direct-date": "off", }, }, ]; ``` -------------------------------- ### Correct Import Order: Non-Contiguous Imports Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/SORT_IMPORTS.md Illustrates that non-contiguous imports, separated by other statements, are checked independently. This example shows how the rule handles imports separated by code. ```ts // Good: Non-contiguous imports are independent import React from "react"; const x = 1; import { foo } from "../foo"; ``` -------------------------------- ### Run Tests Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Commands to run all tests, run tests with coverage, or run tests in watch mode. ```bash pnpm test # Run all tests pnpm test:coverage # Run with coverage report pnpm test:watch # Run in watch mode ``` -------------------------------- ### Create New Rule Documentation File Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Command to create a new Markdown file for documenting an ESLint rule. ```bash # Create documentation touch docs/rules/YOUR_RULE_NAME.md ``` -------------------------------- ### Incorrect Constant Declarations Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CONSTANT_CASE.md Examples of global constants that violate the SCREAMING_SNAKE_CASE convention. ```typescript const defaultCover = "/images/default.jpg"; const pageLimit = 10; const apiBaseUrl = "https://api.example.com"; const template = `hello world`; const phoneRegex = /^[0-9]{10}$/; const default_theme = "dark"; export const categories = [{ id: "1" }] as const; ``` -------------------------------- ### Correct JSX: Newlines Between Multi-line Elements Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/JSX_NEWLINE_BETWEEN_ELEMENTS.md This example demonstrates correct JSX formatting with empty lines inserted between multi-line sibling elements to enhance visual separation. ```jsx function Dashboard() { return (
); } ``` ```jsx function UserProfile() { return (
); } ``` -------------------------------- ### Configure React Preset with NextFriday Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Import and apply the `react` configuration preset from the `eslint-plugin-nextfriday` in your ESLint configuration file. ```javascript import nextfriday from "eslint-plugin-nextfriday"; export default [nextfriday.configs.react]; ``` -------------------------------- ### Incorrect: Unsorted Optional Properties Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/SORT_TYPE_ALPHABETICALLY.md This example demonstrates an interface with optional properties that are not in alphabetical order. ```typescript // Bad: Optional not sorted A-Z interface Props { b?: number; a?: string; } ``` -------------------------------- ### Incorrect: Unsorted Required Properties Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/SORT_TYPE_ALPHABETICALLY.md This example shows an interface where the required properties are not sorted alphabetically. ```typescript // Bad: Required not sorted A-Z interface Props { src: string; alt: string; } ``` -------------------------------- ### Steps to Create a New ESLint Rule Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CLAUDE.md A step-by-step guide for adding a new rule to the ESLint plugin, covering file creation, registration, documentation, README updates, test file updates, and changeset generation. ```plaintext 1. Add rule file: `src/rules/{rule-name}.ts` 2. Create test file: `src/rules/__tests__/{rule-name}.test.ts` 3. Register in `src/index.ts`: - Import the rule - Add to `rules` object - Add to both the warn and recommended variants of the appropriate rule set (e.g., `jsxRules` AND `jsxRecommendedRules`) 4. Create documentation: `docs/rules/{RULE_NAME_UPPERCASE}.md` 5. Update README.md rules table 6. Update `src/__tests__/rules.test.ts`: - Update rule count in "should have exactly N rules" test - Add rule name to "should have correct rule names" test 7. Create a changeset: `pnpm changeset` (required for CI to pass on PRs that change `src/` or `docs/`) ``` -------------------------------- ### Incorrect Import Order: Relative Before External Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/SORT_IMPORTS.md This example shows an incorrect import order where a relative import precedes an external package import. Use this to identify and fix such ordering issues. ```ts // Bad: Relative before external import { foo } from "../foo"; import React from "react"; ``` -------------------------------- ### Create Changeset Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Run this command to generate a changeset file for changes that affect users, which is crucial for the automated release process. ```bash pnpm changeset ``` -------------------------------- ### Incorrect Single-line If Statement Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CURLY_NEWLINE.md This example demonstrates a single-line if statement that incorrectly uses braces, violating the rule. ```ts if (veryLongCondition && anotherCondition) return []; ``` -------------------------------- ### Correct Constant Declarations Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CONSTANT_CASE.md Examples of global constants that adhere to the SCREAMING_SNAKE_CASE convention, including exempt cases. ```typescript const DEFAULT_COVER = "/images/default.jpg"; const PAGE_LIMIT = 10; const API_BASE_URL = "https://api.example.com"; const TEMPLATE = `hello world`; const PHONE_REGEX = /^[0-9]{10}$/; const DEFAULT_THEME = "dark"; export const CATEGORIES = [{ id: "1" }] as const; const SKELETON_ITEMS = [1, 2, 3, 4, 5]; const MAP_STYLE = { height: "320px", width: "100%" }; const STATUS_MAP = { ACTIVE: "active" } as const; // Booleans with standard prefixes (is, has, should, etc.) are exempt const isProduction = true; const hasAccess = false; // Template literals with expressions are dynamic, camelCase is fine const pendingHref = `/branch/${branch.branchNumber}`; // Functions and components are not checked const handleClick = () => {}; const MyComponent = () => {}; // Local scope is not checked function foo() { const maxRetry = 3; } ``` -------------------------------- ### Enable Rule via Preset Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CONSTANT_CASE.md Configuration to enable the rule by including a preset from `eslint-plugin-nextfriday`. ```javascript import nextfriday from "eslint-plugin-nextfriday"; export default [nextfriday.configs["base/recommended"]]; ``` -------------------------------- ### Incorrect Multi-line If Statements Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_CURLY_NEWLINE.md These examples show multi-line if statements that incorrectly use braces, violating the rule. ```ts if (!data) { return []; } if (x > 0) { doSomething(); } ``` -------------------------------- ### Exception for underscore-prefixed variables Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NO_LAZY_IDENTIFIERS.md Variables starting with an underscore are allowed, commonly used for intentionally unused variables. ```ts const _unused = getValue(); ``` -------------------------------- ### Correct Usage of Lazy Components with Suspense Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/JSX_REQUIRE_SUSPENSE.md Shows how to correctly wrap lazy components with `` and provide a fallback UI. This handles loading states gracefully. ```tsx const AsyncComponent = lazy(() => import("./Component")); const LazyModal = React.lazy(() => import("./Modal")); // Wrapped in Suspense with fallback }> // Nested inside Suspense }>
// Multiple lazy components in same Suspense Loading...}> ``` -------------------------------- ### Incorrect Type-Only Imports Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/PREFER_IMPORT_TYPE.md Shows examples of imports that should be converted to 'import type'. These imports are used only for type annotations. ```typescript import { Component } from "react"; import { TSESTree } from "@typescript-eslint/utils"; import { RuleContext } from "@typescript-eslint/utils"; interface Props { component: Component; tree: TSESTree.Node; context: RuleContext; } ``` -------------------------------- ### Correct Destructuring: Sorted Without Defaults Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_SORTED_DESTRUCTURING.md Example of correctly sorted destructured properties without any default values. ```javascript const { a, b, c, d } = foo; ``` -------------------------------- ### Allow Direct Environment Variable Access Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NO_ENV_FALLBACK.md This demonstrates the correct way to access environment variables without fallbacks. It ensures that if an environment variable is missing, the application will fail explicitly, making it easier to identify and resolve configuration issues. ```javascript const apiKey = process.env.API_KEY; const dbUrl = process.env.DATABASE_URL; const port = process.env.PORT; const config = { secret: process.env.SECRET_KEY, region: process.env.AWS_REGION, }; function getToken() { return process.env.AUTH_TOKEN; } if (!process.env.API_KEY) { throw new Error("API_KEY environment variable is required"); } ``` -------------------------------- ### Custom Hook Example (`user-profile.hook.ts`) Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/skills/eslint-plugin-nextfriday/complete-examples.md A custom React hook demonstrating 'use' prefix naming, '.hook.ts' file extension, 'is'/'has' prefixes for boolean state, guard clauses, and sorted interfaces. Use this as a template for creating new custom hooks. ```ts import { useCallback, useEffect, useState } from "react"; import type { User } from "@/types/user"; function useUserProfile({ userId }: UseUserProfileParams): UseUserProfileReturn { const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); const handleFetch = useCallback(async (): Promise => { setIsLoading(true); setHasError(false); const response = await fetch(`/api/users/${userId}`); if (!response.ok) { setHasError(true); setIsLoading(false); return; } const data = await response.json(); setUser(data); setIsLoading(false); }, [userId]); useEffect(() => { handleFetch(); }, [handleFetch]); return { hasError, isLoading, user }; } interface UseUserProfileParams { userId: string; } interface UseUserProfileReturn { hasError: boolean; isLoading: boolean; user: User | null; } export { useUserProfile }; ``` -------------------------------- ### Incorrect Interface: Required After Optional Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/SORT_TYPE_REQUIRED_FIRST.md This example demonstrates an interface where a required property appears after an optional one, which is flagged by the rule. ```typescript interface Props { a: string; b: string; c?: string; d: string; } ``` -------------------------------- ### Development Commands for ESLint Plugin Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CLAUDE.md Common pnpm commands for building, testing, linting, formatting, and managing the ESLint plugin. Use these for development and maintenance tasks. ```bash pnpm build # Build plugin using tsup (dual CJS/ESM output to lib/) pnpm test # Run all tests with Jest (uses @swc/jest transformer) pnpm test src/rules/__tests__/file-kebab-case.test.ts # Run single test pnpm test:coverage # Jest with coverage (run by pre-push hook) pnpm test:watch # Jest watch mode pnpm eslint # Lint and auto-fix pnpm eslint:check # Lint without auto-fix (read-only) pnpm typecheck # Run TypeScript type checking pnpm prettier # Format all files pnpm prettier:check # Verify formatting pnpm sort-package-json # Sort package.json keys pnpm clear # Remove lib/, node_modules/.cache, .eslintcache pnpm changeset # Create a changeset for version bumping ``` -------------------------------- ### Configuration Presets Overview Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CLAUDE.md Details the six available ESLint configuration presets, categorized by rule set tiers (base, react, nextjs) and severity levels (warn, error/recommended). ```plaintext | Preset | Rules | Severity | | ------------------------------- | --------------------------- | ------------ | | `base` / `base/recommended` | 40 base | warn / error | | `react` / `react/recommended` | 40 base + 16 JSX | warn / error | | `nextjs` / `nextjs/recommended` | 40 base + 16 JSX + 1 nextjs | warn / error | ``` -------------------------------- ### Correct Inline Empty Objects and Arrays Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NO_INLINE_NESTED_OBJECT.md Empty nested objects and arrays are permitted to be written inline, as shown in this example. ```typescript const initialState = { data: {}, errors: [], }; ``` -------------------------------- ### Correct Inline Array Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NO_INLINE_NESTED_OBJECT.md This example shows a correct usage where an array with multiple elements is written inline. This is allowed by the rule. ```typescript const validationRules = { required: ["name", "email", "password"], }; ``` -------------------------------- ### Run Project Checks Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Execute these commands to ensure all code quality and test checks pass before committing. ```bash pnpm eslint:check pnpm prettier:check pnpm typecheck pnpm test pnpm build ``` -------------------------------- ### Correct Single-Property Nested Object Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NO_INLINE_NESTED_OBJECT.md This example demonstrates a correct usage where a nested object with only a single property is allowed inline. ```typescript const config = { database: { host: "localhost" }, }; ``` -------------------------------- ### Push to Fork Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Push your feature branch to your origin fork to prepare for creating a pull request. ```bash git push origin feature/your-feature-name ``` -------------------------------- ### Incorrect: Missing newline before return Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NEWLINE_BEFORE_RETURN.md This example shows a function where the return statement is not preceded by a blank line, violating the rule. ```typescript function findUserById(users: User[], id: string): User | null { const user = users.find((user) => user.id === id); return user ?? null; } ``` ```typescript function processData(data: RawData): ProcessedData { const validated = validateData(data); const transformed = transformData(validated); return transformed; } ``` ```typescript function getNodeValue(node: ASTNode): ASTNode { if (node.type === "Expression") { return getNodeValue(node.expression); } return node; } ``` -------------------------------- ### Manual ESLint Configuration Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Example of manual ESLint configuration using the flat config format. This snippet imports the plugin and defines rules for various categories like variable naming, file naming, code style, imports, types, React/JSX, and Next.js specific rules. Requires ESLint 9+. ```javascript import nextfriday from "eslint-plugin-nextfriday"; export default [ { plugins: { nextfriday, }, rules: { // Variable Naming "nextfriday/no-single-char-variables": "error", "nextfriday/no-lazy-identifiers": "error", "nextfriday/boolean-naming-prefix": "error", "nextfriday/enforce-camel-case": "error", "nextfriday/enforce-constant-case": "error", "nextfriday/enforce-property-case": "error", "nextfriday/no-misleading-constant-case": "error", // File Naming "nextfriday/file-kebab-case": "error", "nextfriday/jsx-pascal-case": "error", // Code Style "nextfriday/no-emoji": "error", "nextfriday/prefer-destructuring-params": "error", "nextfriday/prefer-function-declaration": "error", "nextfriday/require-explicit-return-type": "error", "nextfriday/no-complex-inline-return": "error", "nextfriday/no-logic-in-params": "error", "nextfriday/enforce-hook-naming": "error", "nextfriday/enforce-service-naming": "error", "nextfriday/enforce-sorted-destructuring": "error", "nextfriday/no-env-fallback": "error", "nextfriday/no-inline-default-export": "error", "nextfriday/no-direct-date": "error", "nextfriday/newline-after-multiline-block": "error", "nextfriday/newline-before-return": "error", "nextfriday/no-inline-nested-object": "error", "nextfriday/no-inline-return-properties": "error", "nextfriday/prefer-async-await": "error", "nextfriday/enforce-curly-newline": "error", "nextfriday/no-nested-ternary": "error", "nextfriday/prefer-guard-clause": "error", // Import Optimization "nextfriday/no-relative-imports": "error", "nextfriday/prefer-import-type": "error", "nextfriday/prefer-react-import-types": "error", "nextfriday/sort-exports": "error", "nextfriday/sort-imports": "error", // Type Patterns "nextfriday/enforce-type-declaration-order": "error", "nextfriday/no-nested-interface-declaration": "error", "nextfriday/prefer-named-param-types": "error", "nextfriday/prefer-inline-literal-union": "error", "nextfriday/prefer-inline-type-export": "error", "nextfriday/prefer-interface-over-inline-types": "error", "nextfriday/sort-type-alphabetically": "error", "nextfriday/sort-type-required-first": "error", // React/JSX "nextfriday/jsx-newline-between-elements": "error", "nextfriday/jsx-no-inline-object-prop": "error", "nextfriday/jsx-no-newline-single-line-elements": "error", "nextfriday/jsx-no-non-component-function": "error", "nextfriday/jsx-no-ternary-null": "error", "nextfriday/jsx-no-variable-in-callback": "error", "nextfriday/jsx-require-suspense": "error", "nextfriday/jsx-simple-props": "error", "nextfriday/jsx-sort-props": "error", "nextfriday/jsx-spread-props-last": "error", "nextfriday/prefer-jsx-template-literals": "error", "nextfriday/react-props-destructure": "error", "nextfriday/enforce-props-suffix": "error", "nextfriday/enforce-readonly-component-props": "error", // Next.js "nextfriday/nextjs-require-public-env": "error", }, }, ]; ``` -------------------------------- ### Incorrect: Missing blank line before multiline statement Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/NEWLINE_AFTER_MULTILINE_BLOCK.md This example shows a violation where a blank line is missing before a multi-line statement. ```typescript const apiClient = axios.create({ baseURL: process.env.API_URL, timeout: 5000, }); const fetchUsers = () => apiClient.get("/users"); ``` ```typescript const userSchema = z.object({ name: z.string().min(1), email: z.string().email(), age: z.number().positive(), }); const validateUser = (data: unknown) => userSchema.parse(data); ``` ```typescript function calculateTotal(items: CartItem[]): number { return items.reduce((sum, item) => sum + item.price * item.quantity, 0); } function formatCurrency(amount: number): string { return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(amount); } ``` ```typescript const pathname = "/categories/electronics"; const nestedCategory = CATEGORIES.flatMap((category) => category.children ?? []).find( (child) => child.href === pathname, ); ``` -------------------------------- ### Create New Rule File Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md Command to create a new TypeScript file for an ESLint rule implementation. ```bash # Create rule file touch src/rules/your-rule-name.ts ``` -------------------------------- ### Correct: Type Alias Props With Readonly<> Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_READONLY_COMPONENT_PROPS.md This example demonstrates the correct usage of Readonly<> with type aliases for component props. ```tsx type ComponentProps = { title: string; onClick: () => void; }; const Component = (props: Readonly) =>
{props.title}
; ``` -------------------------------- ### Print ESLint Configuration for a File Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/README.md Use the `pnpm eslint --print-config` command to inspect the resolved ESLint configuration for a specific file. This is helpful for debugging rule applications. ```bash pnpm eslint --print-config path/to/file.tsx ``` -------------------------------- ### Project Structure Overview Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/CONTRIBUTING.md A hierarchical view of the project's directories and key files, useful for understanding code organization. ```text eslint-plugin-nextfriday/ ├── src/ │ ├── index.ts # Main plugin entry point │ ├── configs.ts # ESLint configuration presets │ ├── rules.ts # Central registry of all rules │ ├── meta.ts # Plugin metadata │ ├── utils.ts # Shared utilities │ └── rules/ │ ├── rule-name.ts # Individual rule implementations │ └── __tests__/ │ └── rule-name.test.ts ├── docs/ │ └── rules/ │ └── RULE_NAME.md # Rule documentation ├── lib/ # Built output (generated) └── .github/ └── workflows/ # CI/CD pipelines ``` -------------------------------- ### Incorrect Prop Type Naming Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/ENFORCE_PROPS_SUFFIX.md Examples of interfaces and type aliases that violate the `Props` suffix rule in component files. ```tsx // Button.tsx interface Button {} interface Card { title: string; } type ButtonType = { disabled: boolean }; ``` -------------------------------- ### Incorrect Boolean Variable Declarations Source: https://github.com/next-friday/eslint-plugin-nextfriday/blob/main/docs/rules/BOOLEAN_NAMING_PREFIX.md Examples of boolean variables and parameters that violate the naming convention by lacking descriptive prefixes. ```typescript const valid = true; const user = false; const open = true; const closed = false; const active: boolean = true; const enabled: boolean = checkEnabled(); const equal = a === b; const different = a !== b; const bigger = a > b; const negated = !value; const combined = a && b; const either = a || b; function process(active: boolean) {} const fn = (enabled: boolean) => {}; function toggle(active = true) {} ```