Skip to content

[FE] Setup Consistent Code Style Environment #171

@vOrigins

Description

@vOrigins

Goal: Establish a consistent and automated code style and quality checking environment for the qubership-apihub-ui project, ensuring adherence across different IDEs and development environments. Extend settings to other APIHUB projects.

Context:
The project currently has code style configurations in:

  • apihub_code_style.xml: JetBrains IDE specific settings.
  • .eslintrc.json: ESLint rules for code quality and some styling.

Proposed Toolchain:

  1. EditorConfig: Define basic editor settings (indentation, line endings, charset) universally across supporting IDEs.
  2. Dprint: A very fast, pluggable formatter written in Rust, offering high configurability. dprint uses WebAssembly plugins for different languages, making it extensible and performant.
  3. ESLint: Focus on code quality rules, identifying potential bugs and enforcing best practices. Integration with dprint is needed to avoid conflicting rules.
  4. Husky + lint-staged: Run linters and formatters automatically on staged files before committing, preventing non-compliant code from entering the repository.
  5. GitHub Super Linter: Integrate into CI/CD pipeline via GitHub Actions to perform comprehensive linting checks on pull requests and pushes.

Pros & Cons of this Stack:

  • Pros:
    • Consistency: Enforces a single style guide across the entire codebase and team.
    • Automation: Reduces manual formatting effort and code review comments related to style.
    • Early Feedback: Catches errors and style issues early in the development process (pre-commit and in IDE).
    • Industry Standard Tools: Many of these tools (EditorConfig, ESLint, Husky) are widely adopted and well-supported.
    • High Configurability: dprint offers extensive configuration options to match project needs precisely.
    • Improved Code Quality: ESLint and Super Linter help identify potential bugs and enforce best practices.
    • Performance: dprint, being written in Rust with WebAssembly plugins, offers significant speed advantages over JavaScript-based formatters.
  • Cons:
    • Setup Complexity: Initial configuration of dprint can be complex due to its high configurability.
    • Potential Conflicts: Requires careful configuration to prevent conflicts between ESLint and dprint.
    • Dependencies: Adds development dependencies to the project for each tool.
    • IDE Integration: dprint's IDE integration may require additional setup compared to more established formatters.
    • Learning Curve: The team may need time to learn dprint's configuration options and adjust to new workflows.
    • Smaller Community: dprint has a smaller community compared to some alternatives, which might mean fewer resources and examples available.
    • Tool Overlap: Using multiple tools that can perform similar tasks requires clear delineation of responsibilities.

Formatter Comparison

Feature Prettier Biome dprint ESLint / Stylistic
Popularity (GitHub Stars) ~60.3k (prettier/prettier) ~20.2k (biomejs/biome) ~3.4k (dprint/dprint) ~25.9k (eslint/eslint) + ~1.1k (@stylistic/eslint-plugin)
Using in popular projects - Meta (React, docusaurus, lexical)
- Google (blockly)
- Angular (angular)
- Vercel (next.js)
- Airbnb (visx)
- Microsoft (TypeScript) - Google (model-viewer)
- Microsoft (vscode, playwright)
Rules Opinionated Opinionated (Prettier-compatible) Highly Configurable Highly Configurable
Rule Configuration Easy (limited options) Easy (limited options) Complex Complex
VSCode Integration Easy (Official Extension) Easy (Official Extension) Medium (Extension exists, CLI setup needed) Easy (ESLint Extension)
IDEA Integration Easy (Built-in or Official Plugin) Easy (Plugin available) Medium (Plugin exists, CLI setup, potential issues) Easy (Built-in ESLint integration)
Pros Popularity, excellent IDE integration Speed, single tool for formatting and linting Speed, highly customizable rules Popularity, excellent IDE integration, single tool for formatting and linting, highly customizable rules, aligns nested ternary operators in a stair-step pattern
Cons Opinionated Opinionated Early development, IDEA extension issues, complex setup, aligns nested ternary operators in a single line Slow performance, complex setup, requires additional unofficial plugins for JSON and YAML formatting (which require manual configuration of all formatting rules from scratch), no plugins available for Markdown formatting

Tool Specifics:

This section details each tool, including its purpose, configuration, known issues, and specific considerations for this project.

1. EditorConfig

  • Purpose: Defines and maintains consistent coding styles (like indent size, line endings, charset) between different editors and IDEs.
  • Configuration:
    • A .editorconfig file is placed at the project root.
    • Example configuration:
      root = true
      
      [*]
      charset = utf-8
      end_of_line = lf
      indent_style = space
      indent_size = 2
      insert_final_newline = true
      trim_trailing_whitespace = true
      
      [*.md]
      trim_trailing_whitespace = false
  • Pros:
    • Simple to set up.
    • Widely supported by most modern IDEs and editors, often built-in.
    • Helps with basic consistency (indentation, line endings, charset).
  • Cons:
    • Limited in scope; only handles basic editor settings, not complex code formatting.
    • Doesn't replace a full code formatter or linter.
  • Specifics/Questions:
    • Should be customized for each repository to avoid unnecessary settings.

2. Code Formatter: Prettier and Alternatives

2.1. Prettier

  • Purpose: An opinionated code formatter that automatically formats code to ensure a consistent style.
  • Configuration:
    • Install: npm install --save-dev --save-exact prettier or yarn add --dev --exact prettier.
    • Configuration file (e.g., .prettierrc.yml) in the qubership-apihub-ui directory.
    • Example based on existing settings:
      # .prettierrc.yml
      printWidth: 120
      semi: false
      singleQuote: true
      trailingComma: "all" # Corresponds to comma-dangle: always-multiline
      bracketSpacing: true
      # tabWidth: 2 (default, aligns with EditorConfig indent_size)
  • Pros:
    • Highly popular and widely adopted, large community.
    • Opinionated nature reduces debates over style.
    • Excellent IDE integration and tool support.
    • Supports many languages out of the box.
    • Automatically fixes formatting issues.
  • Cons:
    • Opinionated: Can be a downside if the team strongly disagrees with Prettier's style choices, as configuration options are intentionally limited.
    • Can sometimes have surprising formatting for complex or edge-case code.
  • Specifics/Issues:
    • GitHub Super Linter is needed as an extra check to ensure code is formatted, in case Husky is bypassed.
    • Modifies Markdown (e.g., changes numbering).
    • When working in a folder with many repos, Prettier must be installed in each repo (unless a monorepo setup with a single top-level dev dependency is used, or global install, which is not recommended for team consistency).
    • YAML/JSON/OpenAPI Formatting:
      • The Prettier plugin in IDEA sometimes does not format all YAML, only parts of it (Husky can solve this by using the CLI).
      • Sometimes hangs on keyboard shortcut formatting in IDEA.
      • There can be differences between formatting YAML with the Prettier IDE plugin and the Prettier CLI; CLI also checks for syntax errors, and Husky can prevent commits with errors.

2.2. Biome (Alternative Formatter)

  • Purpose: A fast, Rust-based tool designed to be an all-in-one solution for web development, including formatting, linting, and more. Aims for Prettier compatibility in formatting.
  • Observation: Formats code similarly to Prettier.
  • Pros:
    • Very fast due to its Rust implementation.
    • Aims to replace multiple tools (Prettier, ESLint, etc.), potentially simplifying the toolchain and dependencies.
    • Good Prettier compatibility for formatting.
    • Can be a single, cohesive tool for linting and formatting.
  • Cons:
    • Newer tool, so the ecosystem and community are smaller than Prettier/ESLint.
    • Might be less flexible or have fewer plugins/integrations for very specific or complex linting needs compared to ESLint.
    • Being an all-in-one tool might be overkill if only formatting is needed.
  • Configuration: Typically configured via a biome.json file.

2.3. dprint (Alternative Formatter)

  • Purpose: A very fast, pluggable code formatting platform written in Rust. Plugins are WebAssembly files.
  • Observations:
    • In early development.
    • Works well via CLI.
    • Has a complex configuration; difficult to setup settings corresponding to apihub_code_style.xml.
    • For IDE extension functionality, it must be installed globally and requires additional setup.
    • The IntelliJ IDEA extension may not work with the latest IDE versions.
  • Pros:
    • Extremely fast formatting.
    • Pluggable architecture (WASM), allowing plugins for various languages.
    • Highly configurable, offering more control over formatting than Prettier.
  • Cons:
    • Configuration can be complex and verbose.
    • Smaller community and ecosystem compared to Prettier.
    • IDE integration can be less seamless or require more setup (e.g., global install, specific LSP configuration).
    • Being "highly configurable" can lead to more time spent on configuration and potential for inconsistencies if not managed well.
  • Configuration: Typically via a dprint.json file.
  • Specifics/Issues: Based on current observations, matching existing styles might be challenging, and IDE support needs careful evaluation.

2.4. ESLint Stylistic Plugin (Alternative/Supplement for Formatting)

  • Purpose: A collection of ESLint plugins and configurations that provide stylistic rules, allowing ESLint to manage code appearance.
  • Observation: Desirable to migrate to ESLint 9 for better support.
  • Pros:
    • Consolidates all linting (code quality and style) within ESLint.
    • Offers fine-grained control over stylistic rules if default formatter behavior is not desired.
    • Official ESLint project, ensuring good integration with the ESLint ecosystem.
  • Cons:
    • Can make ESLint configuration more complex.
    • May be slower for formatting tasks compared to dedicated, optimized formatters like Prettier, Biome, or dprint.
    • Requires careful setup to avoid conflicts if used alongside another formatter (typically, you'd choose one or the other for formatting).
    • The primary focus of ESLint is linting, not formatting; using it for extensive formatting might be less ergonomic.
  • Configuration: Involves adding the relevant @stylistic plugins and configuring rules in the ESLint configuration file.
  • Specifics/Issues: Requires evaluation of ESLint 9 compatibility and how it fits with the overall linting/formatting strategy.

3. ESLint

  • Purpose: A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript and TypeScript. Focuses on code quality, potential bugs, and best practices.
  • Configuration:
    • Typically via an .eslintrc.json (or .js, .yaml) file.
    • Integration with Prettier (or other chosen formatter):
      • Install eslint-config-prettier. This package disables ESLint rules that conflict with the formatter.
      • Add "prettier" (or the equivalent for other formatters) to the end of the extends array in the ESLint config.
    • Keep ESLint config primarily for code quality rules when a dedicated formatter is used.
  • Pros:
    • Excellent for identifying code quality issues, potential bugs, and anti-patterns.
    • Highly extensible with a vast ecosystem of plugins (e.g., for React, TypeScript, accessibility, security).
    • Enforces coding standards and best practices.
    • Can auto-fix many reported issues.
  • Cons:
    • Configuration can become complex, especially with many plugins and custom rules.
    • Can be slower on large codebases if not configured optimally.
    • If not managed, stylistic rules in ESLint can conflict with dedicated formatters.
  • Specifics/Questions:
    • Review the ESLint config and remove unnecessary rules (mainly those related to formatting if a dedicated formatter is adopted).
    • YAML/JSON/OpenAPI Linting:
      • YAML/JSON should be linted with an ESLint plugin (e.g., eslint-plugin-jsonc, eslint-plugin-yml) and skipped in Super Linter.

4. Husky + lint-staged

  • Purpose: Automate running linters and formatters on staged files before committing code.
  • Configuration:
    • Install husky and lint-staged.
    • Initialize Husky: npx husky install.
    • Add prepare script to package.json: "prepare": "husky install".
    • Configure lint-staged (e.g., in .lintstagedrc.yml or package.json):
      # .lintstagedrc.yml example
      "*.{ts,tsx,json}": # Files for ESLint
        - "eslint --fix"
      "*.{js,ts,tsx,json,html,md,yaml,yml}": # Files for Formatter
        - "prettier --write" # Replace 'prettier' with chosen formatter CLI command
    • Create a pre-commit hook in .husky/pre-commit:
      npx lint-staged
  • Pros:
    • Automates code quality checks before commits.
    • Prevents non-compliant code from entering the repository.
    • lint-staged ensures checks run only on changed/staged files, making pre-commit hooks faster.
    • Husky makes Git hooks shareable across the team.
  • Cons:
    • Adds development dependencies.
    • Can be bypassed by users (e.g., using git commit --no-verify).
    • Relies on npm install (or equivalent) running scripts to set up Husky.
  • Specifics/Notes:
    • The alternative of using local Git hooks directly in .git/hooks/ is generally not recommended for team collaboration.
    • CI Considerations: If pre-commit checks need to be disabled in CI, use npm install --ignore-scripts or modify lint-staged config for the CI environment (e.g., echo "Skipping checks"). This is reflected in the lint-staged in CI point below.
    • lint-staged in CI: The note npm install --ignore-scripts or echo "Skipping checks" or ":" (as a no-op command) for CI environments is important.

5. GitHub Super Linter

  • Purpose: A GitHub Action that lints code using a variety of linters, providing a comprehensive check in the CI/CD pipeline.
  • Configuration:
    • Create a GitHub Actions workflow file (e.g., .github/workflows/super-linter.yaml).
    • Use the super-linter/super-linter action.
    • Configure it to use the project's existing configuration files by ensuring they are present in the checkout and potentially setting LINTER_RULES_PATH if configs are not in the root.
    • Set environment variables as needed (e.g., VALIDATE_ALL_CODEBASE=false to lint only changed files in PRs, FILTER_REGEX_INCLUDE/EXCLUDE to control which files are linted, VALIDATE_YAML, VALIDATE_JSON, etc. to enable/disable specific linters).
  • Pros:
    • Consolidates many linters into a single CI step.
    • Supports a wide range of languages and file types.
    • Helps catch issues that might be missed by local pre-commit hooks.
    • Enforces consistency at the Pull Request level.
  • Cons:
    • Can be slow if many linters are enabled or if linting the entire codebase.
    • Initial setup might produce a lot of noise (many linting errors) if the codebase isn't already clean.
    • Requires careful configuration to enable only relevant linters and to align with project standards.
  • Specifics/Questions:
    • No direct conflicts found with Prettier (unless Super Linter's Prettier is configured differently or used to re-check formatting).
    • Only necessary linters should be enabled.
    • ESLint (with its specific project config) should be moved to a separate workflow (possibly one of the existing ones for tests/builds)? This ensures the project's exact ESLint setup is run in CI.

Existing ESLint Style/Formatting Rules Analysis:

The following rules in the current .eslintrc.json relate to code style and formatting:

  • comma-dangle: ["error", {"arrays": "always-multiline", ...}] - Enforces trailing commas on multiline structures.
  • eol-last: ["error", "always"] - Enforces a newline at the end of files.
  • quotes: ["error", "single"] - Enforces single quotes.
  • semi: ["error", "never"] - Disallows semicolons.
  • @typescript-eslint/member-delimiter-style: ["error", {"multiline": {"delimiter": "none"}}] - Disallows delimiters between multiline interface/type members.
  • @typescript-eslint/semi: ["error", "never"] - Disallows semicolons (TypeScript specific).
  • indent / @typescript-eslint/indent: ["error", 2, {...}] - Enforces 2-space indentation.
  • object-shorthand: ["error", "consistent"] - Enforces consistent use of object shorthand syntax.
  • operator-linebreak: error (default after) - Enforces operator placement (likely at the end of the line by default).
  • json/indent: ["error", 2] (in overrides) - Enforces 2-space indentation for JSON files.

Most of these rules will be handled by Prettier (or a similar formatter). eslint-config-prettier (or an equivalent for other formatters) is essential to disable these ESLint rules to prevent conflicts. If ESLint Stylistic is chosen as the primary formatter, these rules would be configured there.

Migration from .eslintrc.json:

Key formatting settings from .eslintrc.json and their corresponding Prettier/EditorConfig settings:

  • comma-dangle: always-multiline -> trailingComma: 'all' (Formatter like Prettier)
  • eol-last: always -> insert_final_newline = true (EditorConfig)
  • quotes: single -> singleQuote: true (Formatter like Prettier)
  • semi / @typescript-eslint/semi: never -> semi: false (Formatter like Prettier)
  • @typescript-eslint/member-delimiter-style: none for multiline -> Handled by semi: false (Formatter like Prettier)
  • indent / @typescript-eslint/indent: 2 -> indent_size = 2 (EditorConfig) & tabWidth: 2 (Formatter like Prettier)
  • object-shorthand: consistent -> Most formatters enforce shorthand where possible. Recommend disabling this rule in ESLint ("object-shorthand": "off") or setting to "always" if the formatter ensures it.

Migration from apihub_code_style.xml:

Key formatting settings from apihub_code_style.xml and their corresponding Prettier/EditorConfig settings:

  • USE_SEMICOLON_AFTER_STATEMENT (JS/TS): false -> semi: false (Formatter like Prettier)
  • USE_DOUBLE_QUOTES (JS/TS): false -> singleQuote: true (Formatter like Prettier)
  • ENFORCE_TRAILING_COMMA (JS/TS): WhenMultiline -> trailingComma: 'all' (Formatter like Prettier - 'all' often chosen for consistency)
  • SPACES_WITHIN_OBJECT_LITERAL_BRACES (JS/TS): true -> bracketSpacing: true (Formatter like Prettier)
  • SPACES_WITHIN_IMPORTS (JS/TS): true -> Handled by formatter's default import spacing.
  • INDENT_SIZE (JS/TS): 2 -> indent_size = 2 (EditorConfig) & tabWidth: 2 (Formatter like Prettier)
  • CONTINUATION_INDENT_SIZE (JS/TS): 2 -> Handled by formatter/EditorConfig indent size.
  • TAB_SIZE (JS/TS): 2 -> tabWidth: 2 (Formatter like Prettier) & indent_style = space (EditorConfig makes tab_width less critical if spaces are used)
  • KEEP_BLANK_LINES_IN_CODE (JS/TS): 1 -> Formatters manage blank lines automatically; this specific setting isn't directly mapped but usually results in sensible blank line usage.
  • TERNARY_OPERATION_SIGNS_ON_NEXT_LINE (JS/TS): true -> Most formatters (like Prettier) typically place operators at the end of the line. This specific preference might not be supported or might require specific formatter options if available.
  • KEEP_SIMPLE_BLOCKS_IN_ONE_LINE (JS/TS): true -> Formatters might format these onto multiple lines for consistency.
  • KEEP_SIMPLE_METHODS_IN_ONE_LINE (JS/TS): true -> Formatters might format these onto multiple lines.

IDE Setup Instructions:

To ensure a smooth development experience, configure your IDE to work with the chosen tools.

Visual Studio Code:

  1. Install Extensions:

    • EditorConfig for VS Code
    • ESLint
    • Formatter Extension (choose one):
      • Prettier - Code formatter (if using Prettier)
      • Biome (if using Biome)
      • dprint (if using dprint - ensure dprint CLI is installed and configured as per extension docs)
      • (If using ESLint Stylistic primarily, ESLint extension handles it, ensure no conflicting default formatter)
  2. Configure Settings (.vscode/settings.json):

    {
      "editor.defaultFormatter": null, // Set specific formatter below per language
      "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // Or biomejs.biome, dprint.dprint
      },
      "[javascriptreact]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // Or biomejs.biome, dprint.dprint
      },
      "[typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // Or biomejs.biome, dprint.dprint
      },
      "[typescriptreact]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // Or biomejs.biome, dprint.dprint
      },
      "[json]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // Or biomejs.biome, dprint.dprint
      },
      "[yaml]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // Or appropriate YAML formatter if not Prettier/Biome/dprint
      },
      "[markdown]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // Or biomejs.biome, dprint.dprint
      },
      "editor.formatOnSave": true,
      "editor.codeActionsOnSave": {
        "source.fixAll.eslint": "explicit" // Use "explicit" for ESLint v9, or true for older.
        // "source.organizeImports": "explicit" // Optional with Biome or other tools
      },
      "eslint.validate": [
        "javascript",
        "javascriptreact",
        "typescript",
        "typescriptreact",
        "json", // if eslint-plugin-jsonc is used
        "yaml"  // if eslint-plugin-yml is used
      ]
      // For Biome, if using its linter:
      // "eslint.enable": false, // If Biome replaces ESLint entirely
      // "biome.lspBin": "./node_modules/.bin/biome", // Or global path
      // For dprint:
      // Ensure dprint CLI is in PATH or configure path in dprint extension settings.
    }
    • Adjust editor.defaultFormatter based on the chosen formatter.
    • VS Code should automatically pick up .editorconfig.

IntelliJ IDEA / WebStorm:

  1. EditorConfig: Enabled by default. Verify in Settings/Preferences > Editor > Code Style > Enable EditorConfig support.
  2. ESLint:
    • Go to Settings/Preferences > Languages & Frameworks > JavaScript > Code Quality Tools > ESLint.
    • Enable ESLint.
    • Choose Automatic ESLint configuration or manually point to your node_interpreter, eslint package, and ESLint configuration file.
    • Enable Run eslint --fix on save if desired.
  3. Formatter (choose one):
    • Prettier:
      • Go to Settings/Preferences > Languages & Frameworks > JavaScript > Prettier.
      • Point to your node_interpreter and prettier package.
      • Check Run on 'Reformat Code' action and Run on save for relevant file types (e.g., {**/*,*}.{js,ts,jsx,tsx,json,yaml,md}).
    • Biome:
      • Install the Biome plugin from the JetBrains Marketplace.
      • Configure in Settings/Preferences > Languages & Frameworks > Biome.
      • Enable Biome, point to the Biome executable (usually from node_modules).
      • Enable Format on save and Run on 'Reformat Code' action.
      • If using Biome's linter, you might disable ESLint integration or configure them to work together if desired.
    • dprint:
      • Install the dprint plugin from the JetBrains Marketplace.
      • Requires dprint CLI to be installed (globally or provide path).
      • Configure in Settings/Preferences > Tools > dprint.
      • Enable formatting. Run on save might need to be configured via File Watchers if not directly supported by the plugin for on-save.
    • (If using ESLint Stylistic primarily): Ensure ESLint is configured to run on save, and no other formatter is conflicting.
  4. Formatter Precedence: Ensure your chosen formatter takes precedence over IDE formatting for relevant file types under Settings/Preferences > Editor > Code Style > Formatter Control. Add file types to be formatted by the chosen tool (Prettier, Biome, dprint).

References:

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

Status

Ready for Dev

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions