gptlint / gptlint

A linter with superpowers! 🔥 Use LLMs to enforce best practices across your codebase.
https://gptlint.dev
MIT License
213 stars 19 forks source link

Add support for extensible rules via code #5

Closed transitive-bullshit closed 4 months ago

transitive-bullshit commented 4 months ago

This PR adds support for custom ruleDefinitions in the gptlint config with full Rule objects. These Rule objects may contain custom processing logic which overrides or augments the built-in linting logic:

export type Rule = {
  // normal rule definition...

  // optional custom functionality for rules
  preProcessFile?: PreProcessFileFn
  processFile?: ProcessFileFn
  postProcessFile?: PostProcessFileFn
}

export type PreProcessFileFnParams = Readonly<{
  file: SourceFile
  rule: Rule
  chatModel: ChatModel
  cache: LinterCache
  config: ResolvedLinterConfig
  retryOptions?: RetryOptions
}>
export type PreProcessFileFn = (
  opts: PreProcessFileFnParams
) => MaybePromise<PartialLintResult | undefined>

export type ProcessFileFnParams = Readonly<{
  file: SourceFile
  rule: Rule
  lintResult?: LintResult
  chatModel: ChatModel
  cache: LinterCache
  config: ResolvedLinterConfig
  retryOptions?: RetryOptions
}>
export type ProcessFileFn = (
  opts: ProcessFileFnParams
) => MaybePromise<LintResult>

export type PostProcessFileFnParams = SetRequired<
  ProcessFileFnParams,
  'lintResult'
>
export type PostProcessFileFn = (
  opts: ProcessFileFnParams
) => MaybePromise<LintResult>

Here is an example of a custom rule which uses preProcessFile to extend the default linting process:

import path from 'node:path'
import { fileURLToPath } from 'node:url'

import type { Rule } from '../src/types.js'
import { parseRuleFilePath } from '../src/parse-rule-file.js'

const ruleFile = await parseRuleFilePath('./prefer-fetch-over-axios.md', {
  cwd: import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url))
})

const rule: Readonly<Rule> = {
  ...ruleFile,

  preProcessFile: async (ctx) => {
    if (/["']axios["']/g.test(ctx.file.content)) {
      return {
        lintErrors: [
          {
            message: 'Use `fetch` instead of `axios`'
          }
        ]
      }
    } else {
      // Skip linting because we know the file doesn't use axios
      return {
        lintErrors: []
      }
    }
  }
}

export default rule

To use this rule in your project, you would need to change your gptlint.config.js to look like the following:

import preferAxiosOverFetchRule from './rules/prefer-fetch-over-axios.js'

export default [
  {
    ruleDefinitions: [preferAxiosOverFetchRule]
  }
]

This PR also adds support for Rule.scope which may be file (the default, where the rule is applied at the file level), project (where the rule is applied at the per-project level; e.g., each npm package in a JS/TS monorepo), or repo (where the rule is applied at the top-level of the project's git repository). Currently repo is not implemented.

socket-security[bot] commented 4 months ago

New and removed dependencies detected. Learn more about Socket for GitHub ↗︎

Package New capabilities Transitives Size Publisher
npm/multimatch@7.0.0 Transitive: environment +5 464 kB sindresorhus

🚮 Removed packages: npm/@types/prettier@2.7.3

View full report↗︎

transitive-bullshit commented 4 months ago

TODO: fix caching bug with project-based scopes; ex: effective-tsconfig