eslint-docgen 0.4.5 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,7 +63,7 @@ To migrate an existing plugin with manually built documentation you can use the
63
63
 
64
64
  ## ⚙️ Configuration
65
65
 
66
- Create a JSON/JavaScript file called `.eslintdocgenrc.json`/`.eslintdocgenrc.js` in your project root:
66
+ Configuration for all rules in a project is controlled by creating a JSON/JavaScript file called `.eslintdocgenrc.json`/`.eslintdocgenrc.js` in your project root:
67
67
 
68
68
  #### JSON
69
69
  ```jsonc
@@ -81,6 +81,12 @@ module.exports = {
81
81
  };
82
82
  ```
83
83
 
84
+ #### Overriding
85
+
86
+ 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.
87
+
88
+ ### Options
89
+
84
90
  The following config options are available:
85
91
 
86
92
  #### `docPath` (*required*)
@@ -123,6 +129,9 @@ Shows config comments at the top of code examples:
123
129
  #### `showFixExamples` (default `true`)
124
130
  Show examples of how code is fixed by the rule.
125
131
 
132
+ #### `showFilenames` (default: `false`)
133
+ Show the relevant file name for test cases.
134
+
126
135
  #### `excludeExamplesByDefault` (default `false`)
127
136
  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.
128
137
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-docgen",
3
- "version": "0.4.5",
3
+ "version": "0.6.2",
4
4
  "description": "Automatically generate ESLint plugin documentation from rule metadata and test cases.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,10 +20,10 @@
20
20
  "report": "nyc report --reporter=html"
21
21
  },
22
22
  "engines": {
23
- "node": ">=10.0.0"
23
+ "node": ">=12.0.0"
24
24
  },
25
25
  "dependencies": {
26
- "chalk": "^4.1.0",
26
+ "chalk": "^4.1.2",
27
27
  "ejs": "^3.1.6",
28
28
  "eslint": ">=5.0.0",
29
29
  "import-fresh": "^3.3.0",
@@ -36,9 +36,9 @@
36
36
  "upath": "^2.0.1"
37
37
  },
38
38
  "devDependencies": {
39
- "eslint-config-wikimedia": "^0.19.0",
39
+ "eslint-config-wikimedia": "^0.21.0",
40
40
  "eslint-plugin-node": "^11.1.0",
41
- "mocha": "^8.3.2",
41
+ "mocha": "^9.1.3",
42
42
  "nyc": "^15.1.0"
43
43
  }
44
44
  }
@@ -2,6 +2,13 @@
2
2
 
3
3
  const pluralize = require( 'pluralize' );
4
4
  const path = require( 'upath' );
5
+ // Mapping from extension to language name for markdown
6
+ // Default language is 'js'
7
+ const languageFromExtension = {
8
+ '.html': 'html',
9
+ '.ts': 'ts',
10
+ '.vue': 'vue'
11
+ };
5
12
 
6
13
  /**
7
14
  * Format a list of items with natural language
@@ -59,7 +66,25 @@ function buildDocsFromTests(
59
66
  return typeof test === 'string' ? test : test.code;
60
67
  }
61
68
 
62
- function buildRuleDetails( testList, valid, showFixes ) {
69
+ /**
70
+ * @param {string|Object} test Test case
71
+ * @return {string|null} The base file name, or null if not set
72
+ */
73
+ function getFilename( test ) {
74
+ if ( typeof test === 'string' || test.filename === undefined ) {
75
+ // Either the test is just a code snippet, or no file name is set
76
+ return null;
77
+ }
78
+ return path.basename( test.filename );
79
+ }
80
+
81
+ function buildRuleDetails( testList, isValid, showFixes ) {
82
+ testList = testList.filter( ( test ) =>
83
+ test.docgen === undefined ?
84
+ !config.excludeExamplesByDefault :
85
+ test.docgen
86
+ );
87
+
63
88
  let fixedCode, fixedOutput, maxCodeLength;
64
89
  const testsByOptions = {};
65
90
 
@@ -96,30 +121,37 @@ function buildDocsFromTests(
96
121
  const codeSet = {};
97
122
  let previousMultiLine = false;
98
123
  testList.forEach( function ( test, i ) {
99
- if ( !config.excludeExamplesByDefault && test.noDoc ) {
100
- messages.push( {
101
- type: 'warn',
102
- text: '`noDoc: true` is deprecated. Use `docgen: false` instead.'
103
- } );
104
- return;
124
+ const filename = getFilename( test );
125
+ let lang = 'js';
126
+ // Switch to Vue if we are showing file names and its a Vue file.
127
+ // optionsAndSettings.filename is only set if it should be shown
128
+ // TODO should we add other languages too?
129
+ if ( filename ) {
130
+ const ext = path.extname( filename );
131
+ if ( ext in languageFromExtension ) {
132
+ lang = languageFromExtension[ ext ];
133
+ }
105
134
  }
106
135
 
107
- const docgen = test.docgen === undefined ?
108
- !config.excludeExamplesByDefault :
109
- test.docgen;
110
-
111
- if ( !docgen ) {
112
- return;
113
- }
136
+ // Keys are in reverse sort order, e.g. examples with options
137
+ // are shown before examples with settings
138
+ const optionsAndSettings = {
139
+ // Always separate tests by language for syntax highlighting
140
+ lang: lang,
141
+ settings: test.settings,
142
+ options: test.options,
143
+ // Only group by filename if filenames are shown
144
+ filename: config.showFilenames && filename
145
+ };
146
+ const hashObject = {};
147
+ // Ensure examples without a specific key are shown first
148
+ Object.keys( optionsAndSettings ).forEach( ( key ) => {
149
+ hashObject[ key ] = optionsAndSettings[ key ] ?
150
+ [ '1', optionsAndSettings[ key ] ] :
151
+ [ '0', optionsAndSettings[ key ] ];
152
+ } );
114
153
 
115
- let optionsAndSettings;
116
- if ( test.options || test.settings ) {
117
- optionsAndSettings = {
118
- options: test.options,
119
- settings: test.settings
120
- };
121
- }
122
- const hash = optionsAndSettings ? JSON.stringify( optionsAndSettings ) : '';
154
+ const hash = JSON.stringify( hashObject );
123
155
 
124
156
  codeSet[ hash ] = codeSet[ hash ] || {};
125
157
 
@@ -144,9 +176,8 @@ function buildDocsFromTests(
144
176
  };
145
177
 
146
178
  if ( showFixes && test.output ) {
147
- const output = fixedOutput[ i ];
148
179
  const codeLines = code.split( '\n' );
149
- const outputLines = output.split( '\n' );
180
+ const outputLines = fixedOutput[ i ].split( '\n' );
150
181
  const maxLines = Math.max( codeLines.length, outputLines.length );
151
182
  const exampleLines = [];
152
183
  for ( let l = 0; l < maxLines; l++ ) {
@@ -164,7 +195,8 @@ function buildDocsFromTests(
164
195
  example = code;
165
196
  multiLine = code.split( '\n' ).length > 1;
166
197
  }
167
- if ( multiLine || previousMultiLine ) {
198
+ // Put linebreaks around multi-line examples
199
+ if ( testsByOptions[ hash ].tests.length && ( multiLine || previousMultiLine ) ) {
168
200
  example = '\n' + example;
169
201
  }
170
202
  testsByOptions[ hash ].tests.push( example );
@@ -190,10 +222,12 @@ function buildDocsFromTests(
190
222
  return Object.keys( testsByOptions ).map( ( key, i ) => {
191
223
  const section = testsByOptions[ key ];
192
224
  const optionsAndSettings = section.optionsAndSettings;
225
+ const lang = optionsAndSettings && optionsAndSettings.lang;
193
226
  const options = optionsAndSettings && optionsAndSettings.options;
194
227
  const settings = optionsAndSettings && optionsAndSettings.settings;
228
+ const filename = optionsAndSettings && optionsAndSettings.filename;
195
229
 
196
- let examples = '```js\n';
230
+ let examples = '```' + lang + '\n';
197
231
  if ( config.showConfigComments ) {
198
232
  examples += comments[ i ] + '\n';
199
233
  }
@@ -202,9 +236,10 @@ function buildDocsFromTests(
202
236
 
203
237
  return {
204
238
  key: key,
205
- valid: valid,
239
+ valid: isValid,
206
240
  options: options ? JSON.stringify( options ) : '',
207
241
  settings: settings ? JSON.stringify( settings ) : '',
242
+ filename: filename || '',
208
243
  examples: examples,
209
244
  testCount: section.tests.length
210
245
  };
@@ -5,6 +5,7 @@
5
5
  "fixCodeExamples": { "type": "boolean" },
6
6
  "showConfigComments": { "type": "boolean" },
7
7
  "showFixExamples": { "type": "boolean" },
8
+ "showFilenames": { "type": "boolean" },
8
9
  "tabWidth": {
9
10
  "type": "integer",
10
11
  "minimum": 0
@@ -9,6 +9,7 @@ module.exports = {
9
9
  pluginName: pluginName,
10
10
  fixCodeExamples: true,
11
11
  showConfigComments: false,
12
+ showFilenames: false,
12
13
  showFixExamples: true,
13
14
  tabWidth: 4,
14
15
  docPath: null,
@@ -3,37 +3,32 @@
3
3
  const packagePath = require( './package-path' );
4
4
  // Use plugin's version of ESLint
5
5
  const ESLintRuleTester = require( packagePath( 'node_modules/eslint' ) ).RuleTester;
6
- const docMode = !!process.env.DOCGEN || process.argv.includes( '--doc' );
7
-
8
- if ( process.argv.includes( '--doc' ) ) {
9
- const formatter = require( './formatter' );
10
- console.log(
11
- formatter.warn( '--doc is deprecated, use the DOCGEN=1 environment variable instead.' )
12
- );
13
- }
6
+ const inDocMode = !!process.env.DOCGEN;
14
7
 
15
8
  /**
16
9
  * Extends ESLint's RuleTester to also build documentation
17
10
  */
18
11
  class RuleTester extends ESLintRuleTester {
19
12
  run( name, rule, tests ) {
20
- if ( docMode ) {
13
+ if ( inDocMode ) {
21
14
  RuleTester.it( name, ( done ) => {
22
15
  const writeDocsFromTests = require( './write-docs-from-tests' );
23
16
  writeDocsFromTests( name, rule, tests, this.testerConfig, done );
24
17
  } );
25
18
  } else {
26
- // Filter out invalid top level property "noDoc", used in documentation building mode
19
+ // Filter out invalid property "docgen"
20
+ // (used in documentation building mode).
27
21
  tests.valid.forEach( ( test ) => {
28
22
  delete test.docgen;
29
- // Deprecated
30
- delete test.noDoc;
31
23
  } );
32
24
  tests.invalid.forEach( ( test ) => {
33
25
  delete test.docgen;
34
- // Deprecated
35
- delete test.noDoc;
36
26
  } );
27
+
28
+ // Filter out invalid top level property "docgenConfig"
29
+ // (used in documentation building mode).
30
+ delete tests.docgenConfig;
31
+
37
32
  return super.run.call( this, name, rule, tests );
38
33
  }
39
34
  }
@@ -1 +1 @@
1
- ⚙️ This rule is enabled in `plugin:<%- pluginName %>/<%- inConfig.config %>`<%- include( 'optionsAndSettings', { data: inConfig } ) %>.
1
+ 📋 This rule is enabled in `plugin:<%- pluginName %>/<%- inConfig.config %>`<%- include( 'optionsAndSettings', { data: inConfig } ) %>.
@@ -1,3 +1,5 @@
1
+ [//]: # (This file is generated by eslint-docgen. Do not edit it directly.)
2
+
1
3
  # <%- title %>
2
4
 
3
5
  <%- description %>
@@ -1,4 +1,6 @@
1
1
  <% if ( data.options || data.settings ) { _%>
2
2
  with<% if ( data.options ) { %> `<%- data.options %>` options<% } _%>
3
3
  <% if ( data.settings ) { %><% if ( data.options ) { %> and<% } %> `<%- data.settings %>` settings<% } _%>
4
- <% } %>
4
+ <% } _%>
5
+ <% if ( data.filename ) { _%>
6
+ for a file named `<%- data.filename %>`<% } %>
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- /* eslint-disable no-process-exit */
4
3
  const fs = require( 'fs' );
5
4
  const mkdirp = require( 'mkdirp' );
6
5
  const path = require( 'upath' );
@@ -14,21 +13,19 @@ let config, configPath;
14
13
  try {
15
14
  ( { config, configPath } = getConfig() );
16
15
  } catch ( e ) {
17
- console.log( formatter.heading( 'eslint-docgen' ) );
18
- console.log( formatter.error( e.message ) );
19
- console.log();
20
- process.exit( 1 );
16
+ throw new Error( [ formatter.heading( 'eslint-docgen' ), formatter.error( e.message ) ].join( '\n' ) );
21
17
  }
22
18
 
23
- const configErrors = require( './validate-config' )( config );
24
-
25
- if ( configErrors.length ) {
26
- console.log( formatter.heading( configPath ) );
27
- configErrors.forEach( ( error ) => console.log( formatter.error( error ) ) );
28
- console.log();
29
- process.exit( 1 );
19
+ const configValidator = require( './validate-config' );
20
+ function assertValidConfig( maybeValidConfig, configSource ) {
21
+ const configErrors = configValidator( maybeValidConfig );
22
+ if ( configErrors.length ) {
23
+ throw new Error( [ formatter.heading( configSource ), ...configErrors.map( formatter.error ) ].join( '\n' ) );
24
+ }
30
25
  }
31
26
 
27
+ assertValidConfig( config, configPath );
28
+
32
29
  const packagePath = require( './package-path' );
33
30
 
34
31
  const loadTemplates = require( './load-templates' );
@@ -39,28 +36,27 @@ if ( config.globalTemplatePath ) {
39
36
  const { globalTemplates, loadRuleTemplate } = loadTemplates( templatePaths );
40
37
 
41
38
  function writeDocsFromTests( name, rule, tests, testerConfig, done ) {
42
- const outputPath = packagePath( config.docPath.replace( '{name}', name ) );
39
+ // If the tests have a `docgenConfig` property, this overrides the global configuration
40
+ let configForRule = config;
41
+ if ( tests.docgenConfig !== undefined ) {
42
+ configForRule = Object.assign( {}, config, tests.docgenConfig );
43
+ assertValidConfig( configForRule, 'Rule specific config for ' + name );
44
+ delete tests.docgenConfig;
45
+ }
46
+ const outputPath = packagePath( configForRule.docPath.replace( '{name}', name ) );
43
47
  const ruleWithConfig = rulesWithConfig.get( name );
44
48
  if ( !ruleWithConfig ) {
45
- console.log();
46
- console.log( formatter.heading( outputPath ) );
47
- console.log( formatter.error( 'Rule not found.' ) );
48
- console.log();
49
- process.exit( 1 );
49
+ throw new Error( [ formatter.heading( outputPath ), formatter.error( 'Rule not found.' ) ].join( '\n' ) );
50
50
  }
51
51
  const configMap = rulesWithConfig.get( name ).configMap;
52
52
  let output, messages;
53
53
  try {
54
54
  ( { output, messages } = buildDocsFromTests(
55
- name, rule.meta, tests, configMap, config,
55
+ name, rule.meta, tests, configMap, configForRule,
56
56
  globalTemplates, loadRuleTemplate, testerConfig
57
57
  ) );
58
58
  } catch ( e ) {
59
- console.log();
60
- console.log( formatter.heading( outputPath ) );
61
- console.log( formatter.error( e.message ) );
62
- console.log();
63
- process.exit( 1 );
59
+ throw new Error( [ formatter.heading( outputPath ), formatter.error( e.message ) ].join( '\n' ) );
64
60
  }
65
61
 
66
62
  const outputDir = path.dirname( outputPath );
@@ -81,8 +77,10 @@ function writeDocsFromTests( name, rule, tests, testerConfig, done ) {
81
77
  console.log();
82
78
  }
83
79
 
84
- if ( messages.some( ( message ) => message.type === 'error' ) ) {
85
- process.exit( 1 );
80
+ const errors = messages.filter( ( message ) => message.type === 'error' );
81
+
82
+ if ( errors.length ) {
83
+ throw new Error( errors.map( formatter.error ).join( '\n' ) );
86
84
  }
87
85
 
88
86
  done();
package/Changelog.md DELETED
@@ -1,78 +0,0 @@
1
- # eslint-docgen release history
2
-
3
- ## v0.4.5
4
- * Code: Update dependencies
5
-
6
- ## v0.4.4
7
-
8
- * Code: Ensure config.docPath exists before writing output path. (Raine Revere)
9
- * Code: Calculate outputDir from outputPath
10
- * Code: Fix reporting of writeFile error messages
11
- * Code: Output formatted error when rule not found (#92)
12
- * Docs: Fix config param heading level in README (#91)
13
-
14
- ## v0.4.3
15
-
16
- * Code: Run one test 'it' per rule
17
-
18
- ## v0.4.2
19
-
20
- * New: Introduce excludeExamplesByDefault config
21
- * Code: Remove some whitespace in config-schema
22
- * Code: Add a 'report' script
23
-
24
- ## v0.4.1
25
-
26
- * Code: Update mocha to 8.0.1
27
- * Code: Replace path with upath for windows compatibility (#83)
28
- * Code: Use DOCGEN environment variable instead of --doc flag
29
- * Code: Add an editorconfig to make the tabs a reasonable width on GitHub (Jed Fox)
30
- * Code: Allow package.main to be empty
31
- * Code: Support scoped plugin names
32
- * Docs: Add more method documentation
33
- * Docs: Fix external links to examples
34
- * Docs: Add Migration section to README and move some pieces around
35
-
36
- ## v0.4.0
37
-
38
- * Breaking change: New: Add fix message to introduction (#49)
39
- * Breaking change: New: Format long lists correctly ("a, b, and c")
40
- * Breaking change: New: Sort examples by options *then* valid/invalid (#48)
41
- * New: Refactor rulesData to rulesWithConfig and expose (#16)
42
- * New: Add ruleTemplatePath config (#14)
43
- * New: Add showFixExamples config (#49)
44
- * Code: Update eslint-config-wikimedia to 0.16.1 and use /mocha
45
- * Code: Use pkg-dir
46
- * Code: Create a JSON-schema to validate configs (#63)
47
- * Code: Update various dependencies
48
- * Tests: Separate config tests into get/validate
49
- * Tests: Increase timeout
50
- * Tests: Separate buildDocsFromTests cases
51
- * Docs: Fix example docpath
52
-
53
- ## v0.3.1
54
-
55
- * Fix: Use plugin's version of ESLint
56
-
57
- ## v0.3.0
58
-
59
- * Breaking change: New: Support multi-line test cases (#34)
60
- * New: Support multiple lines in formatter
61
- * New: Add min/maxExamples to config (#29)
62
- * New: Use chalk to do pretty output of warnings/errors (#38)
63
- * Code: Bump node dependency to 10 (#44)
64
- * Code: Use strict mode
65
- * Code: Use eslint-plugin-node/recommended
66
- * Code: Removed fixed length truncation from rules-data.js
67
- * Tests: Add tests for most utils (#39)
68
- * Tests: Increase code coverage to 100%
69
- * Docs: Add "plugin" to description
70
-
71
- ## v0.2.0
72
-
73
- * Breaking change: Refactor templates. Users with existing template overrides will need to rewrite them.
74
- * Release: Update ejs to 3.1.3
75
-
76
- ## v0.1.0
77
-
78
- * Initial release.