eslint-docgen 0.8.0 → 0.8.1
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/package.json +9 -6
- package/src/eslint.js +20 -0
- package/src/fix.js +124 -21
- package/src/rule-tester.js +1 -3
- package/src/rules-with-config.js +35 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-docgen",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Automatically generate ESLint plugin documentation from rule metadata and test cases.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"src"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"test": "eslint . --cache &&
|
|
23
|
-
"report": "
|
|
22
|
+
"test": "eslint . --cache && c8 mocha tests/",
|
|
23
|
+
"report": "c8 report --reporter=html"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=20.0.0"
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"chalk": "^4.1.2",
|
|
30
30
|
"ejs": "^4.0.1",
|
|
31
|
-
"eslint": ">=8.0.0",
|
|
32
31
|
"import-fresh": "^3.3.1",
|
|
33
32
|
"jsonschema": "^1.5.0",
|
|
34
33
|
"merge-options": "^3.0.4",
|
|
@@ -38,9 +37,13 @@
|
|
|
38
37
|
"simple-mock": "^0.8.0",
|
|
39
38
|
"upath": "^2.0.1"
|
|
40
39
|
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"eslint": ">=8.0.0"
|
|
42
|
+
},
|
|
41
43
|
"devDependencies": {
|
|
44
|
+
"c8": "^12.0.0",
|
|
45
|
+
"eslint": "^8.57.1",
|
|
42
46
|
"eslint-config-wikimedia": "^0.32.3",
|
|
43
|
-
"mocha": "^
|
|
44
|
-
"nyc": "^17.1.0"
|
|
47
|
+
"mocha": "^12.0.3"
|
|
45
48
|
}
|
|
46
49
|
}
|
package/src/eslint.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const packagePath = require( './package-path' );
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Load a module from the plugin's copy of ESLint
|
|
7
|
+
*
|
|
8
|
+
* Docs should be built with the same version of ESLint, and the same config
|
|
9
|
+
* system, as the plugin's tests are run with.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} [subpath] Module path within the ESLint package, e.g. 'use-at-your-own-risk'
|
|
12
|
+
* @return {Object} Module exports
|
|
13
|
+
*/
|
|
14
|
+
function requireESLint( subpath ) {
|
|
15
|
+
const name = subpath ? 'eslint/' + subpath : 'eslint';
|
|
16
|
+
// eslint-disable-next-line security/detect-non-literal-require
|
|
17
|
+
return require( require.resolve( name, { paths: [ packagePath( '.' ) ] } ) );
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = requireESLint;
|
package/src/fix.js
CHANGED
|
@@ -1,47 +1,150 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
3
|
+
const requireESLint = require( './eslint' );
|
|
4
|
+
const { Linter, ESLint, loadESLint } = requireESLint();
|
|
5
|
+
const { builtinRules } = requireESLint( 'use-at-your-own-risk' );
|
|
6
6
|
const mergeOptions = require( 'merge-options' );
|
|
7
|
-
const { builtinRules } = require( 'eslint/use-at-your-own-risk' );
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* ESLint 9+'s RuleTester only supports flat config, so test cases and the
|
|
10
|
+
* tester config are in flat config format.
|
|
11
|
+
*/
|
|
12
|
+
const isFlatRuleTester = parseInt( Linter.version, 10 ) >= 9;
|
|
13
|
+
|
|
14
|
+
const linter = new Linter( { configType: isFlatRuleTester ? 'flat' : 'eslintrc' } );
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the plugin's ESLint config for its main file, normalized to a format
|
|
18
|
+
* which doesn't depend on the config system used
|
|
19
|
+
*
|
|
20
|
+
* Only fixable core rules are kept, as plugin rules would need the plugin to
|
|
21
|
+
* be loaded, and only fixes are needed.
|
|
22
|
+
*
|
|
23
|
+
* @return {Promise<Object>} Normalized config
|
|
24
|
+
*/
|
|
9
25
|
async function getConfig() {
|
|
10
|
-
|
|
26
|
+
// loadESLint (ESLint 8.57+) returns the class for the config system used
|
|
27
|
+
// by the plugin, i.e. depending on whether it has an eslint.config.js file
|
|
28
|
+
const ESLintClass = loadESLint ? await loadESLint() : ESLint;
|
|
29
|
+
const eslint = new ESLintClass();
|
|
30
|
+
const config = await eslint.calculateConfigForFile( require( './main' ) ) || {};
|
|
31
|
+
const isFlatConfig = ESLintClass.configType === 'flat';
|
|
32
|
+
const languageOptions = ( isFlatConfig ? config.languageOptions : config.parserOptions ) || {};
|
|
11
33
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Object.keys( config.rules ).forEach( ( ruleName ) => {
|
|
34
|
+
const rules = {};
|
|
35
|
+
Object.keys( config.rules || {} ).forEach( ( ruleName ) => {
|
|
15
36
|
if ( builtinRules.has( ruleName ) ) {
|
|
16
37
|
const rule = builtinRules.get( ruleName );
|
|
17
38
|
if ( rule.meta.fixable ) {
|
|
18
|
-
|
|
39
|
+
rules[ ruleName ] = config.rules[ ruleName ];
|
|
19
40
|
}
|
|
20
41
|
}
|
|
21
42
|
} );
|
|
22
|
-
config.rules = fixableRules;
|
|
23
43
|
|
|
24
|
-
return
|
|
44
|
+
return {
|
|
45
|
+
rules,
|
|
46
|
+
settings: config.settings,
|
|
47
|
+
// eslintrc only, can set the ecmaVersion, e.g. "es2017"
|
|
48
|
+
env: config.env,
|
|
49
|
+
// Parser name/path (eslintrc) or object (flat config)
|
|
50
|
+
parser: isFlatConfig ? languageOptions.parser : config.parser,
|
|
51
|
+
parserOptions: isFlatConfig ? languageOptions.parserOptions : config.parserOptions,
|
|
52
|
+
ecmaVersion: languageOptions.ecmaVersion,
|
|
53
|
+
sourceType: languageOptions.sourceType
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get a flat config array, for linting with ESLint 9+
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} config Normalized config, from #getConfig
|
|
61
|
+
* @param {Object|Object[]} testerConfig RuleTester config
|
|
62
|
+
* @return {Object[]} Flat config array
|
|
63
|
+
*/
|
|
64
|
+
function getFlatConfig( config, testerConfig ) {
|
|
65
|
+
const baseConfig = {
|
|
66
|
+
rules: config.rules,
|
|
67
|
+
languageOptions: {}
|
|
68
|
+
};
|
|
69
|
+
if ( config.settings ) {
|
|
70
|
+
baseConfig.settings = config.settings;
|
|
71
|
+
}
|
|
72
|
+
[ 'ecmaVersion', 'sourceType', 'parserOptions' ].forEach( ( key ) => {
|
|
73
|
+
if ( config[ key ] !== undefined ) {
|
|
74
|
+
baseConfig.languageOptions[ key ] = config[ key ];
|
|
75
|
+
}
|
|
76
|
+
} );
|
|
77
|
+
if ( config.parser ) {
|
|
78
|
+
baseConfig.languageOptions.parser = typeof config.parser === 'string' ?
|
|
79
|
+
// eslint-disable-next-line security/detect-non-literal-require
|
|
80
|
+
require( config.parser ) :
|
|
81
|
+
config.parser;
|
|
82
|
+
}
|
|
83
|
+
// The RuleTester config may include rules which aren't loaded here,
|
|
84
|
+
// e.g. "rule-tester/validate-ast", and rules aren't needed for fixing.
|
|
85
|
+
const testerConfigs = [].concat( testerConfig || [] ).map( ( testerConfigItem ) => {
|
|
86
|
+
const copy = Object.assign( {}, testerConfigItem );
|
|
87
|
+
delete copy.rules;
|
|
88
|
+
return copy;
|
|
89
|
+
} );
|
|
90
|
+
return [ baseConfig, ...testerConfigs ];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get an eslintrc config, for linting with ESLint 8
|
|
95
|
+
*
|
|
96
|
+
* @param {Object} config Normalized config, from #getConfig
|
|
97
|
+
* @param {Object} testerConfig RuleTester config
|
|
98
|
+
* @return {Object} eslintrc config
|
|
99
|
+
*/
|
|
100
|
+
function getEslintrcConfig( config, testerConfig ) {
|
|
101
|
+
const baseConfig = {
|
|
102
|
+
rules: config.rules,
|
|
103
|
+
parserOptions: Object.assign( {}, config.parserOptions )
|
|
104
|
+
};
|
|
105
|
+
if ( config.settings ) {
|
|
106
|
+
baseConfig.settings = config.settings;
|
|
107
|
+
}
|
|
108
|
+
if ( config.env ) {
|
|
109
|
+
baseConfig.env = config.env;
|
|
110
|
+
}
|
|
111
|
+
[ 'ecmaVersion', 'sourceType' ].forEach( ( key ) => {
|
|
112
|
+
if ( config[ key ] !== undefined ) {
|
|
113
|
+
baseConfig.parserOptions[ key ] = config[ key ];
|
|
114
|
+
}
|
|
115
|
+
} );
|
|
116
|
+
if ( config.parser ) {
|
|
117
|
+
baseConfig.parser = config.parser;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const mergedConfig = mergeOptions( baseConfig, testerConfig );
|
|
121
|
+
|
|
122
|
+
if ( mergedConfig.parser && typeof mergedConfig.parser !== 'string' ) {
|
|
123
|
+
// A parser object, from an ESLint 8.57 flat config
|
|
124
|
+
linter.defineParser( 'eslint-docgen-parser', mergedConfig.parser );
|
|
125
|
+
mergedConfig.parser = 'eslint-docgen-parser';
|
|
126
|
+
} else if ( typeof mergedConfig.parser === 'string' ) {
|
|
127
|
+
// eslint-disable-next-line security/detect-non-literal-require
|
|
128
|
+
linter.defineParser( mergedConfig.parser, require( mergedConfig.parser ) );
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return mergedConfig;
|
|
25
132
|
}
|
|
26
133
|
|
|
27
134
|
/**
|
|
28
135
|
* Lint and fix some code
|
|
29
136
|
*
|
|
30
137
|
* @param {string} code Code
|
|
31
|
-
* @param {Object} testerConfig
|
|
138
|
+
* @param {Object|Object[]} testerConfig RuleTester config
|
|
32
139
|
* @return {string} Fixed code
|
|
33
140
|
*/
|
|
34
141
|
async function lintFix( code, testerConfig ) {
|
|
35
|
-
const
|
|
142
|
+
const config = await getConfig();
|
|
36
143
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// eslint-disable-next-line security/detect-non-literal-require
|
|
41
|
-
linter.defineParser( mergedConfig.parser, require( mergedConfig.parser ) );
|
|
42
|
-
}
|
|
144
|
+
const result = isFlatRuleTester ?
|
|
145
|
+
linter.verifyAndFix( code, getFlatConfig( config, testerConfig ), { filename: 'example.js' } ) :
|
|
146
|
+
linter.verifyAndFix( code, getEslintrcConfig( config, testerConfig ) );
|
|
43
147
|
|
|
44
|
-
const result = linter.verifyAndFix( code, mergedConfig );
|
|
45
148
|
const err = result.messages.find( ( message ) => message.fatal );
|
|
46
149
|
// TODO
|
|
47
150
|
// istanbul ignore next
|
|
@@ -58,7 +161,7 @@ async function lintFix( code, testerConfig ) {
|
|
|
58
161
|
* Concatenates the code snippets into one code block to improve performance
|
|
59
162
|
*
|
|
60
163
|
* @param {string[]} codeList Code list
|
|
61
|
-
* @param {Object} testerConfig
|
|
164
|
+
* @param {Object|Object[]} testerConfig RuleTester config
|
|
62
165
|
* @return {string[]} Fixed code list
|
|
63
166
|
*/
|
|
64
167
|
async function batchLintFix( codeList, testerConfig ) {
|
package/src/rule-tester.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const packagePath = require( './package-path' );
|
|
4
3
|
// Use plugin's version of ESLint
|
|
5
|
-
|
|
6
|
-
const ESLintRuleTester = require( packagePath( 'node_modules/eslint' ) ).RuleTester;
|
|
4
|
+
const ESLintRuleTester = require( './eslint' )().RuleTester;
|
|
7
5
|
const inDocMode = !!process.env.DOCGEN;
|
|
8
6
|
|
|
9
7
|
/**
|
package/src/rules-with-config.js
CHANGED
|
@@ -11,10 +11,44 @@ rulesWithConfig.forEach( ( rule, name ) => {
|
|
|
11
11
|
rulesWithConfig.get( name ).configMap = new Map();
|
|
12
12
|
} );
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Check if a config is a duplicate of another config in a different format
|
|
16
|
+
*
|
|
17
|
+
* Plugins which support both eslintrc and flat config often export each config
|
|
18
|
+
* twice, e.g. `recommended` and `flat/recommended`, or `recommended` and
|
|
19
|
+
* `legacy-recommended`. Only list these once.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} name Config name
|
|
22
|
+
* @return {boolean}
|
|
23
|
+
*/
|
|
24
|
+
function isDuplicateConfig( name ) {
|
|
25
|
+
const match = name.match( /^(?:flat\/|legacy-)(.*)$/ );
|
|
26
|
+
return !!match && Object.prototype.hasOwnProperty.call( configs, match[ 1 ] );
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the rules set by a config
|
|
31
|
+
*
|
|
32
|
+
* @param {Object|Object[]} config eslintrc config, flat config, or flat config array
|
|
33
|
+
* @return {Object} Rules
|
|
34
|
+
*/
|
|
35
|
+
function getRules( config ) {
|
|
36
|
+
return Array.isArray( config ) ?
|
|
37
|
+
Object.assign( {}, ...config.map( getRules ) ) :
|
|
38
|
+
config.rules || {};
|
|
39
|
+
}
|
|
40
|
+
|
|
14
41
|
// Iterate over configs to add config data to map
|
|
15
42
|
for ( const name in configs ) {
|
|
16
|
-
|
|
43
|
+
if ( isDuplicateConfig( name ) ) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const rules = getRules( configs[ name ] );
|
|
17
47
|
for ( const fullName in rules ) {
|
|
48
|
+
// Flat config arrays can include rules from other plugins
|
|
49
|
+
if ( !fullName.startsWith( pluginName + '/' ) ) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
18
52
|
// Configs use the full rule name with the plugin prefix, so remove this
|
|
19
53
|
const shortName = fullName.slice( pluginName.length + 1 );
|
|
20
54
|
let options, rawSeverity;
|