eslint-plugin-project-structure

repository·main·Indexed 19 days ago

https://github.com/igorkowalski94/eslint-plugin-project-structure

An ESLint plugin for automating the enforcement of project architecture. It provides rules to define and validate folder structures, file composition (including selector order and quantity), and independent module boundaries to prevent massive dependency trees. Version 3.14.3.

Tokens
4.8K
Snippets
14
Records
23
Agent score
67%

What's inside eslint-plugin-project-structure

  1. Overview of eslint-plugin-project-structure

    main

    eslint-plugin-project-structure is an ESLint plugin designed to help developers maintain scalable, consistent, and well-structured projects. It allows you to define and enforce your own project framework by automating the review of:

    • Folder structure: Enforce where specific files or directories should reside.
    • File composition: Define what files should exist within certain directories.
    • Naming conventions: Implement advanced naming rules for files and folders.
    • Module independence: Create and enforce rules for independent modules.
  2. Enforce folder structure consistency

    main

    Use the project-structure/folder-structure rules to validate your project's directory layout. This allows you to:

    • Validate structure: Ensure files and folders only exist where they are explicitly permitted.
    • Regex validation: Enforce naming conventions for files and folders using regex (supports wildcards like * and treats . as a literal character).
    • Enforce existence: Require specific files to exist if another file is present (e.g., requiring a .test.tsx file whenever a .tsx component exists).
    • Handle recursion: Define nested folder structures with depth limits and options to flatten the structure at the final level.
    • Manage path lengths: Set limits on path lengths to prevent overly deep nesting.
    • Inherit names: Allow files/folders to inherit names from their parent directories with optional prefix/suffix or case transformations.

    You can also create a separate configuration file with TypeScript support for these rules.

  3. Control file composition and selector order

    main

    Use the project-structure/file-composition rules to define the internal structure of your files. This allows you to:

    • Validate selectors: Control the presence and quantity of specific code elements including class, function, arrowFunction, type, interface, enum, variable, variableExpression, and propertyDefinition.
    • Prohibit selectors: Prevent certain elements from appearing in specific files (e.g., ensuring **/*.types.ts files only contain interface or type selectors).
    • Enforce order: Define the required order of selectors within a file. This feature supports ESLint's --fix to automatically correct the order.
    • Limit occurrences: Set maximum limits for specific selectors at the root of a file or for specific types of selectors.
    • Enforce single responsibility: Require a maximum of one main component, function, or class per file.
    • Name validation: Use regex to validate the names of selectors and allow them to inherit filenames with optional transformations.

    You can also create a separate configuration file with TypeScript support for these rules.

  4. Create independent modules and control imports

    main

    Use the project-structure/independent-modules rules to prevent massive dependency trees and enforce architectural boundaries. Key capabilities include:

    • Module boundaries: Control exactly what can be imported into a module (e.g., preventing specific types or functions from crossing module boundaries).
    • Import type support: Validates import, require(), import(), jest.mock(), and jest.requireActual(), as well as named and default exports.
    • External dependency control: Disable imports from node_modules for specific modules, with the ability to add exceptions.
    • Path alias support: Automatically detects tsconfig.json settings for path aliases, or allows manual configuration.
    • Built-in resolver: Includes a built-in import resolver that supports common file extensions without requiring additional plugins.
    • Granular rules: Apply detailed rules to large modules, sub-modules, or individual files.

    You can also create a separate configuration file with TypeScript support for these rules.

  5. Explore eslint-plugin-project-structure rules in the Playground

    main

    If you want to test how rules behave before integrating them into your project, you can use the official playground. This is useful for prototyping configurations and understanding rule impact.

    https://github.com/Igorkowalski94/eslint-plugin-project-structure-playground#root
  6. Define folder and file rules

    main

    When defining the structure in the folderStructure rule, you use Rule objects to specify requirements for files and folders.

    • name: The name of the file or folder.
    • enforceExistence: A string or array of strings indicating that this item must exist.
    • children: An array of nested Rule objects defining the sub-structure.
    • NodeType: While not directly in the Rule interface, the rule logic distinguishes between "File" and "Folder" types.
    export interface Rule<T extends string = string> {
      ruleId?: T;
      name?: string;
      enforceExistence?: string[] | string;
      children?: Rule<T>[];
    }
  7. Integrate eslint-plugin-project-structure with ESLint

    main

    To use the plugin, you must register the project-structure plugin and use the projectStructureParser as the language parser. The plugin provides rules to enforce architectural constraints such as folder structure, file composition, and module independence.

    Note that the plugin requires its own parser to analyze the project structure correctly.

    import { projectStructurePlugin, projectStructureParser } from "eslint-plugin-project-structure";
    
    export default [
      {
        files: ["**"],
        languageOptions: { parser: projectStructureParser },
        plugins: {
          "project-structure": projectStructurePlugin,
        },
        rules: {
          "project-structure/folder-structure": ["error", folderStructureConfig],
        },
      },
    ];
  8. Configure the fileComposition rule

    main

    The fileComposition rule is configured using a FileCompositionConfig object. This configuration allows you to define rules for specific files based on patterns, enforce limits on certain types of selectors, and restrict which selectors are allowed in different scopes (file root, file exports, or nested selectors).

    Configuration Structure

    • projectRoot: (Optional) The root directory of the project.
    • regexParameters: (Optional) Parameters for regex processing.
    • filesRules: An array of FileRules objects, where each object defines constraints for files matching a specific filePattern.
    const config: FileCompositionConfig = {
      projectRoot: './',
      filesRules: [
        {
          filePattern: 'src/**/*.ts',
          // ... rules
        }
      ]
    };
  9. Define FileRules for specific file patterns

    main

    Within the filesRules array, you define how files matching a filePattern should be composed. You can enforce strict selector sets, set limits on the number of specific selectors, or define specific rules for how elements are positioned.

    FileRules Options

    • filePattern: A pattern used to match files.
    • allowOnlySpecifiedSelectors: (Optional) A boolean or an AllowOnlySpecifiedSelectors object to restrict which selectors are permitted in fileRoot, fileExport, or nestedSelectors.
    • rootSelectorsLimits: (Optional) An array of RootSelectorLimit objects to restrict the count of specific selectors at the file root.
    • rules: (Optional) An array of Rule objects defining specific constraints for individual selectors.
  10. Restrict allowed selectors with allowOnlySpecifiedSelectors

    main

    Use the allowOnlySpecifiedSelectors option to whitelist specific types of code elements allowed in different parts of a file. You can also provide custom error messages for each selector type.

    AllowOnlySpecifiedSelectors Structure

    • fileRoot: (Optional) Boolean or CustomErrors for selectors at the top level of the file.
    • fileExport: (Optional) Boolean or CustomErrors for selectors that are exported.
    • nestedSelectors: (Optional) Boolean or CustomErrors for selectors nested within other structures.
    • error: (Optional) A mapping of SelectorType to a custom error message string.
    allowOnlySpecifiedSelectors: {
      fileRoot: true,
      fileExport: {
        class: 'Do not export classes from this directory',
        function: 'Use arrow functions instead'
      }
    }
  11. Set limits on root selectors with rootSelectorsLimits

    main

    The rootSelectorsLimits option allows you to control the quantity of specific selector types allowed at the file's root level.

    RootSelectorLimit Structure

    • selector: A SelectorType or an array of SelectorTypes.
    • limit: A number or an object defining max and/or min values.
    rootSelectorsLimits: [
      {
        selector: 'class',
        limit: { max: 2 }
      },
      {
        selector: ['interface', 'type'],
        limit: 5
      }
    ]
  12. Define specific element rules with Rule

    main

    The rules array within FileRules allows for granular control over how specific elements are composed. You can control their position, sorting, and formatting.

    Rule Structure

    • selector: A SelectorType or a VariableExpression.
    • scope: (Optional) The scope to apply the rule to (fileExport, fileRoot, nestedSelectors, or file).
    • positionIndex: (Optional) An object specifying the index and optional sorting ('az' or 'none').
    • filenamePartsToRemove: (Optional) A string or array of strings to strip from the filename.
    • format: (Optional) A string or array of strings defining the expected format.
    rules: [
      {
        selector: 'class',
        scope: 'fileExport',
        positionIndex: { index: 0, sorting: 'az' }
      }
    ]