@@ -7,10 +7,11 @@ const eslintScope = require('eslint-scope');
77const estraverse = require ( 'estraverse' ) ;
88const assert = require ( 'chai' ) . assert ;
99const utils = require ( '../../lib/utils' ) ;
10+ const typescriptEslintParser = require ( '@typescript-eslint/parser' ) ;
1011
1112describe ( 'utils' , ( ) => {
1213 describe ( 'getRuleInfo' , ( ) => {
13- describe ( 'the file does not have a valid rule' , ( ) => {
14+ describe ( 'the file does not have a valid rule (CJS) ' , ( ) => {
1415 [
1516 '' ,
1617 'module.exports;' ,
@@ -25,6 +26,11 @@ describe('utils', () => {
2526 'module.exports = { create: foo }' ,
2627 'module.exports = { create: function* foo() {} }' ,
2728 'module.exports = { create: async function foo() {} }' ,
29+
30+ // Correct TypeScript helper structure but missing parameterized types (note: we don't support CJS for TypeScript rules):
31+ 'module.exports = createESLintRule({ create() {}, meta: {} });' ,
32+ 'module.exports = util.createRule({ create() {}, meta: {} });' ,
33+ 'module.exports = ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });' ,
2834 ] . forEach ( noRuleCase => {
2935 it ( `returns null for ${ noRuleCase } ` , ( ) => {
3036 const ast = espree . parse ( noRuleCase , { ecmaVersion : 8 , range : true } ) ;
@@ -39,6 +45,19 @@ describe('utils', () => {
3945 'export const foo = { create() {} }' ,
4046 'export default { foo: {} }' ,
4147 'const foo = {}; export default foo' ,
48+
49+ // Incorrect TypeScript helper structure:
50+ 'export default foo()({ create() {}, meta: {} });' ,
51+ 'export default foo().bar({ create() {}, meta: {} });' ,
52+ 'export default foo.bar.baz({ create() {}, meta: {} });' ,
53+ 'export default foo(123);' ,
54+ 'export default foo.bar(123);' ,
55+ 'export default foo.bar()(123);' ,
56+
57+ // Correct TypeScript helper structure but missing parameterized types:
58+ 'export default createESLintRule({ create() {}, meta: {} });' ,
59+ 'export default util.createRule({ create() {}, meta: {} });' ,
60+ 'export default ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });' ,
4261 ] . forEach ( noRuleCase => {
4362 it ( `returns null for ${ noRuleCase } ` , ( ) => {
4463 const ast = espree . parse ( noRuleCase , { ecmaVersion : 8 , range : true , sourceType : 'module' } ) ;
@@ -47,9 +66,80 @@ describe('utils', () => {
4766 } ) ;
4867 } ) ;
4968
50- describe ( 'the file has a valid rule' , ( ) => {
69+ describe ( 'the file does not have a valid rule (TypeScript + TypeScript parser + ESM)' , ( ) => {
70+ [
71+ // Incorrect TypeScript helper structure:
72+ 'export default foo()<Options, MessageIds>({ create() {}, meta: {} });' ,
73+ 'export default foo().bar<Options, MessageIds>({ create() {}, meta: {} });' ,
74+ 'export default foo.bar.baz<Options, MessageIds>({ create() {}, meta: {} });' ,
75+ 'export default foo<Options, MessageIds>(123);' ,
76+ 'export default foo.bar<Options, MessageIds>(123);' ,
77+ 'export default foo.bar()<Options, MessageIds>(123);' ,
78+
79+ // Correct TypeScript helper structure but missing parameterized types:
80+ 'export default createESLintRule({ create() {}, meta: {} });' ,
81+ 'export default createESLintRule<>({ create() {}, meta: {} });' ,
82+ 'export default createESLintRule<OnlyOneType>({ create() {}, meta: {} });' ,
83+ 'export default util.createRule({ create() {}, meta: {} });' ,
84+ 'export default ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });' ,
85+ ] . forEach ( noRuleCase => {
86+ it ( `returns null for ${ noRuleCase } ` , ( ) => {
87+ const ast = typescriptEslintParser . parse ( noRuleCase , { ecmaVersion : 8 , range : true , sourceType : 'module' } ) ;
88+ assert . isNull ( utils . getRuleInfo ( { ast } ) , 'Expected no rule to be found' ) ;
89+ } ) ;
90+ } ) ;
91+ } ) ;
92+
93+ describe ( 'the file does not have a valid rule (TypeScript + TypeScript parser + CJS)' , ( ) => {
94+ [
95+ // Correct TypeScript helper structure but missing parameterized types (note: we don't support CJS for TypeScript rules):
96+ 'module.exports = createESLintRule<Options, MessageIds>({ create() {}, meta: {} });' ,
97+ 'module.exports = util.createRule<Options, MessageIds>({ create() {}, meta: {} });' ,
98+ 'module.exports = ESLintUtils.RuleCreator(docsUrl)<Options, MessageIds>({ create() {}, meta: {} });' ,
99+ ] . forEach ( noRuleCase => {
100+ it ( `returns null for ${ noRuleCase } ` , ( ) => {
101+ const ast = typescriptEslintParser . parse ( noRuleCase , { range : true , sourceType : 'script' } ) ;
102+ assert . isNull ( utils . getRuleInfo ( { ast } ) , 'Expected no rule to be found' ) ;
103+ } ) ;
104+ } ) ;
105+ } ) ;
106+
107+ describe ( 'the file has a valid rule (TypeScript + TypeScript parser + ESM)' , ( ) => {
108+ const CASES = {
109+ // Util function only
110+ 'export default createESLintRule<Options, MessageIds>({ create() {}, meta: {} });' : {
111+ create : { type : 'FunctionExpression' } ,
112+ meta : { type : 'ObjectExpression' } ,
113+ isNewStyle : true ,
114+ } ,
115+ // Util function from util object
116+ 'export default util.createRule<Options, MessageIds>({ create() {}, meta: {} });' : {
117+ create : { type : 'FunctionExpression' } ,
118+ meta : { type : 'ObjectExpression' } ,
119+ isNewStyle : true ,
120+ } ,
121+ // Util function from util object with additional doc URL argument
122+ 'export default ESLintUtils.RuleCreator(docsUrl)<Options, MessageIds>({ create() {}, meta: {} });' : {
123+ create : { type : 'FunctionExpression' } ,
124+ meta : { type : 'ObjectExpression' } ,
125+ isNewStyle : true ,
126+ } ,
127+ } ;
128+
129+ Object . keys ( CASES ) . forEach ( ruleSource => {
130+ it ( ruleSource , ( ) => {
131+ const ast = typescriptEslintParser . parse ( ruleSource , { ecmaVersion : 6 , range : true , sourceType : 'module' } ) ;
132+ const ruleInfo = utils . getRuleInfo ( { ast } ) ;
133+ assert (
134+ lodash . isMatch ( ruleInfo , CASES [ ruleSource ] ) ,
135+ `Expected \n${ inspect ( ruleInfo ) } \nto match\n${ inspect ( CASES [ ruleSource ] ) } `
136+ ) ;
137+ } ) ;
138+ } ) ;
139+ } ) ;
140+
141+ describe ( 'the file has a valid rule (CJS)' , ( ) => {
51142 const CASES = {
52- // CJS
53143 'module.exports = { create: function foo() {} };' : {
54144 create : { type : 'FunctionExpression' , id : { name : 'foo' } } , // (This property will actually contain the AST node.)
55145 meta : null ,
@@ -125,7 +215,22 @@ describe('utils', () => {
125215 meta : null ,
126216 isNewStyle : false ,
127217 } ,
218+ } ;
219+
220+ Object . keys ( CASES ) . forEach ( ruleSource => {
221+ it ( ruleSource , ( ) => {
222+ const ast = espree . parse ( ruleSource , { ecmaVersion : 6 , range : true , sourceType : 'script' } ) ;
223+ const ruleInfo = utils . getRuleInfo ( { ast } ) ;
224+ assert (
225+ lodash . isMatch ( ruleInfo , CASES [ ruleSource ] ) ,
226+ `Expected \n${ inspect ( ruleInfo ) } \nto match\n${ inspect ( CASES [ ruleSource ] ) } `
227+ ) ;
228+ } ) ;
229+ } ) ;
230+ } ) ;
128231
232+ describe ( 'the file has a valid rule (ESM)' , ( ) => {
233+ const CASES = {
129234 // ESM (object style)
130235 'export default { create() {} }' : {
131236 create : { type : 'FunctionExpression' } ,
@@ -153,15 +258,17 @@ describe('utils', () => {
153258
154259 Object . keys ( CASES ) . forEach ( ruleSource => {
155260 it ( ruleSource , ( ) => {
156- const ast = espree . parse ( ruleSource , { ecmaVersion : 6 , range : true , sourceType : ruleSource . startsWith ( 'export default' ) ? ' module' : 'script ' } ) ;
261+ const ast = espree . parse ( ruleSource , { ecmaVersion : 6 , range : true , sourceType : ' module' } ) ;
157262 const ruleInfo = utils . getRuleInfo ( { ast } ) ;
158263 assert (
159264 lodash . isMatch ( ruleInfo , CASES [ ruleSource ] ) ,
160265 `Expected \n${ inspect ( ruleInfo ) } \nto match\n${ inspect ( CASES [ ruleSource ] ) } `
161266 ) ;
162267 } ) ;
163268 } ) ;
269+ } ) ;
164270
271+ describe ( 'the file has a valid rule (different scope options)' , ( ) => {
165272 for ( const scopeOptions of [
166273 { ignoreEval : true , ecmaVersion : 6 , sourceType : 'script' , nodejsScope : true } ,
167274 { ignoreEval : true , ecmaVersion : 6 , sourceType : 'script' } ,
0 commit comments