Skip to content

Change Request: Make the FileProblem type optionally generic over the loc property #286

@lumirlumir

Description

@lumirlumir

Which packages would you like to change?

  • @eslint/compat
  • @eslint/config-array
  • @eslint/config-helpers
  • @eslint/core
  • @eslint/mcp
  • @eslint/migrate-config
  • @eslint/object-schema
  • @eslint/plugin-kit

What problem do you want to solve?

The problem arose while I was working on eslint/markdown#548, eslint/css#281, and eslint/json#162.

Currently, we use the FileProblem type when creating the getDisableDirectives and applyInlineConfig methods, which are used by the SourceCode class consumed by various plugins.

getDisableDirectives?(): {
directives: Directive[];
problems: FileProblem[];
};

applyInlineConfig?(): {
configs: InlineConfigElement[];
problems: FileProblem[];
};

Currently, FileProblem['loc'] is typed as SourceLocation, which has the following signature:

export interface FileProblem {
ruleId: string | null;
message: string;
loc: SourceLocation;
}

interface SourceLocation {
    start: {
        line: number;
        column: number;
    }
    end: {
        line: number;
        column: number;
    }
}

However, when I look at the origin of the FileProblem['loc'] property, I found that it depends on the loc or position property of a specific plugin's AST.

For example, FileProblem['loc'] can be:

  • Position from unist in Markdown.
  • JSONSyntaxElement['loc'] (LocationRange) from @humanwhocodes/momoa in JSON.
  • CssLocationRange from @eslint/css-tree in CSS.

The problem is that SourceLocation is only a subset of each of these types (e.g. Position, JSONSyntaxElement['loc'], or CssLocationRange), so we lose plugin-specific fields such as start.offset or end.offset.


  • Position (Markdown)
interface Position {
    start: {
        line: number;
        column: number;
        offset?: number | undefined;
    }
    end: {
        line: number;
        column: number;
        offset?: number | undefined;
    }
}
  • JSONSyntaxElement['loc'] (LocationRange) (JSON)
interface LocationRange {
    start: {
        line: number;
        column: number;
        offset: number;
    }
    end: {
        line: number;
        column: number;
        offset: number;
    }
}
  • CssLocationRange (CSS)
interface CssLocationRange {
    source: string;
    start: {
        line: number;
        column: number;
        offset: number;
    }
    end: {
        line: number;
        column: number;
        offset: number;
    }
}
  • SourceLocation
interface SourceLocation {
    start: {
        line: number;
        column: number;
    }
    end: {
        line: number;
        column: number;
    }
}

As you can see, SourceLocation does not include offset or other plugin-specific properties, so type information is lost.

What do you think is the correct solution?

To resolve the problem above, I would like to suggest making the FileProblem type optionally generic over the loc property.

The final source code might look like this:

  • From

export interface FileProblem {
ruleId: string | null;
message: string;
loc: SourceLocation;
}

  • To
export interface FileProblem<Location extends SourceLocation = SourceLocation> {
	ruleId: string | null;
	message: string;
	loc: Location;
}

This will allow users to provide a more specific type for FileProblem.

Participation

  • I am willing to submit a pull request for this change.

Additional comments

There might be a better alternative—if so, I'd be happy to take a look.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

Status

Triaging

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions