ember-template-lint icon indicating copy to clipboard operation
ember-template-lint copied to clipboard

Can a plugin specify overrides?

Open ijlee2 opened this issue 1 year ago • 2 comments

Background

When the latest versions of ember-template-lint (v5.3.0) and ember-template-lint-plugin-prettier (v4.1.0) are installed, template code in test files will be checked for formatting. Running yarn lint:hbs:fix will auto-format the code.

However, the indentations are currently suboptimal. In addition, reformatting increases the lines of code (LOC) changed in the pull request that updates ember-template-lint to v5. Thus, an end-developer may want to turn off auto-formatting in test files.

How to turn off auto-formatting

Suppose an end-developer has a shared configuration file (e.g. to maintain a monorepo). There are two approaches: one, where the developer uses require to read the file, and another, where they write a plugin and extend it.

Approach 1: Use require to read the file
/* Shared */
'use strict';

module.exports = {
  plugins: ['ember-template-lint-plugin-prettier'],
  extends: ['recommended', 'ember-template-lint-plugin-prettier:recommended'],
  overrides: [
    {
      files: ['tests/**/*-test.{js,ts}'],
      rules: {
        prettier: 'off',
      },
    },
  ],
};

/* Consuming project */
module.exports = require('some-namespace/ember-template-lint');
Approach 2: Use extends to extend a plugin
/* Shared */
'use strict';

module.exports = {
  name: 'my-shared-plugin',
  rules: {},
  configurations: {
    recommended: {
      plugins: ['ember-template-lint-plugin-prettier'],
      extends: ['recommended', 'ember-template-lint-plugin-prettier:recommended'],
      overrides: [
        {
          files: ['tests/**/*-test.{js,ts}'],
          rules: {
            prettier: 'off',
          },
        },
      ],
    },
  },
};

/* Consuming project */
module.exports = {
  plugins: ['some-namespace/ember-template-lint'],
  extends: 'my-shared-plugin:recommended'
};

Problem

In Approach 1, the overrides key correctly prevents auto-formatting the template code in test files. In Approach 2, however, the plugin's overrides is not respected.

Preliminary investigation

Depending on the approach, we get a different config from getProjectConfig(). (See the GitHub Gist for a diff view.)

Approach 1: Returned config
{
  "rules": { /* ... */ },
  "overrides": [
    {
      "files": [
        "tests/**/*-test.{js,ts}"
      ],
      "rules": {
        "prettier": {
          "config": false,
          "severity": 0
        }
      }
    }
  ],
  "ignore": [],
  "format": {},
  "plugins": {
    "ember-template-lint-plugin-prettier": { /* ... */ }
  },
  "loadedRules": {},
  "loadedConfigurations": {
    "4-x-recommended": { /* ... */ },
    "a11y": { /* ... */ },
    "recommended": { /* ... */ },
    "stylistic": { /* ... */ },
    "ember-template-lint-plugin-prettier:recommended": { /* ... */ }
  },
  "_processed": true
}
Approach 2: Returned config
{
  "rules": { /* ... */ },
  "overrides": [],
  "ignore": [],
  "format": {},
  "plugins": {
    "my-shared-plugin": { /* ... */ },
    "ember-template-lint-plugin-prettier": { /* ... */ }
  },
  "loadedRules": {},
  "loadedConfigurations": {
    "4-x-recommended": { /* ... */ },
    "a11y": { /* ... */ },
    "recommended": { /* ... */ },
    "stylistic": { /* ... */ },
    "my-shared-plugin:recommended": { /* ... */ },
    "ember-template-lint-plugin-prettier:recommended": { /* ... */ }
  },
  "_processed": true
}

Notice that, in Approach 2, config.overrides is equal to the empty array. In other words, validateOverrides() must have determined that there are no rules to override. This is possibly because, when we enter the function validateOverrides(), config.overrides was [] to begin with; if so, the map function (line 347) would be a no-op.

I didn't find unit tests that cover these questions:

  • What is the final value of config when a plugin specifies overrides?
  • What is the final value of config when a plugin specifies overrides and the end-developer specifies overrides?

Possible solutions?

I'd expect that:

  • A plugin can specify overrides, to override the rules that the plugin consumed.
  • If both the plugin and the end-developer specify overrides, the rules from the end-developer's overrides win.

Regardless of whether we allow a plugin to specify overrides, I'd like to suggest that we:

  • Write unit tests that cover the two questions above.
  • Clarify in README which keys a plugin can specify. (The sample plugin, ember-template-lint-plugin-peopleconnect, represents only one example and may not document everything.)

Related links

I wrote this issue based on a couple of Discord chats. Here are the links to the main messages:

ijlee2 avatar Dec 19 '22 12:12 ijlee2

Turns out this happens everywhere where there is hbs used, as for example in this story:

export const Default = (args: { hello: string; to: string }) => ({
  template: hbs`
    <Greeting @hello={{this.hello}} @to={{this.to}} />
  `,
  context: args
});

As this is a .ts file and I configured vscode to use eslint as default formatter - my IDE shows this as totally fine, but CLI will complain about it. Meaning: no immediate feedback during development - needs a CI roundtrip for that.

gossi avatar Dec 20 '22 17:12 gossi

In my case, I have a plugin that defines custom rules and is used in several apps in the yarn workspace. After upgrading to template lint v5 I want to disable a few rules in test files. Using overrides works in each app's config but adding overrides to a shared plugin doesn't work. So, I have to duplicate the overrides in all the apps.

andreyfel avatar Mar 03 '23 13:03 andreyfel