Skip to main content
Deno 2.2 is here with built-in OpenTelemetry, Lint plugins, node:sqlite, and more 🎉️
Learn more

Publish a lint rule, get a prize

In Deno 2.2, we’ve added the ability to extend deno lint with a new plugin system. While deno lint currently has 123 built-in rules, the new plugin system means that anyone can create new lint rules. (For comparison, ESLint has 310 rules.)

To encourage more deno lint rules, we’re kicking off a contest to invite you to write and publish new lint rules to JSR until Tuesday, March 4th, 6pm PT. To reward you for your effort, every participant will receive a free, random assortment of Deno stickers!

Read on to learn more about how to participate:

Writing a new lint rule

To show you how to write a new lint rule, we’ll create a plugin that reports an error if you name a variable foo:

deno.json
{
  "lint": {
    "plugins": ["./my-plugin.ts"]
  }
}
my-plugin.ts
const plugin: Deno.lint.Plugin = {
  name: "my-lint-plugin",
  rules: {
    "my-lint-rule": {
      create(context) {
        return {
          VariableDeclarator(node) {
            if (node.id.type === "Identifier" && node.id.name === "foo") {
              context.report({
                node,
                message: "Use more descriptive name than `foo`",
              });
            }
          },
        };
      },
    },
  },
};
export default plugin;
main.js
const foo = "foo";
console.log(foo);
$ deno lint main.js
error[my-lint-plugin/my-lint-rule]: Use more descriptive name than`foo`
--> /dev/main.js:1:7
  |
1 | const foo = "foo";
  | ^^^^^^^^^^^

Found 1 problem Checked 1 file

In addition to a visitor based API, you can also use CSS-like selectors for targeting specific nodes. Let’s rewrite above rule, using the selector syntax.

my-plugin.ts
const plugin: Deno.lint.Plugin = {
  name: "my-lint-plugin",
  rules: {
    "my-lint-rule": {
      create(context) {
        return {
          'VariableDeclarator[id.name="foo"]'(node) {
            context.report({
              node,
              message: "Use more descriptive name than `foo`",
            });
          },
        };
      },
    },
  },
};
export default plugin;

To see this in action, check out this one minute video demo:

For more information, check out the deno lint plugin API in our Deno docs.

Not sure what lint rule to create?

For starters, there are many popular rules from ESlint that have yet to be implemented in Deno:

  • semi: Enforces consistent use of semicolons at the end of statements
  • quotes – Enforces the consistent use of quotes (single, double, or backticks) for strings
  • curly – Requires brace {} usage for all control blocks (if/else/for/etc.), even if optional
  • no-alert – Disallows use of alert, confirm, and prompt in code
  • no-use-before-define – Disallows using variables or functions before they are defined
  • consistent-return – Requires that functions either always return a value or never return a value
  • no-shadow – Disallows variable declarations from shadowing variables declared in outer scopes
  • no-unused-expressions – Flags expression statements that are unused (have no effect)
  • no-empty-function – Disallows empty function bodies
  • no-underscore-dangle – Disallows dangling underscores in identifiers (prefix or suffix)
  • no-implied-eval – Disallows code that invokes the effect of eval() implicitly

To see more examples of lint rules, here is the ESLint GitHub repo.

For ideas and inspiration for lint rules that might not exist yet, check out this GitHub issue (Warning: this issue may be a bit stale, so tread with caution).

And just a reminder, if you do end up borrowing code from ESLint, please remember to give credit and provide the license!

Publishing your lint rule to JSR

Lint plugins can be authored in TypeScript, and Deno provides full type declarations out-of-the-box under the Deno.lint namespace.

To publish your plugin to JSR, update your deno.json so the exports points to your plugin:

deno.json
{
  "lint": {
    "plugins": ["./my-plugin.ts"]
  },
  "name": "@lambtron/andys-lint-rule",
  "version": "0.3.0",
  "license": "MIT",
  "exports": "./my-plugin.ts"
}

Then, run deno publish (or npx jsr publish), and it’ll appear on JSR.

For a full, end-to-end example, check out Andy’s Lint Rule.

Consuming lint plugins from JSR (or npm) is simple. You can use the specifier directly in lint.plugins in your deno.json:

deno.json
{
  "lint": {
    "plugins": [
      "./my-plugin.ts",
      "jsr:@my-scope/lint-plugin",
      "npm:@my-scope/other-plugin"
    ]
  }
}

Prizes and how to submit

Anyone who submits a deno lint rule to JSR will win free assortment of Deno stickers!

Picture of assorted Deno stickers

In order to be eligible to receive your stickers, please fill out this form with key details of your lint rule and shipping information.

We will be accepting submissions until Tuesday, March 4th, 6pm PT.

Additional resources

If you want to participate but don’t have any ideas, come join our Discord, where we’ll discuss lint rule ideas, answer any questions, and get you setup writing your lint rules!

We look forward to seeing what lint rules you create!

Get technical support, share your project, or simply say hi on Twitter, Discord, BlueSky, and Mastodon.