npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tony.ganchev/eslint-plugin-header

v3.2.4

Published

The native ESLint 9/10 header plugin. A zero-bloat, drop-in replacement for 'eslint-plugin-header' with first-class Flat Config & TypeScript support. Auto-fix Copyright, License, and banner comments in JavaScrip and TypeScript files.

Downloads

91,488

Readme

@tony.ganchev/eslint-plugin-header

npm version Downloads/month Build Status

The native ESLint 9/10 standard header-validating plugin. A zero-bloat, drop-in replacement for eslint-plugin-header with first-class Flat Config & TypeScript support. Auto-fix copyright, license, and banner comments in JavaScript and TypeScript files.

Table of Contents

  1. Motivation and Acknowledgements
  2. Compatibility
  3. Usage
    1. File-based Configuration
    2. Inline Configuration
      1. Header Contents Configuration
      2. Providing To-year in Auto-fix
      3. Trailing Empty Lines Configuration
      4. Line Endings
    3. Examples
  4. Comparison to Alternatives
    1. Compared to eslint-plugin-headers
      1. Health Scans
    2. Compared to eslint-plugin-license-header
  5. Versioning
    1. What is a Feature?
    2. What is Backward-compatibility?
  6. License

Motivation and Acknowledgements

The plugin started as a fork of eslint-plugin-header to address missing ESLint 9 compatibility.

Today it addresses the following issues:

  • Support for ESLint 9/10 with a fully-validated configuration schema.
  • Continued support for ESLint 7/8.
  • Complete Windows support.
  • New object-based configuration providing the bases for future enhancements.
  • Continued support for eslint-plugin-header array configuration.
  • Bugfixes where the original project has not been updated for the three years before the fork.
  • Fixes issues with she-bangs and empty lines before the header. See PR history for more details.
  • Good error reporting and improved auto-fixes.
  • Complete drop-in-replacement compatibility with existing projects using eslint-plugin-header.

Multiple other projects took from where eslint-plugin-header left off. A comparison of the current project to these alternatives is available in a dedicated section.

Compatibility

The plugin supports ESLint 7 / 8 / 9 / 10. Both flat config and legacy, hierarchical config can be used.

The NPM package provides TypeScript type definitions and can be used with TypeScript-based ESLint flat configuration without the need for @ts-ignore statements.

Usage

The plugin and its header rule goes through evolution of its configuration in the 3.2.x release. We introduced a new single object-based configuration format that is easier to evolve in the future to add more capabilities.

The legacy configuration format inherited from eslint-plugin-header is still supported and you can learn how to use it in a dedicated document. For information on how to switch from the legacy configuration format to the new style, follow our migration guide. The current document from this point on will cover only the new configuration format.

This header rule takes a single object as configuration, after the severity level. At the very least, the object should contain a header field describing the expected header to match in the source files.

For TypesScript-based flat ESLint configuration, two types are provided:

  • HeaderRuleConfig defines the overall rule configuration for the header rule and includes severity level and supports both the modern object-based configuration and the legacy array-based configuration.
  • HeaderOptions helper type that defines the structure of the configuration object used in the modern configuration style that is used in this document. It can be used to either simplify auto-completion since this type is not mixed with a large number of named tuple types, or it can be used when the config object is defined outside of the definition of a specific rule.

File-based Configuration

In this configuration mode, the header template is read from a file.

eslint.config.ts:

import header, {
    HeaderOptions, HeaderRuleConfig
} from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        file: "config/header.js"
                    }
                } as HeaderOptions
            ] as HeaderRuleConfig
        }
    }
]);

config/header.js:

// Copyright 2015
// My company

Due to limitations in ESLint plugins, the file is read relative to the working directory that ESLint is executed in. If you run ESLint from elsewhere in your tree then the header file will not be found.

Inline Configuration

In this configuration mode, the matching rules for the header are given inline. The header field should contain the following nested properties:

  • commentType which is either "block" or "line" to indicate what style of comment should be used.
  • line which defines the lines of the header. It can be either a single multiline string / regular expression with the full contents of the header comment or an array with comment lines or regular expressions matching each line. It can also include template replacement strings to enable ESLint's auto-fix capabilities.

Header Contents Configuration

Suppose we want our header to look like this:

/*
 * Copyright (c) 2015
 * My Company
 */

All of the following configurations will match the header:

  • Single string:

    import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
    import { defineConfig } from "eslint/config";
    
    export default defineConfig([
        {
            files: ["**/*.js"],
            plugins: {
                "@tony.ganchev": header
            },
            rules: {
                "@tony.ganchev/header": [
                    "error",
                    {
                        header: {
                            commentType: "block",
                            lines: ["\n * Copyright (c) 2015\n * My Company\n "]
                        }
                    } as HeaderOptions
                ]
            }
        }
    ]);

    Note that the above would work for both Windows and POSIX systems even though the EOL in the header content was specified as \n.

    Also, notice how we have an empty space before each line. This is because the plugin only strips the leading // characters from a line comment. Similarly, for a block comment, only the opening /* and closing */ will be preserved with all new lines and whitespace preserved. Keep this in mind as this can lead to poorly configured header matching rules that never pass. In a future release error messages would be more detailed and show exactly where header validation failed.

  • Single regular expression:

    You can match the whole header with a regular expression. To do it, simply pass a RegExp object in place of a string.

    import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
    import { defineConfig } from "eslint/config";
    
    export default defineConfig([
        {
            files: ["**/*.js"],
            plugins: {
                "@tony.ganchev": header
            },
            rules: {
                "@tony.ganchev/header": [
                    "error",
                    {
                        header: {
                            commentType: "block",
                            lines: [
                                /\n \* Copyright \(c\) 2015\n \* Company\n /
                            ]
                        }
                    } as HeaderOptions
                ]
            }
        }
    ]);

    If you still use hierarchical configuration, you can define the regular expression as a string.

    import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
    import { defineConfig } from "eslint/config";
    
    export default defineConfig([
        {
            files: ["**/*.js"],
            plugins: {
                "@tony.ganchev": header
            },
            rules: {
                "@tony.ganchev/header": [
                    "error",
                    {
                        header: {
                            commentType: "block",
                            lines: [
                                { pattern: "\\n \\* Copyright \\(c\\) 2015"
                                    + "\\n \\* My Company\\n "}
                            ]
                        }
                    } as HeaderOptions
                ]
            }
        }
    ]);

    Notice the double escaping of the braces. Since these pattern strings into RegExp objects, the backslashes need to be present in the string instead of disappear as escape characters.

    You can pass a RegExp object to the pattern field. This is necessary if you want to add an aut-fix for the line as we will explain further in this document.

    import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
    import { defineConfig } from "eslint/config";
    
    export default defineConfig([
        {
            files: ["**/*.js"],
            plugins: {
                "@tony.ganchev": header
            },
            rules: {
                "@tony.ganchev/header": [
                    "error",
                    {
                        header: {
                            commentType: "block",
                            lines: [
                                { pattern: /Copyright \(c\) 20\d{2}/ }
                            ]
                        }
                    } as HeaderOptions
                ]
            }
        }
    ]);
  • Array of strings:

    import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
    import { defineConfig } from "eslint/config";
    
    export default defineConfig([
        {
            files: ["**/*.js"],
            plugins: {
                "@tony.ganchev": header
            },
            rules: {
                "@tony.ganchev/header": [
                    "error",
                    {
                        header: {
                            commentType: "block",
                            lines: [
                                "",
                                " * Copyright (c) 2015",
                                " * My Company",
                                " "
                            ]
                        }
                    } as HeaderOptions
                ]
            }
        }
    ]);
  • Array of strings and/or patterns:

    import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
    import { defineConfig } from "eslint/config";
    
    export default defineConfig([
        {
            files: ["**/*.js"],
            plugins: {
                "@tony.ganchev": header
            },
            rules: {
                "@tony.ganchev/header": [
                    "error",
                    {
                        header: {
                            commentType: "block",
                            lines: [
                                "",
                                / \* Copyright \(c\) 2015/,
                                " * My Company",
                                " "
                            ]
                        }
                    } as HeaderOptions
                ]
            }
        }
    ]);

Regular expressions allow for a number of improvements in the maintainability of the headers. Given the example above, what is clear is that new sources may have been created later than 2015 and a comment with a different year should be perfectly valid, such as:

/*
 * Copyright 2020
 * My company
 */

Moreover, suppose your legal department expects that the year of first and last change be added except if all changes happen in the same year, we also need to support:

/*
 * Copyright 2017-2022
 * My company
 */

We can use a regular expression to support all of these cases for your header:

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "block",
                        lines: [
                            "",
                            / \* Copyright \(c\) (\d{4}-)?\d{4}/,
                            " * My Company",
                            " "
                        ]
                    }
                } as HeaderOptions
            ]
        }
    }
]);

Note on auto-fixes i.e. eslint --fix: whenever strings are used to define the header - counting in file-based configuration - the same strings would be used to replace a header comment that did not pass validation. This is not possible with regular expressions. For regular expression pattern-objects, a second property template adds a replacement string.

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "line",
                        lines: [
                            {
                                pattern: / Copyright \(c\) (\d{4}-)?\d{4}/,
                                template: " Copyright 2025",
                            },
                            " My Company"
                        ]
                    }
                } as HeaderOptions
            ]
        }
    }
]);

There are a number of things to consider:

  • Templates need to be matched by the regular expression pattern or otherwise an auto-fixed source would again fail linting. This needs to be validated manually today as the plugin does not do it for you.
  • Templates are hardcoded strings therefore it may be better to hand-fix a bad header in order not to lose the from- and to-years in the copyright notice.

Providing To-year in Auto-fix

A common request across similar plugins is to provide for {year} variable to not change the ESLint configuration every year. While such special requests were relevant to old JSON-based configuration, this can be handled with JavaScript in the flat configuration format:

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "line",
                        lines: [
                            {
                                pattern: / Copyright \(c\) (\d{4}-)?\d{4}/,
                                template: ` Copyright ${new Date().getFullYear()}`,
                            },
                            " My Company"
                        ]
                    }
                } as HeaderOptions
            ]
        }
    }
]);

Trailing Empty Lines Configuration

The third argument of the rule configuration which defaults to 1 specifies the number of newlines that are enforced after the header.

Zero newlines:

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "block",
                        lines: [
                            " Copyright now",
                            "My Company "
                        ],
                    },
                    trailingEmptyLines: {
                        minimum: 0
                    }
                } as HeaderOptions
            ]
        }
    }
]);
/* Copyright now
My Company */ console.log(1)

One newline (default):

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "block",
                        lines: [
                            " Copyright now",
                            "My Company "
                        ],
                    },
                    trailingEmptyLines: {
                        minimum: 1
                    }
                } as HeaderOptions
            ]
        }
    }
]);
/* Copyright now
My Company */
console.log(1)

Two newlines:

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "block",
                        lines: [
                            " Copyright now",
                            "My Company "
                        ]
                    },
                    trailingEmptyLines: {
                        minimum: 2
                    }
                } as HeaderOptions
            ]
        }
    }
]);
/* Copyright now
My Company */

console.log(1)

Line Endings

The rule works with both Unix/POSIX and Windows line endings. For ESLint --fix, the rule will use the line ending format of the current operating system (via Node's os package). This setting can be overwritten as follows:

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "block",
                        lines: [
                            "Copyright 2018",
                            "My Company"
                        ]
                    },
                    lineEndings: "windows"
                } as HeaderOptions
            ]
        }
    }
]);

Possible values are "unix" for \n and "windows" for \r\n line endings. The default value is "os" which means assume the system-specific line endings.

Examples

The following examples are all valid.

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "block",
                        lines: ["Copyright 2015, My Company"]
                    }
                } as HeaderOptions
            ]
        }
    }
]);
/*Copyright 2015, My Company*/
console.log(1);
import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "line",
                        lines: [
                            "Copyright 2015",
                            "My Company"
                        ]
                    }
                } as HeaderOptions
            ]
        }
    }
]);
//Copyright 2015
//My Company
console.log(1)
import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "line",
                        lines: [
                            /^Copyright \d{4}$/,
                            /^My Company$/
                        ]
                    }
                } as HeaderOptions
            ]
        }
    }
]);
//Copyright 2017
//My Company
console.log(1)

With more decoration:

import header, { HeaderOptions } from "@tony.ganchev/eslint-plugin-header";
import { defineConfig } from "eslint/config";

export default defineConfig([
    {
        files: ["**/*.js"],
        plugins: {
            "@tony.ganchev": header
        },
        rules: {
            "@tony.ganchev/header": [
                "error",
                {
                    header: {
                        commentType: "block",
                        lines: [
                            "************************",
                            " * Copyright 2015",
                            " * My Company",
                            " ************************"
                        ]
                    }
                } as HeaderOptions
            ]
        }
    }
]);
/*************************
 * Copyright 2015
 * My Company
 *************************/
 console.log(1);

Comparison to Alternatives

A number of projects have been aiming to solve problems similar to @tony.ganchev/eslint-plugin-header - mainly with respect to providing ESLint 9 support. The section below tries to outline why developers may choose either. The evaluation is based on the versions of each project as of the time of publishing the current version of @tony.ganchev/eslint-plugin-header.

Further iterations of this document would add migration information.

Compared to eslint-plugin-headers

@tony.ganchev/eslint-plugin-header is a drop-in replacement for eslint-plugin-header and all plugins that already use the latter can migrate to the fork right away. At the same time, it provides improved user experience and windows support.

eslint-plugin-headers is not a drop-in replacement. It offers additional features. Some of them, such as support for Vue templates do not have an analogue in the current version of @tony.ganchev/eslint-plugin-header while others such as {year} variable placeholders are redundant in the world of ESLint 9's flat, JavaScript-based configuration as already pointed out in this document.

The configuration format philosophy of the two plugin differs. @tony.ganchev/eslint-plugin-header supports both the legacy model inherited from eslint-plugin-header and a new object-based configuration that is easy to adopt and offers both a lot of power to the user as to what the headers should look like, and keeps the configuration compact - just a few lines defining the content inside the comment. At the same time, the configuration is structured in a way that can evolve without breaking compatibility, which is critical for a tool that is not differentiating for the critical delivery of teams.

eslint-plugin-headers also offers an object-based format, but the content is flat and may need breaking changes to be kept concise as new features come about. Further, it makes assumption that then need to be corrected such as a block comment starting with /** instead of /* by default. The correction needs to happen not by adjusting the header template but through a separate confusing configuration properties. Overall, the configuration tends to be noisier nad harder to read than that of @tony.ganchev/eslint-plugin-header.

eslint-plugin-headers's error reporting is rudimentary - either the header passes or it fails and with complex templates you get no idea what the issue is. Granted this is the case with eslint-plugin-header but we spent many hours improving this and the side by side comparison is telling.

eslint-plugin-headers does not offer TypeScript bindings for its configuration format making it slower to author configuration. @tony.ganchev/eslint-plugin-header as often as possible reports which line is problematic and starting from which character.

eslint-plugin-headers supports some level of partial auto-fixes such as replacing company names but not years. Or keeping JSDoc variables after the copyright notice within the same comment. Some cases can be supported by @tony.ganchev/eslint-plugin-header but in general our design has shied away from touching existing comments to provide "smart" fixes. This is the current state of the feature set yet the team is looking for the right model to bridge the functionality gaps.

We have prepared a detailed migration guide for anyone eager to migrate to @tony.ganchev/eslint-plugin-header.

Health Scans

At the time of the publishing of the current version of @tony.ganchev/eslint-plugin-header, the latter has a slight edge in both scans against eslint-plugin-headers.

Compared to eslint-plugin-license-header

eslint-plugin-license-header per its limited documentation does not have a lot of features including not matching arbitrary from-years in the copyright notice. This on one hand leads to it having a nice, dead-simple configuration, but means no complex multi-year project would be happy with it. Surprisingly, given the limited feature set, the plugin has more peer dependencies than the competition.

We have prepared a detailed migration guide for anyone eager to migrate to @tony.ganchev/eslint-plugin-header.

Versioning

The project follows standard NPM semantic versioning policy.

The following guidelines apply:

  • major versions - new functionality that breaks compatibility.
  • minor versions - new features that do not break compatibility. For the most part we would aim to continue releasing new versions in the 3.x product line and have opt-in flags for changes in behavior of existing features.
  • revisions - bugfixes and minor non-feature improvements that do not break compatibility. Note that bug-fixes are allowed to break compatibility with previous version if the older version regressed previous expected behavior.

Two concepts are important when going over the above guidelines and we will go over them in the next sections.

What is a Feature?

We keep the distinction between a feature and a non-feature improvement / bug fix as simple as possible:

  • If configuration changes, it's a feature.
  • If it doesn't, then you have two cases:
    • If it changes behavior back to what is expected, it is a bug.
    • If it changes the expected behavior, it is an improvement.

What is Backward-compatibility?

Backward compatibility in the context of this plugin relates to how the plugin consistently passes or fails one and the same code in between upgrades to newer backward-compatible versions. This guarantees that plugin updates can be done without breaking CI/CD pipeline linting.

Backward-compatibility does not cover the following functional aspects:

  • Rule violation messages are not kept stable between backward-compatible versions. This allows us to improve error reporting in addition to bug fixes.
  • Auto-fix behavior is not stable between backward-compatible versions. As auto- fixes are not part of CI/CD processes results of them may vary.
  • Bugs to released functionality. Bugs are considered regression to expected functionality regardless of whether they went into a release or not. We fix bugs without maintaining bug-compatibility.

License

MIT