Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Directive to Skip File Parsing #118

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
121 changes: 121 additions & 0 deletions designs/2024-ignore-file-directive/README.md
@@ -0,0 +1,121 @@
- Repo: eslint/eslint
- Start Date: 2024-04-01
- RFC PR:
- Authors: Jesper Engberg ([@jeengbe](https://github.com/jeengbe))

# Directive to Skip File Parsing

## Summary

A new `/* eslint-ignore-file */` directive that gives files the ability to ignore themselves.

## Motivation

The term "application developer" refers to the developer that embeds code generated by a third party over which they have little/no control in their project. "Library developer" refers to the developer who maintains the third-party tool that generates the code used by the application developer. Numbered lists in this document are meant to ease referencing and do not imply an order.


Many libraries that generate code generate code with leading `/* eslint-disable */` directives at the top of generated files. However, those files are still parsed and validated, which slows down the linting process needlessly. Potentially large and complex files are processed, even though their results are useless to the application developer. In the worst case, bugs in linting rules (see [this issue about a broken linting rule](https://github.com/eslint/eslint/issues/18022)) crash the entire ESLint process over artifacts an application developer has no control over. The problem is that `/* eslint-disable */` merely mutes linting results.

This directive will work alongside the `.eslintignore` file in that it too will indicate which file be parsed or not. It extends the concept of ignoring source files by allowing files (specifically generated files) to exclude themselves.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .eslintignore file is deprecated. All ignores are now in the eslint.config.js file.


Lastly, library devlopers seldom actually mean "parse, but mute errors" when `/* eslint-disable */` is added at the top of files. While it the best option currently available, "completely skip this file" is actually meant.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious if you have any data to back up this assertion?

In my experience, I don't believe this is true. ESLint will still validate the syntax in this case.


To summarize:

1. Performance boost by not parsing unwanted files
2. Don't crash over code the application developer has no/limited control over
3. Does what leading `/* eslint-disable */` are meant to do

## Detailed Design

Please note that I am not familiar with the internals of ESLint and might make assumptions that are incorrect.

For any _source file_ (the root of an AST), if it contains a leading `/* eslint-ignore-file */` directive, skip parsing it. More concretely, the following pseudocode algorithm explains how to determine whether a file should be ignored or processed:

```
for line of source file:
if line (is not zero or more whitespace) and (does not start with zero or more whitespace + "/"):
parse file
if line is "/* eslint-ignore-file */":
skip file parsing
```

A more sophisticated algorithm may be chosen at a later point in the lifecycle of this RFC.

In practice, the imaginary
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at the actual source code to determine where changes should be made. This is part of the RFC process.


```ts
export declare function parseFile(filePath: string): AST;
```

needs to be replaced with:

```ts
export function parseFile(filePath: string): AST | null {
const fileContent = streamFileContent(filePath);

if (shouldIgnore(fileContent)) return null;
return parseFileImpl(fileContent.collectToString());
}

declare function parseFileImpl(fileContent: string): AST;
```

How the rest of the system deals with `null` results from parsing a source file is not in the scope of this RFC.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is. We need a complete understanding of how such a change would function inside of ESLint.


If an `/* eslint-ignore-file */` directive is encountered while parsing a file, a non-fatal error shall be raised, and the directive shall have no further effect.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the point was that we wouldn't parse the file? But here you're saying we will in order to find the comment? Can you clarify?

And what does it mean for a non-fatal error to be raised? Do you mean it would be look like a lint violation? Would the user get any output?


## Documentation

TBD. The primary audience is a library author. The point **Drawbacks > Bardwards Compatibility** shall be highlighted.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look through our docs and find what would need to be updated.


## Drawbacks

1. Backwards Compatibility

Library authors will need to prefix their generated code with both `/* eslint-ignore-file */` and `/* eslint-disable */` directives in order to not break existing projects that have not upgraded their ESLint core to a version that supports the `/* eslint-ignore-file */` directive. Otherwise, updating libraries without updating ESLint accordingly will lead to possible linging errors on generated source files that were previously muted.

2. Primitive Parser
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we parse at all? Why not just use a regular expression to see if the text exists in the source?


The algorithm described in the **Detailed Design** section is very limited because it does not parse source code. Instead, it directly scans the lines' content. A file containing the following source code is skipped, even though it seems like it shouldn't:

```ts

/* some comment */ statement;

/* eslint-ignore-file */
```

This is an intentional middleground between keeping the analysis as simple (and fast) as reasonable and correctness, which would require parsing read comments to some degree.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per your motivation, shouldn't the directive always be first in a generated file?


3. `.eslintignore` No Longer Authoritative

By extending how files can be ignored, it is no longer purely `.eslintignore` files that dictate which files are parsed or not.

## Backwards Compatibility Analysis

Other than what is highlighted in **Drawbacks > Bardwards Compatibility**, there are no backwards compatibility issues.

## Alternatives

1. As highlighted in **Motivation**, everything described in this RPC is functionally already possible with `.eslintignore` files.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is already possible using an ignore file, then why would we add another way to do the same thing?


2. `/* eslint-disable */` directives are a weaker alternative of what this RPC proposes.

## Open Questions

1. Allow `// eslint-ignore-file`? Is `// eslint-disable` allowed?

2. Is a more sophisticated algorithm worth it? It is not too difficult to construct a proper one, but that call is not up to me.

## Help Needed

I am unable to implement this in the code myself.

## Frequently Asked Questions

n/a

## Related Discussions

See [related GitHub Issue](https://github.com/eslint/eslint/issues/18028)