Giter Club home page Giter Club logo

eslint-docgen's Introduction

eslint-docgen

Automatically generate ESLint plugin documentation from rule metadata and test cases.

⬇️ Installation

npm install eslint-docgen --save-dev

🛠️ Setup

Replace all uses of RuleTester with the version from this package:

// Old:
const RuleTester = require( 'eslint' ).RuleTester;
// New:
const RuleTester = require( 'eslint-docgen' ).RuleTester;

Create a configuration file as described in Configuration, setting docPath and preferably rulePath and testPath.

📖 Usage

To build your documentation, run your rule tests with the DOCGEN environment variable set in the command line, e.g.

DOCGEN=1 mocha tests/rules/

You could add this to your package.json to make it available as npm run doc, e.g.

{
    //
    "scripts": {
        //
        "doc": "rm -rf docs/rules && DOCGEN=1 mochan tests/rules/"
    }
    //
}

Documentation will be built using rule metadata and test data passed to RuleTester:

rule.meta.docs.description

Used as the description of the rule in the documentation.

rule.meta.docs.deprecated / rule.meta.docs.replacedBy

Used to show a deprecation warning in the documentation, optionally with links to replacement rule(s).

tests.valid/tests.invalid from RuleTester#run

Will generate code blocks showing examples of valid/invalid usage. Blocks will be grouped by unique options/settings configurations. Fixable rules with output will generate a separate block showing the before and after.

By default all test cases will be included in the examples. To exclude specific test cases from these code blocks use the docgen: false option:

{
    code: 'App.method();',
    docgen: false
}

If you have excludeExamplesByDefault set to true in your config, you can include specific test cases in these code blocks by using the docgen: true option:

{
    code: 'App.method();',
    docgen: true
}

🤖 Migration

To migrate an existing plugin with manually built documentation you can use the following process:

  1. Follow the steps in Installation and Setup.
  2. Move your existing documentation to a new folder, e.g. docs/template/MYRULE.md and in your .eslintdocgenrc set ruleTemplatePath to this new folder, e.g. "docs/template/{name}.md". Optionally you can rename these files to .ejs.
  3. Run the generator (as described in Usage) to confirm that it copies your old documentation (now your templates) back to the original documentation path.
  4. Start switching out manually written sections of your templates with include blocks such as those found in index.ejs.

⚙️ Configuration

Configuration for all rules in a project is controlled by creating a JSON/JavaScript file called .eslintdocgenrc.json/.eslintdocgenrc.js in your project root:

JSON

{
    "docPath": "docs/rules/{name}.md",
    // ...
}

JavaScript

module.exports = {
    docPath: 'docs/rules/{name}.md',
    // ...
};

Overriding

The project-wide rules configuration can be overridden for individual rules by adding a docgenConfig property to the tests object passed to RuleTester.run(). All configuration options that are supported project-wide can be changed.

Options

The following config options are available:

docPath (required)

The path to store rule documentation files, with {name} as a placeholder for the rule name, e.g. "docs/rules/{name}.md" or "rules/{name}/README.md".

rulePath

The path where the rule is defined, only required if ruleLink is true. Same format as docPath.

testPath

The path where the rule's tests are defined, only required if testPath is true. Same format as docPath.

ruleTemplatePath

When defined, will try to use a rule specific template instead of index.ejs, e.g. "docs/templates/{name}.ejs". Same format as docPath.

globalTemplatePath

When defined, templates in this path will override the global templates defined in src/templates.

docLink (default false)

Add a link to the documentation source in the "Resources" section.

ruleLink (default true)

Add a link to the rule source in the "Resources" section. Requires rulePath to be defined.

testLink (default true)

Add a link to the rule's test source in the "Resources" section. Requires testPath to be defined.

pluginName (default from package name)

The name of your plugin as used in directives, e.g. plugin:pluginName/rule. Defaults to the name in package.json with eslint-plugin- stripped.

fixCodeExamples (default true)

Fix code examples using the ESLint configuration used for your main script.

showConfigComments (default false)

Shows config comments at the top of code examples:

/* eslint myPlugin/rule: "error" */
// Test cases

showFixExamples (default true)

Show examples of how code is fixed by the rule.

showFilenames (default: false)

Show the relevant file name for test cases.

excludeExamplesByDefault (default false)

Exclude tests from being used as examples by default. When this is true users must set docgen: true on any test they want to be included in examples.

minExamples (default ['warn', 2])

Minimum examples per rule. Tuple where first value is one of 'warn' or 'error', and the second value is the minimum number of examples required. Use null for no minimum.

maxExamples (default ['warn', 50])

Maximum examples per rule. Tuple where first value is one of 'warn' or 'error', and the second value is the maximum number of examples allowed. Use null for no maximum.

tabWidth (default 4)

Number of spaces to convert tabs to in code examples. Tabs in examples are always converted to spaces so their widths can be determined reliably for alignment.

🔍 Rules index

To assist with building an index of your rules, for example to put in a root README, this package exports rulesWithConfig. The value is a Map much like the one returned by Linter#getRules but each rule has an additional configMap property that describes which configs include the rule and the options used (null if no options are used).

Note that the rule names do not include the plugin prefix.

Example:

require( 'eslint-docgen' ).rulesWithConfig.get( 'no-event-shorthand' );
// Outputs:
{
    meta: [Object],
    create: [Function],
    configMap: Map {
        'deprecated-3.5' => null,
        'deprecated-3.3' => [ { allowAjaxEvents: true } ]
    }
}

✏️ Examples

eslint-docgen's People

Contributors

dannys712 avatar edg2s avatar j-f1 avatar jdforrester avatar jpoehnelt avatar raineorshine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eslint-docgen's Issues

Config error output swallowed in jest

When using jest to run the tests I do not get any useful output from assertValidConfig:

process.exit called with "1"
  at assertValidConfig (node_modules/eslint-docgen/src/write-docs-from-tests.js:30:11)
  at Object.<anonymous> (node_modules/eslint-docgen/src/write-docs-from-tests.js:34:1)
  at Object.<anonymous> (node_modules/eslint-docgen/src/rule-tester.js:15:32)

eslint-docgen's RuleTester fails, but eslint version works fine

I really wanted to use this package and I tried for a few days on and off to get it to work, but I keep running into this error:

$ DOCGEN=1 yarn test --detectOpenHandles
< ... >
 /path/to/my/rule.md
      error  Configured parser '/path/to/my/project/node_modules/@babel/eslint-parser/lib/index.cjs' was not found.:
             undefined

      at writeDocsFromTests (node_modules/eslint-docgen/src/write-docs-from-tests.js:86:15)

@babel/eslint-parser is used by one of the dependencies of my eslint plugin (an internal work thing) so it's reasonable that it would show up as the parser here.

The path that is given in the error message is valid and leads to @babel/eslint-parser as expected if I try to open it (meaning it's certainly able to be found :) ).

I opened up the source of write-docs-from-tests.js and added some more logging around the error - the full stack trace is like this:

Error: Configured parser '/path/to/my/project/node_modules/@babel/eslint-parser/lib/index.cjs' was not found.:
        undefined
            at lintFix (/path/to/my/project/node_modules/eslint-docgen/src/fix.js:55:15)
            at Object.batchLintFix (/path/to/my/project/node_modules/eslint-docgen/src/fix.js:74:19)
            at buildRuleDetails (/path/to/my/project/node_modules/eslint-docgen/src/build-docs-from-tests.js:94:16)
            at buildDocsFromTests (/path/to/my/project/node_modules/eslint-docgen/src/build-docs-from-tests.js:271:18)
            at writeDocsFromTests (/path/to/my/project/node_modules/eslint-docgen/src/write-docs-from-tests.js:73:37)

If I change RuleTester to the eslint version, like const {RuleTester} = require('eslint'); then the tests run fine and there's no issue. But then there's no docs :) unless I write them or copy/paste rule descriptions into markdown myself... which is why I would love to be able to use this package!

Review popular plugins for potential new feature requirements

Examples:

Repo Example Notes
ESLint core indent
plugin-es no-math-atanh.md Example uses <eslint-playground which doesn't render on GitHub
plugin-import no-absolute-path.md
plugin-jsx-a11y aria-role.md
plugin-mocha no-mocha-arrows.md
plugin-node no-process-exit.md
plugin-promise catch-or-return.md All but two docs in this repo are empty
plugin-qunit no-async-test.md
plugin-react jsx-pascal-case.md
plugin-unicorn better-regex.md
plugin-vue valid-v-for.md

Lint-fixing examples can break them

In plugin-mediawiki we have two tests:

new OO.ui.ButtonWidget( { classes: ["foo"] } )
new OO.ui.ButtonWidget( { "classes": ["foo"] } )

The quoted key case gets fixed to be identical to the one above.

In this particular case it can just be noDoc'd but in other cases this may not be appropriate.

Lint-fixing can be turned off globally but that too may not be desirable.

Possible solutions:

  • Add a per rule noDocFix option
  • Restrict fixing to just whitespace fixes
  • Have a mode where eslint-docgen reports lint errors in examples, but doesn't fix them. Properly this would be a pre-processing plugin.
  • Warn when duplicate tests are detected

Support for TSESlint

I use TSESLint, https://www.npmjs.com/package/@typescript-eslint/experimental-utils, for my custom rules which adds TypeScript support to the RuleTester. It would be nice if this was supported more cleanly.

My workaround is below.

import { ESLintUtils, TSESLint } from "@typescript-eslint/experimental-utils";
import { RuleTester as DocRuleTester } from "eslint-docgen";

export class RuleTester extends DocRuleTester implements TSESLint.RuleTester {
  constructor(options: TSESLint.RuleTesterConfig) {
    super(options);
  }

  public run<TMessageIds extends string, TOptions extends readonly unknown[]>(
    ruleName: string,
    rule: TSESLint.RuleModule<TMessageIds, TOptions, TSESLint.RuleListener>,
    tests: TSESLint.RunTests<TMessageIds, TOptions>
  ): void {
    return super.run.call(this, ruleName, rule, tests);
  }
  public defineRule<
    TMessageIds extends string,
    TOptions extends readonly unknown[]
  >(
    name: string,
    rule:
      | TSESLint.RuleModule<TMessageIds, TOptions, TSESLint.RuleListener>
      | TSESLint.RuleCreateFunction<
          TMessageIds,
          TOptions,
          TSESLint.RuleListener
        >
  ): void {
    super.defineRule.call(this, name, rule);
  }
}```

Better Vue support

As noticed at wikimedia/eslint-plugin-mediawiki/pull/65 the documentation generator is fairly inflexible regarding per-rule configuration, such as not using the fixCodeExamples option (which assumes everything is javascript) or choosing a syntax highlighting other than javascript.

Proposal:

  • in RuleTester.run() the same tests object that includes the valid and invalid examples can also have a docgenConfig property that would override the global configuration. If not in documentation mode, the property would be deleted automatically, like the config to show or hide specific tests
  • Add an option to both the global config and new per-rule config to control the syntax highlighting language, defaulting to javascript like currently

Make exhaustive list of fixes optional

Most plugins don't list fix examples (possibly because they are hard to maintain). To support this style, the listing of fix examples could be made optional.

Invitation to collaborate

Apologies for the @-spam, I have tagged ESLint plugin authors here who I identified as potential users of this documentation builder (i.e. your plugin was part of our analysis in #7).

I have been working on this tool for building ESLint plugin documentation and we have been using it at Wikimedia for eslint-plugin-no-jquery and eslint-plugin-mediawiki.

In those cases it is running fully automated off a master template (index.ejs) but it can also be used with existing documentation, allowing you to drop in automatically generated blocks incrementally. For this I have written a short migration guide.

Feature highlights:

  • Generates "Examples of (in)correct code" blocks automatically from test cases.
    • Test cases can be selective excluded from these blocks if required
    • Also generates examples of how code is fixed for fixable rules, showing before and after.
  • Generates boilerplate messages such as "This rule is deprecated (and replaced by...)", "This rule is included in the recommended config" and "This rule is fixable"
  • Generates links to rule and test source.

Examples:

Thanks for taking a look, and apologies again if this is not of any interest to you,
Ed

Allow a long description to be stored somewhere

The rule schema says that meta.docs.description should be a short description.

Long descriptions can either go in:

  • the rule metadata (violating the schema)
  • passed to RuleTester
  • separate md files

Consider making example spacing configurable

Currently single line test have no spaces between the, but multi-line tests do (example).

eslint.org documentation spaces tests inconsistently, so it possible that a user may prefer to space their tests, especially if there are only a few single line cases.

Possible config option:

  • exampleSpacing: "always", "never", "multiline" (default)

Support output to a single README.md

As suggested in #89. Perhaps a README template is provided, then the concatenation of all the rule doc output is placed in that file. This would be useful for plugins with only a few rules.

Link to test source

Maybe make this configurable in config (#2) along with the existing rule source link

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.